diff --git a/osu.Android.props b/osu.Android.props index eab2be1b72..24a0d20874 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index efd5868504..5b7a460079 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -10,10 +10,10 @@ namespace osu.Game.Rulesets.Mania.Difficulty public class ManiaDifficultyAttributes : DifficultyAttributes { /// - /// The perceived hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc). + /// The hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc). /// /// - /// Rate-adjusting mods don't directly affect the hit window, but have a perceived effect as a result of adjusting audio timing. + /// Rate-adjusting mods do not affect the hit window at all in osu-stable. /// [JsonProperty("great_hit_window")] public double GreatHitWindow { get; set; } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 1f82eb7ccd..b17aa7fc4d 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -48,7 +48,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty { StarRating = skills[0].DifficultyValue() * star_scaling_factor, Mods = mods, - GreatHitWindow = Math.Ceiling(getHitWindow300(mods) / clockRate), + // In osu-stable mania, rate-adjustment mods don't affect the hit window. + // This is done the way it is to introduce fractional differences in order to match osu-stable for the time being. + GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate), ScoreMultiplier = getScoreMultiplier(mods), MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1), }; @@ -108,7 +110,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty } } - private int getHitWindow300(Mod[] mods) + private double getHitWindow300(Mod[] mods) { if (isForCurrentRuleset) { @@ -121,19 +123,14 @@ namespace osu.Game.Rulesets.Mania.Difficulty return applyModAdjustments(47, mods); - static int applyModAdjustments(double value, Mod[] mods) + static double applyModAdjustments(double value, Mod[] mods) { if (mods.Any(m => m is ManiaModHardRock)) value /= 1.4; else if (mods.Any(m => m is ManiaModEasy)) value *= 1.4; - if (mods.Any(m => m is ManiaModDoubleTime)) - value *= 1.5; - else if (mods.Any(m => m is ManiaModHalfTime)) - value *= 0.75; - - return (int)value; + return value; } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 8a8c41bb8a..722cb55036 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -43,14 +43,11 @@ namespace osu.Game.Rulesets.Mania.Difficulty countMeh = Score.Statistics.GetValueOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetValueOrDefault(HitResult.Miss); - IEnumerable scoreIncreaseMods = Ruleset.GetModsFor(ModType.DifficultyIncrease); - - double scoreMultiplier = 1.0; - foreach (var m in mods.Where(m => !scoreIncreaseMods.Contains(m))) - scoreMultiplier *= m.ScoreMultiplier; - - // Scale score up, so it's comparable to other keymods - scaledScore *= 1.0 / scoreMultiplier; + if (Attributes.ScoreMultiplier > 0) + { + // Scale score up, so it's comparable to other keymods + scaledScore *= 1.0 / Attributes.ScoreMultiplier; + } // Arbitrary initial value for scaling pp in order to standardize distributions across game modes. // The specific number has no intrinsic meaning and can be adjusted as needed. diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs index 952fc7ddd6..fdacc75c92 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs @@ -98,8 +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(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m - || isLastColumn; + bool hasRightLine = (rightLineWidth > 0 && skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m) || isLastColumn; Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White; Color4 backgroundColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, columnIndex)?.Value ?? Color4.Black; diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index c4bffbc0ab..2521d570f2 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; @@ -12,6 +13,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Extensions; +using osu.Framework.Graphics; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; @@ -21,6 +23,8 @@ using osu.Game.Database; using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Tests.Resources; @@ -53,6 +57,25 @@ namespace osu.Game.Tests.Online [SetUp] public void SetUp() => Schedule(() => { + ((DummyAPIAccess)API).HandleRequest = req => + { + switch (req) + { + case GetBeatmapsRequest beatmapsReq: + var beatmap = CreateAPIBeatmap(); + beatmap.OnlineID = testBeatmapInfo.OnlineID; + beatmap.OnlineBeatmapSetID = testBeatmapSet.OnlineID; + beatmap.Checksum = testBeatmapInfo.MD5Hash; + beatmap.BeatmapSet!.OnlineID = testBeatmapSet.OnlineID; + + beatmapsReq.TriggerSuccess(new GetBeatmapsResponse { Beatmaps = new List { beatmap } }); + return true; + + default: + return false; + } + }; + beatmaps.AllowImport = new TaskCompletionSource(); testBeatmapFile = TestResources.GetQuickTestBeatmapForImport(); @@ -63,18 +86,35 @@ namespace osu.Game.Tests.Online Realm.Write(r => r.RemoveAll()); Realm.Write(r => r.RemoveAll()); - selectedItem.Value = new PlaylistItem + selectedItem.Value = new PlaylistItem(testBeatmapInfo) { - Beatmap = { Value = testBeatmapInfo }, RulesetID = testBeatmapInfo.Ruleset.OnlineID, }; - Child = availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker - { - SelectedItem = { BindTarget = selectedItem, } - }; + recreateChildren(); }); + private void recreateChildren() + { + var beatmapLookupCache = new BeatmapLookupCache(); + + Child = new DependencyProvidingContainer + { + CachedDependencies = new[] + { + (typeof(BeatmapLookupCache), (object)beatmapLookupCache) + }, + Children = new Drawable[] + { + beatmapLookupCache, + availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker + { + SelectedItem = { BindTarget = selectedItem, } + } + } + }; + } + [Test] public void TestBeatmapDownloadingFlow() { @@ -123,10 +163,7 @@ namespace osu.Game.Tests.Online }); addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded); - AddStep("recreate tracker", () => Child = availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker - { - SelectedItem = { BindTarget = selectedItem } - }); + AddStep("recreate tracker", recreateChildren); addAvailabilityCheckStep("state not downloaded as well", BeatmapAvailability.NotDownloaded); AddStep("reimport original beatmap", () => beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).WaitSafely()); @@ -167,7 +204,8 @@ namespace osu.Game.Tests.Online public Live CurrentImport { get; private set; } - public TestBeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, GameHost host = null, WorkingBeatmap defaultBeatmap = null) + public TestBeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore resources, + GameHost host = null, WorkingBeatmap defaultBeatmap = null) : base(storage, realm, rulesets, api, audioManager, resources, host, defaultBeatmap) { } diff --git a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs index d33081662d..9e7ea02101 100644 --- a/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs +++ b/osu.Game.Tests/OnlinePlay/PlaylistExtensionsTest.cs @@ -3,6 +3,7 @@ using System; using NUnit.Framework; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; namespace osu.Game.Tests.OnlinePlay @@ -29,9 +30,9 @@ namespace osu.Game.Tests.OnlinePlay { var items = new[] { - new PlaylistItem { ID = 1, BeatmapID = 1001, PlaylistOrder = 1 }, - new PlaylistItem { ID = 2, BeatmapID = 1002, PlaylistOrder = 2 }, - new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, + new PlaylistItem(new APIBeatmap { OnlineID = 1001 }) { ID = 1, PlaylistOrder = 1 }, + new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 2, PlaylistOrder = 2 }, + new PlaylistItem(new APIBeatmap { OnlineID = 1003 }) { ID = 3, PlaylistOrder = 3 }, }; Assert.Multiple(() => @@ -47,9 +48,9 @@ namespace osu.Game.Tests.OnlinePlay { var items = new[] { - new PlaylistItem { ID = 2, BeatmapID = 1002, PlaylistOrder = 2 }, - new PlaylistItem { ID = 1, BeatmapID = 1001, PlaylistOrder = 1 }, - new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, + new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 2, PlaylistOrder = 2 }, + new PlaylistItem(new APIBeatmap { OnlineID = 1001 }) { ID = 1, PlaylistOrder = 1 }, + new PlaylistItem(new APIBeatmap { OnlineID = 1003 }) { ID = 3, PlaylistOrder = 3 }, }; Assert.Multiple(() => @@ -65,9 +66,9 @@ namespace osu.Game.Tests.OnlinePlay { var items = new[] { - new PlaylistItem { ID = 1, BeatmapID = 1001, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 55, 0, TimeSpan.Zero) }, - new PlaylistItem { ID = 2, BeatmapID = 1002, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 53, 0, TimeSpan.Zero) }, - new PlaylistItem { ID = 3, BeatmapID = 1003, PlaylistOrder = 3 }, + new PlaylistItem(new APIBeatmap { OnlineID = 1001 }) { ID = 1, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 55, 0, TimeSpan.Zero) }, + new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 2, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 53, 0, TimeSpan.Zero) }, + new PlaylistItem(new APIBeatmap { OnlineID = 1003 }) { ID = 3, PlaylistOrder = 3 }, }; Assert.Multiple(() => @@ -83,9 +84,9 @@ namespace osu.Game.Tests.OnlinePlay { var items = new[] { - new PlaylistItem { ID = 1, BeatmapID = 1001, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 55, 0, TimeSpan.Zero) }, - new PlaylistItem { ID = 2, BeatmapID = 1002, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 53, 0, TimeSpan.Zero) }, - new PlaylistItem { ID = 3, BeatmapID = 1002, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 57, 0, TimeSpan.Zero) }, + new PlaylistItem(new APIBeatmap { OnlineID = 1001 }) { ID = 1, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 55, 0, TimeSpan.Zero) }, + new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 2, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 53, 0, TimeSpan.Zero) }, + new PlaylistItem(new APIBeatmap { OnlineID = 1002 }) { ID = 3, Expired = true, PlayedAt = new DateTimeOffset(2021, 12, 21, 7, 57, 0, TimeSpan.Zero) }, }; Assert.Multiple(() => diff --git a/osu.Game.Tests/Utils/NamingUtilsTest.cs b/osu.Game.Tests/Utils/NamingUtilsTest.cs new file mode 100644 index 0000000000..62e688db90 --- /dev/null +++ b/osu.Game.Tests/Utils/NamingUtilsTest.cs @@ -0,0 +1,132 @@ +// 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.Game.Utils; + +namespace osu.Game.Tests.Utils +{ + [TestFixture] + public class NamingUtilsTest + { + [Test] + public void TestEmptySet() + { + string nextBestName = NamingUtils.GetNextBestName(Enumerable.Empty(), "New Difficulty"); + + Assert.AreEqual("New Difficulty", nextBestName); + } + + [Test] + public void TestNotTaken() + { + string[] existingNames = + { + "Something", + "Entirely", + "Different" + }; + + string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty"); + + Assert.AreEqual("New Difficulty", nextBestName); + } + + [Test] + public void TestNotTakenButClose() + { + string[] existingNames = + { + "New Difficulty(1)", + "New Difficulty (abcd)", + "New Difficulty but not really" + }; + + string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty"); + + Assert.AreEqual("New Difficulty", nextBestName); + } + + [Test] + public void TestAlreadyTaken() + { + string[] existingNames = + { + "New Difficulty" + }; + + string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty"); + + Assert.AreEqual("New Difficulty (1)", nextBestName); + } + + [Test] + public void TestAlreadyTakenWithDifferentCase() + { + string[] existingNames = + { + "new difficulty" + }; + + string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty"); + + Assert.AreEqual("New Difficulty (1)", nextBestName); + } + + [Test] + public void TestAlreadyTakenWithBrackets() + { + string[] existingNames = + { + "new difficulty (copy)" + }; + + string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty (copy)"); + + Assert.AreEqual("New Difficulty (copy) (1)", nextBestName); + } + + [Test] + public void TestMultipleAlreadyTaken() + { + string[] existingNames = + { + "New Difficulty", + "New difficulty (1)", + "new Difficulty (2)", + "New DIFFICULTY (3)" + }; + + string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty"); + + Assert.AreEqual("New Difficulty (4)", nextBestName); + } + + [Test] + public void TestEvenMoreAlreadyTaken() + { + string[] existingNames = Enumerable.Range(1, 30).Select(i => $"New Difficulty ({i})").Append("New Difficulty").ToArray(); + + string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty"); + + Assert.AreEqual("New Difficulty (31)", nextBestName); + } + + [Test] + public void TestMultipleAlreadyTakenWithGaps() + { + string[] existingNames = + { + "New Difficulty", + "New Difficulty (1)", + "New Difficulty (4)", + "New Difficulty (9)" + }; + + string nextBestName = NamingUtils.GetNextBestName(existingNames, "New Difficulty"); + + Assert.AreEqual("New Difficulty (2)", nextBestName); + } + } +} diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index 0a2f622da1..ecd4035edd 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -269,11 +269,12 @@ namespace osu.Game.Tests.Visual.Editing } [Test] - public void TestCreateNewBeatmapFailsWithBlankNamedDifficulties() + public void TestCreateMultipleNewDifficultiesSucceeds() { Guid setId = Guid.Empty; AddStep("retrieve set ID", () => setId = EditorBeatmap.BeatmapInfo.BeatmapSet!.ID); + AddStep("set difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = "New Difficulty"); AddStep("save beatmap", () => Editor.Save()); AddAssert("new beatmap persisted", () => { @@ -282,15 +283,24 @@ namespace osu.Game.Tests.Visual.Editing }); AddStep("try to create new difficulty", () => Editor.CreateNewDifficulty(new OsuRuleset().RulesetInfo)); - AddAssert("beatmap set unchanged", () => + AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is CreateNewDifficultyDialog); + AddStep("confirm creation with no objects", () => DialogOverlay.CurrentDialog.PerformOkAction()); + + AddUntilStep("wait for created", () => + { + string difficultyName = Editor.ChildrenOfType().SingleOrDefault()?.BeatmapInfo.DifficultyName; + return difficultyName != null && difficultyName != "New Difficulty"; + }); + AddAssert("new difficulty has correct name", () => EditorBeatmap.BeatmapInfo.DifficultyName == "New Difficulty (1)"); + AddAssert("new difficulty persisted", () => { var set = beatmapManager.QueryBeatmapSet(s => s.ID == setId); - return set != null && set.PerformRead(s => s.Beatmaps.Count == 1 && s.Files.Count == 1); + return set != null && set.PerformRead(s => s.Beatmaps.Count == 2 && s.Files.Count == 2); }); } [Test] - public void TestCreateNewBeatmapFailsWithSameNamedDifficulties([Values] bool sameRuleset) + public void TestSavingBeatmapFailsWithSameNamedDifficulties([Values] bool sameRuleset) { Guid setId = Guid.Empty; const string duplicate_difficulty_name = "duplicate"; diff --git a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs index 2c994576f3..bafc579134 100644 --- a/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs +++ b/osu.Game.Tests/Visual/Multiplayer/QueueModeTestScene.cs @@ -71,9 +71,8 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = { Value = Mode }, Playlist = { - new PlaylistItem + new PlaylistItem(InitialBeatmap) { - Beatmap = { Value = InitialBeatmap }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs index 423822cbe4..d8ec0ad1f0 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoom.cs @@ -53,19 +53,13 @@ namespace osu.Game.Tests.Visual.Multiplayer Type = { Value = MatchType.HeadToHead }, Playlist = { - new PlaylistItem + new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) { - Beatmap = + BeatmapInfo = { - Value = new TestBeatmap(new OsuRuleset().RulesetInfo) - { - BeatmapInfo = - { - StarRating = 2.5 - } - }.BeatmapInfo, + StarRating = 2.5 } - } + }.BeatmapInfo) } }), createLoungeRoom(new Room @@ -76,26 +70,20 @@ namespace osu.Game.Tests.Visual.Multiplayer Type = { Value = MatchType.HeadToHead }, Playlist = { - new PlaylistItem + new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) { - Beatmap = + BeatmapInfo = { - Value = new TestBeatmap(new OsuRuleset().RulesetInfo) + StarRating = 2.5, + Metadata = { - BeatmapInfo = - { - StarRating = 2.5, - Metadata = - { - Artist = "very very very very very very very very very long artist", - ArtistUnicode = "very very very very very very very very very long artist", - Title = "very very very very very very very very very very very long title", - TitleUnicode = "very very very very very very very very very very very long title", - } - } - }.BeatmapInfo, + Artist = "very very very very very very very very very long artist", + ArtistUnicode = "very very very very very very very very very long artist", + Title = "very very very very very very very very very very very long title", + TitleUnicode = "very very very very very very very very very very very long title", + } } - } + }.BeatmapInfo) } }), createLoungeRoom(new Room @@ -105,32 +93,20 @@ namespace osu.Game.Tests.Visual.Multiplayer EndDate = { Value = DateTimeOffset.Now.AddDays(1) }, Playlist = { - new PlaylistItem + new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) { - Beatmap = + BeatmapInfo = { - Value = new TestBeatmap(new OsuRuleset().RulesetInfo) - { - BeatmapInfo = - { - StarRating = 2.5 - } - }.BeatmapInfo, + StarRating = 2.5 } - }, - new PlaylistItem + }.BeatmapInfo), + new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo) { - Beatmap = + BeatmapInfo = { - Value = new TestBeatmap(new OsuRuleset().RulesetInfo) - { - BeatmapInfo = - { - StarRating = 4.5 - } - }.BeatmapInfo, + StarRating = 4.5 } - } + }.BeatmapInfo) } }), createLoungeRoom(new Room diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 4dc7dc1c42..76353323d6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -168,7 +168,7 @@ namespace osu.Game.Tests.Visual.Multiplayer assertDownloadButtonVisible(false); void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}", - () => playlist.ChildrenOfType().Single().Alpha == (visible ? 1 : 0)); + () => playlist.ChildrenOfType().SingleOrDefault()?.Alpha == (visible ? 1 : 0)); } [Test] @@ -209,10 +209,9 @@ namespace osu.Game.Tests.Visual.Multiplayer Size = new Vector2(500, 300), Items = { - new PlaylistItem + new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { ID = 0, - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, Expired = true, RequiredMods = new[] @@ -222,10 +221,9 @@ namespace osu.Game.Tests.Visual.Multiplayer new APIMod(new OsuModAutoplay()) } }, - new PlaylistItem + new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { ID = 1, - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, RequiredMods = new[] { @@ -262,7 +260,7 @@ namespace osu.Game.Tests.Visual.Multiplayer } private void moveToItem(int index, Vector2? offset = null) - => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset)); + => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset)); private void moveToDragger(int index, Vector2? offset = null) => AddStep($"move mouse to dragger {index}", () => { @@ -293,25 +291,21 @@ namespace osu.Game.Tests.Visual.Multiplayer for (int i = 0; i < 20; i++) { - playlist.Items.Add(new PlaylistItem + playlist.Items.Add(new PlaylistItem(i % 2 == 1 + ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo + : new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "Artist", + Author = new RealmUser { Username = "Creator name here" }, + Title = "Long title used to check background colour", + }, + BeatmapSet = new BeatmapSetInfo() + }) { ID = i, OwnerID = 2, - Beatmap = - { - Value = i % 2 == 1 - ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo - : new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Artist = "Artist", - Author = new RealmUser { Username = "Creator name here" }, - Title = "Long title used to check background colour", - }, - BeatmapSet = new BeatmapSetInfo() - } - }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, RequiredMods = new[] { @@ -341,11 +335,10 @@ namespace osu.Game.Tests.Visual.Multiplayer foreach (var b in beatmaps()) { - playlist.Items.Add(new PlaylistItem + playlist.Items.Add(new PlaylistItem(b) { ID = index++, OwnerID = 2, - Beatmap = { Value = b }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, RequiredMods = new[] { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs index a308d60cf1..c3ec7a5369 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneHostOnlyQueueMode.cs @@ -58,12 +58,12 @@ namespace osu.Game.Tests.Visual.Multiplayer RunGameplay(); IBeatmapInfo firstBeatmap = null; - AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.APIRoom?.Playlist[0].Beatmap.Value); + AddStep("get first playlist item beatmap", () => firstBeatmap = MultiplayerClient.APIRoom?.Playlist[0].Beatmap); selectNewItem(() => OtherBeatmap); - AddAssert("first playlist item hasn't changed", () => MultiplayerClient.APIRoom?.Playlist[0].Beatmap.Value == firstBeatmap); - AddAssert("second playlist item changed", () => MultiplayerClient.APIRoom?.Playlist[1].Beatmap.Value != firstBeatmap); + AddAssert("first playlist item hasn't changed", () => MultiplayerClient.APIRoom?.Playlist[0].Beatmap == firstBeatmap); + AddAssert("second playlist item changed", () => MultiplayerClient.APIRoom?.Playlist[1].Beatmap != firstBeatmap); } [Test] @@ -105,7 +105,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(otherBeatmap = beatmap())); AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen); - AddUntilStep("selected item is new beatmap", () => (CurrentSubScreen as MultiplayerMatchSubScreen)?.SelectedItem.Value?.BeatmapID == otherBeatmap.OnlineID); + AddUntilStep("selected item is new beatmap", () => (CurrentSubScreen as MultiplayerMatchSubScreen)?.SelectedItem.Value?.Beatmap.OnlineID == otherBeatmap.OnlineID); } private void addItem(Func beatmap) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs index 6144824ba0..6f43511e8a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchBeatmapDetailArea.cs @@ -32,10 +32,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private void createNewItem() { - SelectedRoom.Value.Playlist.Add(new PlaylistItem + SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { ID = SelectedRoom.Value.Playlist.Count, - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, RequiredMods = new[] { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index fc8c3f7f58..e38da96bd5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -93,9 +93,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -229,9 +228,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -251,9 +249,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -281,9 +278,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -312,9 +308,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Password = { Value = "password" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -334,9 +329,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Password = { Value = "password" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -367,9 +361,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Password = { Value = "password" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -387,9 +380,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -409,9 +401,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -448,9 +439,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -487,9 +477,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -526,9 +515,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -560,9 +548,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -600,9 +587,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -620,9 +606,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, AllowedMods = new[] { new APIMod(new OsuModHidden()) } } @@ -660,10 +645,9 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } } }); @@ -691,10 +675,9 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } } }, API.LocalUser.Value); @@ -708,11 +691,10 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("change server-side settings", () => { roomManager.ServerSideRooms[0].Name.Value = "New name"; - roomManager.ServerSideRooms[0].Playlist.Add(new PlaylistItem + roomManager.ServerSideRooms[0].Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { ID = 2, - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, }); }); @@ -737,10 +719,9 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } } }); @@ -773,10 +754,9 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } } }); @@ -812,21 +792,20 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } } }); enterGameplay(); - AddStep("join other user", () => multiplayerClient.AddUser(new APIUser { Id = 1234 })); - AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem(new PlaylistItem - { - BeatmapID = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo.OnlineID - })).WaitSafely()); + AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem( + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + })).WaitSafely()); AddUntilStep("item arrived in playlist", () => multiplayerClient.Room?.Playlist.Count == 2); @@ -843,10 +822,9 @@ namespace osu.Game.Tests.Visual.Multiplayer QueueMode = { Value = QueueMode.AllPlayers }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } } }); @@ -854,10 +832,11 @@ namespace osu.Game.Tests.Visual.Multiplayer enterGameplay(); AddStep("join other user", () => multiplayerClient.AddUser(new APIUser { Id = 1234 })); - AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem(new PlaylistItem - { - BeatmapID = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo.OnlineID - })).WaitSafely()); + AddStep("add item as other user", () => multiplayerClient.AddUserPlaylistItem(1234, new MultiplayerPlaylistItem( + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + })).WaitSafely()); AddUntilStep("item arrived in playlist", () => multiplayerClient.Room?.Playlist.Count == 2); @@ -876,9 +855,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 1eebe781f1..057032c413 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -73,9 +73,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item", () => { - SelectedRoom.Value.Playlist.Add(new PlaylistItem + SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID }); }); @@ -90,9 +89,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item", () => { - SelectedRoom.Value.Playlist.Add(new PlaylistItem + SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo) { - Beatmap = { Value = new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo }, RulesetID = new TaikoRuleset().RulesetInfo.OnlineID, AllowedMods = new[] { new APIMod(new TaikoModSwap()) } }); @@ -113,9 +111,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("set playlist", () => { - SelectedRoom.Value.Playlist.Add(new PlaylistItem + SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID }); }); @@ -128,9 +125,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("set playlist", () => { - SelectedRoom.Value.Playlist.Add(new PlaylistItem + SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID }); }); @@ -159,9 +155,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("add playlist item with allowed mod", () => { - SelectedRoom.Value.Playlist.Add(new PlaylistItem + SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) } }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs index d391fbcf8f..312281ac18 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlayer.cs @@ -28,9 +28,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("initialise gameplay", () => { - Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.APIRoom, new PlaylistItem + Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.APIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo) { - Beatmap = { Value = Beatmap.Value.BeatmapInfo }, RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID, }, MultiplayerClient.Room?.Users.ToArray())); }); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs index af12046d76..6b5b45b73e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerPlaylist.cs @@ -143,14 +143,12 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "test name" }, Playlist = { - new PlaylistItem + new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo) { - Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, - RulesetID = Ruleset.Value.OnlineID, + RulesetID = Ruleset.Value.OnlineID }, - new PlaylistItem + new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo) { - Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, RulesetID = Ruleset.Value.OnlineID, Expired = true } @@ -167,10 +165,8 @@ namespace osu.Game.Tests.Visual.Multiplayer /// /// Adds a step to create a new playlist item. /// - private void addItemStep(bool expired = false) => AddStep("add item", () => MultiplayerClient.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem + private void addItemStep(bool expired = false) => AddStep("add item", () => MultiplayerClient.AddPlaylistItem(new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap) { - Beatmap = { Value = importedBeatmap }, - BeatmapID = importedBeatmap.OnlineID, Expired = expired, PlayedAt = DateTimeOffset.Now }))); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs index 397c2d16d1..117aaeba83 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerQueueList.cs @@ -120,11 +120,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add playlist item", () => { - MultiplayerPlaylistItem item = new MultiplayerPlaylistItem(new PlaylistItem - { - Beatmap = { Value = importedBeatmap }, - BeatmapID = importedBeatmap.OnlineID, - }); + MultiplayerPlaylistItem item = new MultiplayerPlaylistItem(new PlaylistItem(importedBeatmap)); MultiplayerClient.AddUserPlaylistItem(userId(), item).WaitSafely(); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs index 6851dba721..35992a0b38 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerReadyButton.cs @@ -54,9 +54,8 @@ namespace osu.Game.Tests.Visual.Multiplayer importedSet = beatmaps.GetAllUsableBeatmapSets().First(); Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); - selectedItem.Value = new PlaylistItem + selectedItem.Value = new PlaylistItem(Beatmap.Value.BeatmapInfo) { - Beatmap = { Value = Beatmap.Value.BeatmapInfo }, RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID }; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs index 44a1745eee..cc08135939 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerResults.cs @@ -22,12 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo; var score = TestResources.CreateTestScoreInfo(beatmapInfo); - PlaylistItem playlistItem = new PlaylistItem - { - BeatmapID = beatmapInfo.OnlineID, - }; - - Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem)); + Stack.Push(screen = new MultiplayerResultsScreen(score, 1, new PlaylistItem(beatmapInfo))); }); AddUntilStep("wait for loaded", () => screen.IsLoaded); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs index 0033a4ca74..956d40a456 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectateButton.cs @@ -55,9 +55,8 @@ namespace osu.Game.Tests.Visual.Multiplayer importedSet = beatmaps.GetAllUsableBeatmapSets().First(); Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()); - selectedItem.Value = new PlaylistItem + selectedItem.Value = new PlaylistItem(Beatmap.Value.BeatmapInfo) { - Beatmap = { Value = Beatmap.Value.BeatmapInfo }, RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID, }; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs index dfc16c44f2..bdc348b043 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerTeamResults.cs @@ -26,18 +26,13 @@ namespace osu.Game.Tests.Visual.Multiplayer var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo; var score = TestResources.CreateTestScoreInfo(beatmapInfo); - PlaylistItem playlistItem = new PlaylistItem - { - BeatmapID = beatmapInfo.OnlineID, - }; - SortedDictionary teamScores = new SortedDictionary { { 0, new BindableInt(team1Score) }, { 1, new BindableInt(team2Score) } }; - Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, playlistItem, teamScores)); + Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, new PlaylistItem(beatmapInfo), teamScores)); }); AddUntilStep("wait for loaded", () => screen.IsLoaded); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs index 9adf2c0370..98dc243ab5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestScenePlaylistsRoomSettingsPlaylist.cs @@ -8,7 +8,6 @@ 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.Graphics.Containers; using osu.Game.Models; using osu.Game.Online.API; @@ -107,18 +106,8 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("item 0 is selected", () => playlist.SelectedItem.Value == playlist.Items[0]); } - [Test] - public void TestChangeBeatmapAndRemove() - { - createPlaylist(); - - AddStep("change beatmap of first item", () => playlist.Items[0].BeatmapID = 30); - moveToDeleteButton(0); - AddStep("click delete button", () => InputManager.Click(MouseButton.Left)); - } - private void moveToItem(int index, Vector2? offset = null) - => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset)); + => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset)); private void moveToDeleteButton(int index, Vector2? offset = null) => AddStep($"move mouse to delete button {index}", () => { @@ -139,25 +128,21 @@ namespace osu.Game.Tests.Visual.Multiplayer for (int i = 0; i < 20; i++) { - playlist.Items.Add(new PlaylistItem + playlist.Items.Add(new PlaylistItem(i % 2 == 1 + ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo + : new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Artist = "Artist", + Author = new RealmUser { Username = "Creator name here" }, + Title = "Long title used to check background colour", + }, + BeatmapSet = new BeatmapSetInfo() + }) { ID = i, OwnerID = 2, - Beatmap = - { - Value = i % 2 == 1 - ? new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo - : new BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Artist = "Artist", - Author = new RealmUser { Username = "Creator name here" }, - Title = "Long title used to check background colour", - }, - BeatmapSet = new BeatmapSetInfo() - } - }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID, RequiredMods = new[] { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs index 20db922122..5e4013b0f1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneStarRatingRangeDisplay.cs @@ -31,8 +31,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { SelectedRoom.Value.Playlist.AddRange(new[] { - new PlaylistItem { Beatmap = { Value = new BeatmapInfo { StarRating = min } } }, - new PlaylistItem { Beatmap = { Value = new BeatmapInfo { StarRating = max } } }, + new PlaylistItem(new BeatmapInfo { StarRating = min }), + new PlaylistItem(new BeatmapInfo { StarRating = max }), }); }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 05b97e333f..19e1f62fd5 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -67,9 +67,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Type = { Value = MatchType.TeamVersus }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -88,9 +87,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Type = { Value = MatchType.TeamVersus }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID } } @@ -126,10 +124,9 @@ namespace osu.Game.Tests.Visual.Multiplayer Type = { Value = MatchType.HeadToHead }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } } }); @@ -152,10 +149,9 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "Test Room" }, Playlist = { - new PlaylistItem + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) { - Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo }, - RulesetID = new OsuRuleset().RulesetInfo.OnlineID + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, } } }); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs index ca3387392a..666e32d1d0 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsMatchSettingsOverlay.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("set name", () => SelectedRoom.Value.Name.Value = "Room name"); AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value); - AddStep("set beatmap", () => SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } })); + AddStep("set beatmap", () => SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo))); AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value); AddStep("clear name", () => SelectedRoom.Value.Name.Value = ""); @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.Playlists { settings.NameField.Current.Value = expected_name; settings.DurationField.Current.Value = expectedDuration; - SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }); + SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)); RoomManager.CreateRequested = r => { @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Playlists var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo; SelectedRoom.Value.Name.Value = "Test Room"; - SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = beatmap } }); + SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmap)); errorMessage = $"{not_found_prefix} {beatmap.OnlineID}"; @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("setup", () => { SelectedRoom.Value.Name.Value = "Test Room"; - SelectedRoom.Value.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(Ruleset.Value).BeatmapInfo } }); + SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)); RoomManager.CreateRequested = _ => failText; }); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index a05d01613c..161624413d 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -165,9 +165,8 @@ namespace osu.Game.Tests.Visual.Playlists { AddStep("load results", () => { - LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem + LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo) { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID })); }); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index a6f1762cb3..14f8c1d6d4 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -64,9 +64,8 @@ namespace osu.Game.Tests.Visual.Playlists room.Host.Value = API.LocalUser.Value; room.RecentParticipants.Add(room.Host.Value); room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); - room.Playlist.Add(new PlaylistItem + room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { - Beatmap = { Value = importedBeatmap.Beatmaps.First() }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID }); }); @@ -89,9 +88,8 @@ namespace osu.Game.Tests.Visual.Playlists room.Host.Value = API.LocalUser.Value; room.RecentParticipants.Add(room.Host.Value); room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); - room.Playlist.Add(new PlaylistItem + room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { - Beatmap = { Value = importedBeatmap.Beatmaps.First() }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID }); }); @@ -106,9 +104,8 @@ namespace osu.Game.Tests.Visual.Playlists { room.Name.Value = "my awesome room"; room.Host.Value = API.LocalUser.Value; - room.Playlist.Add(new PlaylistItem + room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First()) { - Beatmap = { Value = importedBeatmap.Beatmaps.First() }, RulesetID = new OsuRuleset().RulesetInfo.OnlineID }); }); @@ -158,21 +155,17 @@ namespace osu.Game.Tests.Visual.Playlists { room.Name.Value = "my awesome room"; room.Host.Value = API.LocalUser.Value; - room.Playlist.Add(new PlaylistItem + room.Playlist.Add(new PlaylistItem(new BeatmapInfo { - Beatmap = + MD5Hash = realHash, + OnlineID = realOnlineId, + Metadata = new BeatmapMetadata(), + BeatmapSet = new BeatmapSetInfo { - Value = new BeatmapInfo - { - MD5Hash = realHash, - OnlineID = realOnlineId, - Metadata = new BeatmapMetadata(), - BeatmapSet = new BeatmapSetInfo - { - OnlineID = realOnlineSetId, - } - } - }, + OnlineID = realOnlineSetId, + } + }) + { RulesetID = new OsuRuleset().RulesetInfo.OnlineID }); }); diff --git a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs index 062aa33b99..c43ed744bd 100644 --- a/osu.Game.Tests/Visual/TestMultiplayerComponents.cs +++ b/osu.Game.Tests/Visual/TestMultiplayerComponents.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Screens; @@ -46,6 +47,9 @@ namespace osu.Game.Tests.Visual [Cached] private readonly BeatmapLookupCache beatmapLookupCache = new BeatmapLookupCache(); + [Resolved] + private BeatmapManager beatmapManager { get; set; } + private readonly OsuScreenStack screenStack; private readonly TestMultiplayer multiplayerScreen; @@ -69,9 +73,9 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuGameBase game) + private void load(IAPIProvider api) { - ((DummyAPIAccess)api).HandleRequest = request => multiplayerScreen.RequestsHandler.HandleRequest(request, api.LocalUser.Value, game); + ((DummyAPIAccess)api).HandleRequest = request => multiplayerScreen.RequestsHandler.HandleRequest(request, api.LocalUser.Value, beatmapManager); } public override bool OnBackButton() => (screenStack.CurrentScreen as OsuScreen)?.OnBackButton() ?? base.OnBackButton(); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 777d5db2ad..5f7de0d762 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -22,6 +22,7 @@ using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Skinning; using osu.Game.Stores; +using osu.Game.Utils; #nullable enable @@ -123,7 +124,10 @@ namespace osu.Game.Beatmaps { var playableBeatmap = referenceWorkingBeatmap.GetPlayableBeatmap(rulesetInfo); - var newBeatmapInfo = new BeatmapInfo(rulesetInfo, new BeatmapDifficulty(), playableBeatmap.Metadata.DeepClone()); + var newBeatmapInfo = new BeatmapInfo(rulesetInfo, new BeatmapDifficulty(), playableBeatmap.Metadata.DeepClone()) + { + DifficultyName = NamingUtils.GetNextBestName(targetBeatmapSet.Beatmaps.Select(b => b.DifficultyName), "New Difficulty") + }; var newBeatmap = new Beatmap { BeatmapInfo = newBeatmapInfo }; foreach (var timingPoint in playableBeatmap.ControlPointInfo.TimingPoints) newBeatmap.ControlPointInfo.Add(timingPoint.Time, timingPoint.DeepClone()); @@ -150,8 +154,10 @@ namespace osu.Game.Beatmaps newBeatmap.BeatmapInfo = newBeatmapInfo = referenceWorkingBeatmap.BeatmapInfo.Clone(); // assign a new ID to the clone. newBeatmapInfo.ID = Guid.NewGuid(); - // add "(copy)" suffix to difficulty name to avoid clashes on save. - newBeatmapInfo.DifficultyName += " (copy)"; + // add "(copy)" suffix to difficulty name, and additionally ensure that it doesn't conflict with any other potentially pre-existing copies. + newBeatmapInfo.DifficultyName = NamingUtils.GetNextBestName( + targetBeatmapSet.Beatmaps.Select(b => b.DifficultyName), + $"{newBeatmapInfo.DifficultyName} (copy)"); // clear the hash, as that's what is used to match .osu files with their corresponding realm beatmaps. newBeatmapInfo.Hash = string.Empty; // clear online properties. diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index a39d7bfb47..b0f20de685 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -112,6 +112,8 @@ namespace osu.Game.Graphics if (Interlocked.Decrement(ref screenShotTasks) == 0 && cursorVisibility.Value == false) cursorVisibility.Value = true; + host.GetClipboard()?.SetImage(image); + string filename = getFilename(); if (filename == null) return; diff --git a/osu.Game/Online/API/Requests/GetBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapsRequest.cs index 1d71e22b77..22af022659 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapsRequest.cs @@ -2,12 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; namespace osu.Game.Online.API.Requests { public class GetBeatmapsRequest : APIRequest { - private readonly int[] beatmapIds; + public readonly IReadOnlyList BeatmapIds; private const int max_ids_per_request = 50; @@ -16,9 +17,9 @@ namespace osu.Game.Online.API.Requests if (beatmapIds.Length > max_ids_per_request) throw new ArgumentException($"{nameof(GetBeatmapsRequest)} calls only support up to {max_ids_per_request} IDs at once"); - this.beatmapIds = beatmapIds; + BeatmapIds = beatmapIds; } - protected override string Target => "beatmaps/?ids[]=" + string.Join("&ids[]=", beatmapIds); + protected override string Target => "beatmaps/?ids[]=" + string.Join("&ids[]=", BeatmapIds); } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index dca60e54cb..f5795141c5 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -112,7 +112,27 @@ namespace osu.Game.Online.API.Requests.Responses public int OnlineID { get; set; } = -1; public string Name => $@"{nameof(APIRuleset)} (ID: {OnlineID})"; - public string ShortName => nameof(APIRuleset); + + public string ShortName + { + get + { + // TODO: this should really not exist. + switch (OnlineID) + { + case 0: return "osu"; + + case 1: return "taiko"; + + case 2: return "fruits"; + + case 3: return "mania"; + + default: throw new ArgumentOutOfRangeException(); + } + } + } + public string InstantiationInfo => string.Empty; public Ruleset CreateInstance() => throw new NotImplementedException(); diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 9d45229961..a56cc7f8d6 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -727,10 +727,9 @@ namespace osu.Game.Online.Multiplayer RoomUpdated?.Invoke(); } - private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item) => new PlaylistItem + private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item) => new PlaylistItem(new APIBeatmap { OnlineID = item.BeatmapID }) { ID = item.ID, - BeatmapID = item.BeatmapID, OwnerID = item.OwnerID, RulesetID = item.RulesetID, Expired = item.Expired, @@ -740,14 +739,6 @@ namespace osu.Game.Online.Multiplayer AllowedMods = item.AllowedMods.ToArray() }; - /// - /// Retrieves a from an online source. - /// - /// The beatmap ID. - /// A token to cancel the request. - /// The retrieval task. - public abstract Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default); - /// /// For the provided user ID, update whether the user is included in . /// diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index ad898759ff..7e62908ecd 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -10,9 +10,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Game.Database; using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; namespace osu.Game.Online.Multiplayer @@ -30,9 +28,6 @@ namespace osu.Game.Online.Multiplayer private HubConnection? connection => connector?.CurrentConnection; - [Resolved] - private BeatmapLookupCache beatmapLookupCache { get; set; } = null!; - public OnlineMultiplayerClient(EndpointConfiguration endpoints) { endpoint = endpoints.MultiplayerEndpointUrl; @@ -215,11 +210,6 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.RemovePlaylistItem), playlistItemId); } - public override Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default) - { - return beatmapLookupCache.GetBeatmapAsync(beatmapId, cancellationToken); - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs index d74cdd8c34..388a02f798 100644 --- a/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs +++ b/osu.Game/Online/Rooms/MultiplayerPlaylistItem.cs @@ -63,8 +63,8 @@ namespace osu.Game.Online.Rooms { ID = item.ID; OwnerID = item.OwnerID; - BeatmapID = item.BeatmapID; - BeatmapChecksum = item.Beatmap.Value?.MD5Hash ?? string.Empty; + BeatmapID = item.Beatmap.OnlineID; + BeatmapChecksum = item.Beatmap.MD5Hash; RulesetID = item.RulesetID; RequiredMods = item.RequiredMods.ToArray(); AllowedMods = item.AllowedMods.ToArray(); diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index c67cbade6a..07506ba1f0 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -4,14 +4,17 @@ using System; using System.Diagnostics; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Online.API.Requests.Responses; using Realms; namespace osu.Game.Online.Rooms @@ -32,6 +35,9 @@ namespace osu.Game.Online.Rooms [Resolved] private RealmAccess realm { get; set; } = null!; + [Resolved] + private BeatmapLookupCache beatmapLookupCache { get; set; } = null!; + /// /// The availability state of the currently selected playlist item. /// @@ -40,10 +46,9 @@ namespace osu.Game.Online.Rooms private readonly Bindable availability = new Bindable(BeatmapAvailability.NotDownloaded()); private ScheduledDelegate progressUpdate; - private BeatmapDownloadTracker downloadTracker; - private IDisposable realmSubscription; + private APIBeatmap selectedBeatmap; protected override void LoadComplete() { @@ -57,40 +62,55 @@ namespace osu.Game.Online.Rooms return; downloadTracker?.RemoveAndDisposeImmediately(); + selectedBeatmap = null; - Debug.Assert(item.NewValue.Beatmap.Value.BeatmapSet != null); - - downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet); - - AddInternal(downloadTracker); - - downloadTracker.State.BindValueChanged(_ => Scheduler.AddOnce(updateAvailability), true); - downloadTracker.Progress.BindValueChanged(_ => + beatmapLookupCache.GetBeatmapAsync(item.NewValue.Beatmap.OnlineID).ContinueWith(task => Schedule(() => { - if (downloadTracker.State.Value != DownloadState.Downloading) - return; + var beatmap = task.GetResultSafely(); - // 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); - }, true); - - // handles changes to hash that didn't occur from the import process (ie. a user editing the beatmap in the editor, somehow). - realmSubscription?.Dispose(); - realmSubscription = realm.RegisterForNotifications(r => filteredBeatmaps(), (items, changes, ___) => - { - if (changes == null) - return; - - Scheduler.AddOnce(updateAvailability); - }); + if (SelectedItem.Value?.Beatmap.OnlineID == beatmap.OnlineID) + { + selectedBeatmap = beatmap; + beginTracking(); + } + }), TaskContinuationOptions.OnlyOnRanToCompletion); }, true); } + private void beginTracking() + { + Debug.Assert(selectedBeatmap.BeatmapSet != null); + + downloadTracker = new BeatmapDownloadTracker(selectedBeatmap.BeatmapSet); + + AddInternal(downloadTracker); + + downloadTracker.State.BindValueChanged(_ => Scheduler.AddOnce(updateAvailability), true); + 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); + }, true); + + // handles changes to hash that didn't occur from the import process (ie. a user editing the beatmap in the editor, somehow). + realmSubscription?.Dispose(); + realmSubscription = realm.RegisterForNotifications(r => filteredBeatmaps(), (items, changes, ___) => + { + if (changes == null) + return; + + Scheduler.AddOnce(updateAvailability); + }); + } + private void updateAvailability() { - if (downloadTracker == null || SelectedItem.Value == null) + if (downloadTracker == null || selectedBeatmap == null) return; switch (downloadTracker.State.Value) @@ -108,12 +128,12 @@ namespace osu.Game.Online.Rooms break; case DownloadState.LocallyAvailable: - bool hashMatches = filteredBeatmaps().Any(); + bool available = filteredBeatmaps().Any(); - availability.Value = hashMatches ? BeatmapAvailability.LocallyAvailable() : BeatmapAvailability.NotDownloaded(); + availability.Value = available ? 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) + if (!available && downloadTracker.Progress.Value == 1) Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important); break; @@ -125,8 +145,8 @@ namespace osu.Game.Online.Rooms private IQueryable filteredBeatmaps() { - int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID; - string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; + int onlineId = selectedBeatmap.OnlineID; + string checksum = selectedBeatmap.MD5Hash; return realm.Realm .All() diff --git a/osu.Game/Online/Rooms/PlaylistExtensions.cs b/osu.Game/Online/Rooms/PlaylistExtensions.cs index e78f91f20b..34c93bd9e0 100644 --- a/osu.Game/Online/Rooms/PlaylistExtensions.cs +++ b/osu.Game/Online/Rooms/PlaylistExtensions.cs @@ -41,6 +41,6 @@ namespace osu.Game.Online.Rooms } public static string GetTotalDuration(this BindableList playlist) => - playlist.Select(p => p.Beatmap.Value.Length).Sum().Milliseconds().Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 2); + playlist.Select(p => p.Beatmap.Length).Sum().Milliseconds().Humanize(minUnit: TimeUnit.Second, maxUnit: TimeUnit.Hour, precision: 2); } } diff --git a/osu.Game/Online/Rooms/PlaylistItem.cs b/osu.Game/Online/Rooms/PlaylistItem.cs index c082babb01..33718f050b 100644 --- a/osu.Game/Online/Rooms/PlaylistItem.cs +++ b/osu.Game/Online/Rooms/PlaylistItem.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Linq; using JetBrains.Annotations; @@ -12,6 +14,7 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.Rooms { + [JsonObject(MemberSerialization.OptIn)] public class PlaylistItem : IEquatable { [JsonProperty("id")] @@ -20,9 +23,6 @@ namespace osu.Game.Online.Rooms [JsonProperty("owner_id")] public int OwnerID { get; set; } - [JsonProperty("beatmap_id")] - public int BeatmapID { get; set; } - [JsonProperty("ruleset_id")] public int RulesetID { get; set; } @@ -38,35 +38,50 @@ namespace osu.Game.Online.Rooms [JsonProperty("played_at")] public DateTimeOffset? PlayedAt { get; set; } - [JsonIgnore] - public IBindable Valid => valid; - - private readonly Bindable valid = new BindableBool(true); - - [JsonIgnore] - public readonly Bindable Beatmap = new Bindable(); - - [JsonProperty("beatmap")] - private APIBeatmap apiBeatmap { get; set; } - [JsonProperty("allowed_mods")] public APIMod[] AllowedMods { get; set; } = Array.Empty(); [JsonProperty("required_mods")] public APIMod[] RequiredMods { get; set; } = Array.Empty(); - public PlaylistItem() + /// + /// Used for deserialising from the API. + /// + [JsonProperty("beatmap")] + private APIBeatmap apiBeatmap { - Beatmap.BindValueChanged(beatmap => BeatmapID = beatmap.NewValue?.OnlineID ?? -1); + // This getter is required/used internally by JSON.NET during deserialisation to do default-value comparisons. It is never used during serialisation (see: ShouldSerializeapiBeatmap()). + // It will always return a null value on deserialisation, which JSON.NET will handle gracefully. + get => (APIBeatmap)Beatmap; + set => Beatmap = value; + } + + /// + /// Used for serialising to the API. + /// + [JsonProperty("beatmap_id")] + private int onlineBeatmapId => Beatmap.OnlineID; + + [JsonIgnore] + public IBeatmapInfo Beatmap { get; set; } = null!; + + [JsonIgnore] + public IBindable Valid => valid; + + private readonly Bindable valid = new BindableBool(true); + + [JsonConstructor] + private PlaylistItem() + { + } + + public PlaylistItem(IBeatmapInfo beatmap) + { + Beatmap = beatmap; } public void MarkInvalid() => valid.Value = false; - public void MapObjects() - { - Beatmap.Value ??= apiBeatmap; - } - #region Newtonsoft.Json implicit ShouldSerialize() methods // The properties in this region are used implicitly by Newtonsoft.Json to not serialise certain fields in some cases. @@ -82,9 +97,22 @@ namespace osu.Game.Online.Rooms #endregion - public bool Equals(PlaylistItem other) + public PlaylistItem With(IBeatmapInfo beatmap) => new PlaylistItem(beatmap) + { + ID = ID, + OwnerID = OwnerID, + RulesetID = RulesetID, + Expired = Expired, + PlaylistOrder = PlaylistOrder, + PlayedAt = PlayedAt, + AllowedMods = AllowedMods, + RequiredMods = RequiredMods, + valid = { Value = Valid.Value }, + }; + + public bool Equals(PlaylistItem? other) => ID == other?.ID - && BeatmapID == other.BeatmapID + && Beatmap.OnlineID == other.Beatmap.OnlineID && RulesetID == other.RulesetID && Expired == other.Expired && AllowedMods.SequenceEqual(other.AllowedMods) diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs index e948c1adae..7cbe1a9017 100644 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/OnlinePlay/Components/BeatmapTitle.cs @@ -68,14 +68,14 @@ namespace osu.Game.Screens.OnlinePlay.Components } else { - var metadataInfo = beatmap.Value.Metadata; + var metadataInfo = beatmap.Metadata; string artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode; string titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode; var title = new RomanisableString($"{artistUnicode} - {titleUnicode}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title}".Trim()); - textFlow.AddLink(title, LinkAction.OpenBeatmap, beatmap.Value.OnlineID.ToString(), "Open beatmap"); + textFlow.AddLink(title, LinkAction.OpenBeatmap, beatmap.OnlineID.ToString(), "Open beatmap"); } } } diff --git a/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs index d534a1e374..8402619ebc 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ModeTypeInfo.cs @@ -67,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Components var mods = item.RequiredMods.Select(m => m.ToMod(ruleset)).ToArray(); drawableRuleset.FadeIn(transition_duration); - drawableRuleset.Child = new DifficultyIcon(item.Beatmap.Value, ruleset.RulesetInfo, mods) { Size = new Vector2(height) }; + drawableRuleset.Child = new DifficultyIcon(item.Beatmap, ruleset.RulesetInfo, mods) { Size = new Vector2(height) }; } else drawableRuleset.FadeOut(transition_duration); diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs index ffc5c07d4e..8906bebf0e 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Components { Schedule(() => { - var beatmap = playlistItem?.Beatmap.Value; + var beatmap = playlistItem?.Beatmap; string? lastCover = (background?.Beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.Covers.Cover; string? newCover = (beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.Covers.Cover; diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs index d144e1e3a9..d46ff12279 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundSprite.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.OnlinePlay.Components private void updateBeatmap() { - sprite.Beatmap.Value = Playlist.GetCurrentItem()?.Beatmap.Value; + sprite.Beatmap.Value = Playlist.GetCurrentItem()?.Beatmap; } protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs b/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs index f3e90aa396..7e31591389 100644 --- a/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs +++ b/osu.Game/Screens/OnlinePlay/Components/PlaylistItemBackground.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.OnlinePlay.Components public PlaylistItemBackground(PlaylistItem? playlistItem) { - Beatmap = playlistItem?.Beatmap.Value; + Beatmap = playlistItem?.Beatmap; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 21b64b61bb..4242886e66 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -112,9 +112,6 @@ namespace osu.Game.Screens.OnlinePlay.Components try { - foreach (var pi in room.Playlist) - pi.MapObjects(); - var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value); if (existing == null) rooms.Add(room); diff --git a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs index edf9c5d155..95ecadd21a 100644 --- a/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Components/StarRatingRangeDisplay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.OnlinePlay.Components private void updateRange(object sender, NotifyCollectionChangedEventArgs e) { - var orderedDifficulties = Playlist.Where(p => p.Beatmap.Value != null).Select(p => p.Beatmap.Value).OrderBy(b => b.StarRating).ToArray(); + var orderedDifficulties = Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray(); StarDifficulty minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarRating : 0, 0); StarDifficulty maxDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[^1].StarRating : 0, 0); diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index dcf2a5a480..25b36e0774 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -25,7 +24,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osu.Game.Online.Chat; -using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; @@ -68,8 +66,8 @@ namespace osu.Game.Screens.OnlinePlay private readonly DelayedLoadWrapper onScreenLoader = new DelayedLoadWrapper(Empty) { RelativeSizeAxes = Axes.Both }; private readonly IBindable valid = new Bindable(); - private readonly Bindable beatmap = new Bindable(); + private IBeatmapInfo beatmap; private IRulesetInfo ruleset; private Mod[] requiredMods; @@ -96,10 +94,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved] private UserLookupCache userLookupCache { get; set; } - [CanBeNull] - [Resolved(CanBeNull = true)] - private MultiplayerClient multiplayerClient { get; set; } - [Resolved] private BeatmapLookupCache beatmapLookupCache { get; set; } @@ -110,7 +104,6 @@ namespace osu.Game.Screens.OnlinePlay { Item = item; - beatmap.BindTo(item.Beatmap); valid.BindTo(item.Valid); if (item.Expired) @@ -151,7 +144,6 @@ namespace osu.Game.Screens.OnlinePlay maskingContainer.BorderThickness = isCurrent ? 5 : 0; }, true); - beatmap.BindValueChanged(_ => Scheduler.AddOnce(refresh)); valid.BindValueChanged(_ => Scheduler.AddOnce(refresh)); onScreenLoader.DelayedLoadStarted += _ => @@ -166,19 +158,9 @@ namespace osu.Game.Screens.OnlinePlay Schedule(() => ownerAvatar.User = foundUser); } - if (Item.Beatmap.Value == null) - { - IBeatmapInfo foundBeatmap; + beatmap = await beatmapLookupCache.GetBeatmapAsync(Item.Beatmap.OnlineID).ConfigureAwait(false); - if (multiplayerClient != null) - // This call can eventually go away (and use the else case below). - // Currently required only due to the method being overridden to provide special behaviour in tests. - foundBeatmap = await multiplayerClient.GetAPIBeatmap(Item.BeatmapID).ConfigureAwait(false); - else - foundBeatmap = await beatmapLookupCache.GetBeatmapAsync(Item.BeatmapID).ConfigureAwait(false); - - Schedule(() => Item.Beatmap.Value = foundBeatmap); - } + Scheduler.AddOnce(refresh); } catch (Exception e) { @@ -280,32 +262,36 @@ namespace osu.Game.Screens.OnlinePlay maskingContainer.BorderColour = colours.Red; } - if (Item.Beatmap.Value != null) - difficultyIconContainer.Child = new DifficultyIcon(Item.Beatmap.Value, ruleset, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(icon_height) }; + if (beatmap != null) + difficultyIconContainer.Child = new DifficultyIcon(beatmap, ruleset, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(icon_height) }; else difficultyIconContainer.Clear(); - panelBackground.Beatmap.Value = Item.Beatmap.Value; + panelBackground.Beatmap.Value = beatmap; beatmapText.Clear(); - if (Item.Beatmap.Value != null) + if (beatmap != null) { - beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineID.ToString(), null, text => - { - text.Truncate = true; - }); + beatmapText.AddLink(beatmap.GetDisplayTitleRomanisable(includeCreator: false), + LinkAction.OpenBeatmap, + beatmap.OnlineID.ToString(), + null, + text => + { + text.Truncate = true; + }); } authorText.Clear(); - if (!string.IsNullOrEmpty(Item.Beatmap.Value?.Metadata.Author.Username)) + if (!string.IsNullOrEmpty(beatmap?.Metadata.Author.Username)) { authorText.AddText("mapped by "); - authorText.AddUserLink(Item.Beatmap.Value.Metadata.Author); + authorText.AddUserLink(beatmap.Metadata.Author); } - bool hasExplicitContent = (Item.Beatmap.Value?.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true; + bool hasExplicitContent = (beatmap?.BeatmapSet as IBeatmapSetOnlineInfo)?.HasExplicitContent == true; explicitContentPill.Alpha = hasExplicitContent ? 1 : 0; modDisplay.Current.Value = requiredMods.ToArray(); @@ -457,7 +443,7 @@ namespace osu.Game.Screens.OnlinePlay Alpha = AllowShowingResults ? 1 : 0, TooltipText = "View results" }, - Item.Beatmap.Value == null ? Empty() : new PlaylistDownloadButton(Item), + beatmap == null ? Empty() : new PlaylistDownloadButton(beatmap), editButton = new PlaylistEditButton { Size = new Vector2(30, 30), @@ -499,7 +485,7 @@ namespace osu.Game.Screens.OnlinePlay private sealed class PlaylistDownloadButton : BeatmapDownloadButton { - private readonly PlaylistItem playlistItem; + private readonly IBeatmapInfo beatmap; [Resolved] private BeatmapManager beatmapManager { get; set; } @@ -509,10 +495,10 @@ namespace osu.Game.Screens.OnlinePlay private const float width = 50; - public PlaylistDownloadButton(PlaylistItem playlistItem) - : base(playlistItem.Beatmap.Value.BeatmapSet) + public PlaylistDownloadButton(IBeatmapInfo beatmap) + : base(beatmap.BeatmapSet) { - this.playlistItem = playlistItem; + this.beatmap = beatmap; Size = new Vector2(width, 30); Alpha = 0; @@ -532,7 +518,7 @@ namespace osu.Game.Screens.OnlinePlay { case DownloadState.LocallyAvailable: // Perform a local query of the beatmap by beatmap checksum, and reset the state if not matching. - if (beatmapManager.QueryBeatmap(b => b.MD5Hash == playlistItem.Beatmap.Value.MD5Hash) == null) + if (beatmapManager.QueryBeatmap(b => b.MD5Hash == beatmap.MD5Hash) == null) State.Value = DownloadState.NotDownloaded; else { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs index a87f21630c..a1a82c907a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/DrawableRoom.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Threading; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -12,6 +14,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -328,6 +331,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private OsuColour colours { get; set; } + [Resolved] + private BeatmapLookupCache beatmapLookupCache { get; set; } + private SpriteText statusText; private LinkFlowContainer beatmapText; @@ -385,8 +391,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components SelectedItem.BindValueChanged(onSelectedItemChanged, true); } + private CancellationTokenSource beatmapLookupCancellation; + private void onSelectedItemChanged(ValueChangedEvent item) { + beatmapLookupCancellation?.Cancel(); beatmapText.Clear(); if (Type.Value == MatchType.Playlists) @@ -395,17 +404,25 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components return; } - if (item.NewValue?.Beatmap.Value != null) - { - statusText.Text = "Currently playing "; - beatmapText.AddLink(item.NewValue.Beatmap.Value.GetDisplayTitleRomanisable(), - LinkAction.OpenBeatmap, - item.NewValue.Beatmap.Value.OnlineID.ToString(), - creationParameters: s => - { - s.Truncate = true; - }); - } + var beatmap = item.NewValue?.Beatmap; + if (beatmap == null) + return; + + var cancellationSource = beatmapLookupCancellation = new CancellationTokenSource(); + beatmapLookupCache.GetBeatmapAsync(beatmap.OnlineID, cancellationSource.Token) + .ContinueWith(task => Schedule(() => + { + if (cancellationSource.IsCancellationRequested) + return; + + var retrievedBeatmap = task.GetResultSafely(); + + statusText.Text = "Currently playing "; + beatmapText.AddLink(retrievedBeatmap.GetDisplayTitleRomanisable(), + LinkAction.OpenBeatmap, + retrievedBeatmap.OnlineID.ToString(), + creationParameters: s => s.Truncate = true); + }), cancellationSource.Token); } } diff --git a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs index a7b907c7d2..cdd2ae0c9c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Match/DrawableMatchRoom.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (editButton != null) host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true); - SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap.Value, true); + SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true); } protected override Drawable CreateBackground() => background = new BackgroundSprite(); diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 836629ada0..e297c90491 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -376,7 +376,7 @@ namespace osu.Game.Screens.OnlinePlay.Match private void updateWorkingBeatmap() { - var beatmap = SelectedItem.Value?.Beatmap.Value; + var beatmap = SelectedItem.Value?.Beatmap; // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineID == beatmap.OnlineID); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs index 12caf1fde1..e30ec36e9c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSongSelect.cs @@ -67,8 +67,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer var multiplayerItem = new MultiplayerPlaylistItem { ID = itemToEdit ?? 0, - BeatmapID = item.BeatmapID, - BeatmapChecksum = item.Beatmap.Value.MD5Hash, + BeatmapID = item.Beatmap.OnlineID, + BeatmapChecksum = item.Beatmap.MD5Hash, RulesetID = item.RulesetID, RequiredMods = item.RequiredMods.ToArray(), AllowedMods = item.AllowedMods.ToArray() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index cb50a56052..429b0ad89b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -398,38 +397,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void updateCurrentItem() { Debug.Assert(client.Room != null); - - var expectedSelectedItem = Room.Playlist.SingleOrDefault(i => i.ID == client.Room.Settings.PlaylistItemId); - - if (expectedSelectedItem == null) - return; - - // There's no reason to renew the selected item if its content hasn't changed. - if (SelectedItem.Value?.Equals(expectedSelectedItem) == true && expectedSelectedItem.Beatmap.Value != null) - return; - - // Clear the selected item while the lookup is performed, so components like the ready button can enter their disabled states. - SelectedItem.Value = null; - - if (expectedSelectedItem.Beatmap.Value == null) - { - Task.Run(async () => - { - var beatmap = await client.GetAPIBeatmap(expectedSelectedItem.BeatmapID).ConfigureAwait(false); - - Schedule(() => - { - expectedSelectedItem.Beatmap.Value = beatmap; - - if (Room.Playlist.SingleOrDefault(i => i.ID == client.Room?.Settings.PlaylistItemId)?.Equals(expectedSelectedItem) == true) - applyCurrentItem(); - }); - }); - } - else - applyCurrentItem(); - - void applyCurrentItem() => SelectedItem.Value = expectedSelectedItem; + SelectedItem.Value = Room.Playlist.SingleOrDefault(i => i.ID == client.Room.Settings.PlaylistItemId); } private void handleRoomLost() => Schedule(() => diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index eab1f83967..7b64784316 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -113,12 +113,8 @@ namespace osu.Game.Screens.OnlinePlay { itemSelected = true; - var item = new PlaylistItem + var item = new PlaylistItem(Beatmap.Value.BeatmapInfo) { - Beatmap = - { - Value = Beatmap.Value.BeatmapInfo - }, RulesetID = Ruleset.Value.OnlineID, RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(), AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray() diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs index 2b071175d5..8403e1e0fe 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsPlayer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void load(IBindable ruleset) { // Sanity checks to ensure that PlaylistsPlayer matches the settings for the current PlaylistItem - if (!Beatmap.Value.BeatmapInfo.MatchesOnlineID(PlaylistItem.Beatmap.Value)) + if (!Beatmap.Value.BeatmapInfo.MatchesOnlineID(PlaylistItem.Beatmap)) throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap"); if (ruleset.Value.OnlineID != PlaylistItem.RulesetID) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs index 6c8ab52d22..6674a37c3c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSettingsOverlay.cs @@ -392,7 +392,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists foreach (var item in Playlist) { - if (invalidBeatmapIDs.Contains(item.BeatmapID)) + if (invalidBeatmapIDs.Contains(item.Beatmap.OnlineID)) item.MarkInvalid(); } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs index 3ac576b18e..86591c1d6d 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsSongSelect.cs @@ -41,10 +41,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private void createNewItem() { - PlaylistItem item = new PlaylistItem + PlaylistItem item = new PlaylistItem(Beatmap.Value.BeatmapInfo) { ID = Playlist.Count == 0 ? 0 : Playlist.Max(p => p.ID) + 1, - Beatmap = { Value = Beatmap.Value.BeatmapInfo }, RulesetID = Ruleset.Value.OnlineID, RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(), AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray() diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index 19f9952d14..6c40546325 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -46,9 +46,8 @@ namespace osu.Game.Tests.Visual.Multiplayer Name = { Value = "test name" }, Playlist = { - new PlaylistItem + new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo) { - Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo }, RulesetID = Ruleset.Value.OnlineID } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 15ede6cc26..6dc5159b6f 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -7,14 +7,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Game.Beatmaps; using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; @@ -39,9 +36,6 @@ namespace osu.Game.Tests.Visual.Multiplayer [Resolved] private IAPIProvider api { get; set; } = null!; - [Resolved] - private BeatmapManager beatmaps { get; set; } = null!; - private readonly TestMultiplayerRoomManager roomManager; /// @@ -407,23 +401,6 @@ namespace osu.Game.Tests.Visual.Multiplayer public override Task RemovePlaylistItem(long playlistItemId) => RemoveUserPlaylistItem(api.LocalUser.Value.OnlineID, playlistItemId); - public override Task GetAPIBeatmap(int beatmapId, CancellationToken cancellationToken = default) - { - IBeatmapInfo? beatmap = roomManager.ServerSideRooms.SelectMany(r => r.Playlist) - .FirstOrDefault(p => p.BeatmapID == beatmapId)?.Beatmap.Value - ?? beatmaps.QueryBeatmap(b => b.OnlineID == beatmapId); - - if (beatmap == null) - throw new InvalidOperationException("Beatmap not found."); - - return Task.FromResult(new APIBeatmap - { - BeatmapSet = new APIBeatmapSet { OnlineID = beatmap.BeatmapSet?.OnlineID ?? -1 }, - OnlineID = beatmapId, - Checksum = beatmap.MD5Hash - }); - } - private async Task changeMatchType(MatchType type) { Debug.Assert(Room != null); diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs index baff7c168f..b6a347a896 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Database; +using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay; @@ -33,9 +34,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay protected override Container Content => content; - [Resolved] - private OsuGameBase game { get; set; } - private readonly Container content; private readonly Container drawableDependenciesContainer; private DelegatedDependencyContainer dependencies; @@ -62,19 +60,16 @@ namespace osu.Game.Tests.Visual.OnlinePlay drawableDependenciesContainer.Clear(); dependencies.OnlinePlayDependencies = CreateOnlinePlayDependencies(); drawableDependenciesContainer.AddRange(OnlinePlayDependencies.DrawableComponents); + + var handler = OnlinePlayDependencies.RequestsHandler; + + // Resolving the BeatmapManager in the test scene will inject the game-wide BeatmapManager, while many test scenes cache their own BeatmapManager instead. + // To get around this, the BeatmapManager is looked up from the dependencies provided to the children of the test scene instead. + var beatmapManager = dependencies.Get(); + + ((DummyAPIAccess)API).HandleRequest = request => handler.HandleRequest(request, API.LocalUser.Value, beatmapManager); }); - public override void SetUpSteps() - { - base.SetUpSteps(); - - AddStep("setup API", () => - { - var handler = OnlinePlayDependencies.RequestsHandler; - ((DummyAPIAccess)API).HandleRequest = request => handler.HandleRequest(request, API.LocalUser.Value, game); - }); - } - /// /// Creates the room dependencies. Called every . /// diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index 73d0df2c36..8dfd969c51 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -43,16 +43,9 @@ namespace osu.Game.Tests.Visual.OnlinePlay if (ruleset != null) { - room.Playlist.Add(new PlaylistItem + room.Playlist.Add(new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() }) { RulesetID = ruleset.OnlineID, - Beatmap = - { - Value = new BeatmapInfo - { - Metadata = new BeatmapMetadata() - } - } }); } diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs index 5a0a7e71d4..8290af8f78 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomRequestsHandler.cs @@ -4,12 +4,16 @@ using System; using System.Collections.Generic; using System.Linq; +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.Online.Rooms; +using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual.OnlinePlay { @@ -33,9 +37,9 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// /// The API request to handle. /// The local user to store in responses where required. - /// The game base for cases where actual online requests need to be sent. + /// The beatmap manager to attempt to retrieve beatmaps from, prior to returning dummy beatmaps. /// Whether the request was successfully handled. - public bool HandleRequest(APIRequest request, APIUser localUser, OsuGameBase game) + public bool HandleRequest(APIRequest request, APIUser localUser, BeatmapManager beatmapManager) { switch (request) { @@ -128,6 +132,26 @@ namespace osu.Game.Tests.Visual.OnlinePlay Statistics = new Dictionary() }); return true; + + case GetBeatmapsRequest getBeatmapsRequest: + var result = new List(); + + foreach (int id in getBeatmapsRequest.BeatmapIds) + { + var baseBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineID == id); + + if (baseBeatmap == null) + { + baseBeatmap = new TestBeatmap(new RulesetInfo { OnlineID = 0 }).BeatmapInfo; + baseBeatmap.OnlineID = id; + baseBeatmap.BeatmapSet!.OnlineID = id; + } + + result.Add(OsuTestScene.CreateAPIBeatmap(baseBeatmap)); + } + + getBeatmapsRequest.TriggerSuccess(new GetBeatmapsResponse { Beatmaps = result }); + return true; } return false; diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index ec02655544..f287a04d71 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -225,12 +225,24 @@ namespace osu.Game.Tests.Visual protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset); /// - /// Returns a sample API Beatmap with BeatmapSet populated. + /// Returns a sample API beatmap with a populated beatmap set. /// /// The ruleset to create the sample model using. osu! ruleset will be used if not specified. - protected APIBeatmap CreateAPIBeatmap(RulesetInfo ruleset = null) + protected APIBeatmap CreateAPIBeatmap(RulesetInfo ruleset = null) => CreateAPIBeatmap(CreateBeatmap(ruleset ?? Ruleset.Value).BeatmapInfo); + + /// + /// Constructs a sample API beatmap set containing a beatmap. + /// + /// The ruleset to create the sample model using. osu! ruleset will be used if not specified. + protected APIBeatmapSet CreateAPIBeatmapSet(RulesetInfo ruleset = null) => CreateAPIBeatmapSet(CreateBeatmap(ruleset ?? Ruleset.Value).BeatmapInfo); + + /// + /// Constructs a sample API beatmap with a populated beatmap set from a given source beatmap. + /// + /// The source beatmap. + public static APIBeatmap CreateAPIBeatmap(IBeatmapInfo original) { - var beatmapSet = CreateAPIBeatmapSet(ruleset ?? Ruleset.Value); + var beatmapSet = CreateAPIBeatmapSet(original); // Avoid circular reference. var beatmap = beatmapSet.Beatmaps.First(); @@ -243,18 +255,16 @@ namespace osu.Game.Tests.Visual } /// - /// Returns a sample API BeatmapSet with beatmaps populated. + /// Constructs a sample API beatmap set containing a beatmap from a given source beatmap. /// - /// The ruleset to create the sample model using. osu! ruleset will be used if not specified. - protected APIBeatmapSet CreateAPIBeatmapSet(RulesetInfo ruleset = null) + /// The source beatmap. + public static APIBeatmapSet CreateAPIBeatmapSet(IBeatmapInfo original) { - var beatmap = CreateBeatmap(ruleset ?? Ruleset.Value).BeatmapInfo; - - Debug.Assert(beatmap.BeatmapSet != null); + Debug.Assert(original.BeatmapSet != null); return new APIBeatmapSet { - OnlineID = ((IBeatmapSetInfo)beatmap.BeatmapSet).OnlineID, + OnlineID = original.BeatmapSet.OnlineID, Status = BeatmapOnlineStatus.Ranked, Covers = new BeatmapSetOnlineCovers { @@ -262,29 +272,29 @@ namespace osu.Game.Tests.Visual Card = "https://assets.ppy.sh/beatmaps/163112/covers/card.jpg", List = "https://assets.ppy.sh/beatmaps/163112/covers/list.jpg" }, - Title = beatmap.Metadata.Title, - TitleUnicode = beatmap.Metadata.TitleUnicode, - Artist = beatmap.Metadata.Artist, - ArtistUnicode = beatmap.Metadata.ArtistUnicode, + Title = original.Metadata.Title, + TitleUnicode = original.Metadata.TitleUnicode, + Artist = original.Metadata.Artist, + ArtistUnicode = original.Metadata.ArtistUnicode, Author = new APIUser { - Username = beatmap.Metadata.Author.Username, - Id = beatmap.Metadata.Author.OnlineID + Username = original.Metadata.Author.Username, + Id = original.Metadata.Author.OnlineID }, - Source = beatmap.Metadata.Source, - Tags = beatmap.Metadata.Tags, + Source = original.Metadata.Source, + Tags = original.Metadata.Tags, Beatmaps = new[] { new APIBeatmap { - OnlineID = ((IBeatmapInfo)beatmap).OnlineID, - OnlineBeatmapSetID = ((IBeatmapSetInfo)beatmap.BeatmapSet).OnlineID, - Status = beatmap.Status, - Checksum = beatmap.MD5Hash, - AuthorID = beatmap.Metadata.Author.OnlineID, - RulesetID = beatmap.Ruleset.OnlineID, - StarRating = beatmap.StarRating, - DifficultyName = beatmap.DifficultyName, + OnlineID = original.OnlineID, + OnlineBeatmapSetID = original.BeatmapSet.OnlineID, + Status = ((BeatmapInfo)original).Status, + Checksum = original.MD5Hash, + AuthorID = original.Metadata.Author.OnlineID, + RulesetID = original.Ruleset.OnlineID, + StarRating = original.StarRating, + DifficultyName = original.DifficultyName, } } }; diff --git a/osu.Game/Utils/NamingUtils.cs b/osu.Game/Utils/NamingUtils.cs new file mode 100644 index 0000000000..482e3d0954 --- /dev/null +++ b/osu.Game/Utils/NamingUtils.cs @@ -0,0 +1,61 @@ +// 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.Text.RegularExpressions; + +namespace osu.Game.Utils +{ + public static class NamingUtils + { + /// + /// Given a set of and a target , + /// finds a "best" name closest to that is not in . + /// + /// + /// + /// This helper is most useful in scenarios when creating new objects in a set + /// (such as adding new difficulties to a beatmap set, or creating a clone of an existing object that needs a unique name). + /// If is already present in , + /// this method will append the lowest possible number in brackets that doesn't conflict with + /// to and return that. + /// See osu.Game.Tests.Utils.NamingUtilsTest for concrete examples of behaviour. + /// + /// + /// and are compared in a case-insensitive manner, + /// so this method is safe to use for naming files in a platform-invariant manner. + /// + /// + public static string GetNextBestName(IEnumerable existingNames, string desiredName) + { + string pattern = $@"^(?i){Regex.Escape(desiredName)}(?-i)( \((?[1-9][0-9]*)\))?$"; + var regex = new Regex(pattern, RegexOptions.Compiled); + var takenNumbers = new HashSet(); + + foreach (string name in existingNames) + { + var match = regex.Match(name); + if (!match.Success) + continue; + + string copyNumberString = match.Groups[@"copyNumber"].Value; + + if (string.IsNullOrEmpty(copyNumberString)) + { + takenNumbers.Add(0); + continue; + } + + takenNumbers.Add(int.Parse(copyNumberString)); + } + + int bestNumber = 0; + while (takenNumbers.Contains(bestNumber)) + bestNumber += 1; + + return bestNumber == 0 + ? desiredName + : $"{desiredName} ({bestNumber})"; + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7b50c804ff..4c6f81defa 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 03a105673c..99b9de3fe2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -60,7 +60,7 @@ - + @@ -83,7 +83,7 @@ - +