1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 12:33:01 +08:00

Merge branch 'master' into net6

This commit is contained in:
Dan Balasescu 2022-02-16 00:40:04 +09:00 committed by GitHub
commit ea5bb46fb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 751 additions and 387 deletions

View File

@ -66,7 +66,7 @@ namespace osu.Game.Tests.Online
selectedItem.Value = new PlaylistItem
{
Beatmap = { Value = testBeatmapInfo },
Ruleset = { Value = testBeatmapInfo.Ruleset },
RulesetID = testBeatmapInfo.Ruleset.OnlineID,
};
Child = availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker

Binary file not shown.

View File

@ -19,6 +19,10 @@ namespace osu.Game.Tests.Visual.Menus
base.SetUpSteps();
AddAssert("no screen offset applied", () => Game.ScreenOffsetContainer.X == 0f);
// avoids mouse interacting with settings overlay.
AddStep("move mouse to centre", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for overlays", () => Game.Settings.IsLoaded && Game.Notifications.IsLoaded);
}

View File

@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = InitialBeatmap },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}));

View File

@ -17,6 +17,7 @@ using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Models;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
@ -215,25 +216,25 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
ID = 0,
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
Expired = true,
RequiredMods =
RequiredMods = new[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
new OsuModAutoplay()
new APIMod(new OsuModHardRock()),
new APIMod(new OsuModDoubleTime()),
new APIMod(new OsuModAutoplay())
}
},
new PlaylistItem
{
ID = 1,
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RequiredMods =
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
RequiredMods = new[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
new OsuModAutoplay()
new APIMod(new OsuModHardRock()),
new APIMod(new OsuModDoubleTime()),
new APIMod(new OsuModAutoplay())
}
}
}
@ -314,12 +315,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
BeatmapSet = new BeatmapSetInfo()
}
},
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RequiredMods =
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
RequiredMods = new[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
new OsuModAutoplay()
new APIMod(new OsuModHardRock()),
new APIMod(new OsuModDoubleTime()),
new APIMod(new OsuModAutoplay())
}
});
}
@ -348,12 +349,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
ID = index++,
OwnerID = 2,
Beatmap = { Value = b },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RequiredMods =
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
RequiredMods = new[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
new OsuModAutoplay()
new APIMod(new OsuModHardRock()),
new APIMod(new OsuModDoubleTime()),
new APIMod(new OsuModAutoplay())
}
});
}

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
@ -35,12 +36,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
ID = SelectedRoom.Value.Playlist.Count,
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RequiredMods =
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
RequiredMods = new[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
new OsuModAutoplay()
new APIMod(new OsuModHardRock()),
new APIMod(new OsuModDoubleTime()),
new APIMod(new OsuModAutoplay())
}
});
}

View File

@ -19,6 +19,7 @@ using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
@ -99,7 +100,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -235,7 +236,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -257,7 +258,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}, API.LocalUser.Value);
@ -287,7 +288,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}, API.LocalUser.Value);
@ -318,7 +319,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -340,7 +341,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}, API.LocalUser.Value);
@ -373,7 +374,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -393,7 +394,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -415,7 +416,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -454,7 +455,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -493,7 +494,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -532,7 +533,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -566,7 +567,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -606,7 +607,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -626,8 +627,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
AllowedMods = { new OsuModHidden() }
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
AllowedMods = new[] { new APIMod(new OsuModHidden()) }
}
}
});
@ -666,7 +667,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -697,7 +698,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
}, API.LocalUser.Value);
@ -715,7 +716,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
ID = 2,
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@ -743,7 +744,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -779,7 +780,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -818,7 +819,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -849,7 +850,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -882,7 +883,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});

View File

@ -10,14 +10,18 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
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.Rooms;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
@ -72,7 +76,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@ -89,8 +93,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new TaikoRuleset().RulesetInfo },
AllowedMods = { new TaikoModSwap() }
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
AllowedMods = new[] { new APIMod(new TaikoModSwap()) }
});
});
@ -112,7 +116,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@ -127,7 +131,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@ -149,5 +153,28 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("match started", () => Client.Room?.State == MultiplayerRoomState.WaitingForLoad);
}
[Test]
public void TestFreeModSelectionHasAllowedMods()
{
AddStep("add playlist item with allowed mod", () =>
{
SelectedRoom.Value.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
});
});
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
AddUntilStep("wait for join", () => RoomJoined);
ClickButtonWhenEnabled<RoomSubScreen.UserModSelectButton>();
AddUntilStep("mod select contains only double time mod",
() => this.ChildrenOfType<UserModSelectOverlay>().SingleOrDefault()?.ChildrenOfType<ModButton>().SingleOrDefault()?.Mod is OsuModDoubleTime);
}
}
}

View File

@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
Stack.Push(player = new MultiplayerPlayer(Client.APIRoom, new PlaylistItem
{
Beatmap = { Value = Beatmap.Value.BeatmapInfo },
Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset }
RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID,
}, Client.Room?.Users.ToArray()));
});

View File

@ -146,12 +146,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
Ruleset = { Value = Ruleset.Value }
RulesetID = Ruleset.Value.OnlineID,
},
new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
Ruleset = { Value = Ruleset.Value },
RulesetID = Ruleset.Value.OnlineID,
Expired = true
}
}

View File

@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
selectedItem.Value = new PlaylistItem
{
Beatmap = { Value = Beatmap.Value.BeatmapInfo },
Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset },
RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID
};
if (button != null)

View File

@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
selectedItem.Value = new PlaylistItem
{
Beatmap = { Value = Beatmap.Value.BeatmapInfo },
Ruleset = { Value = Beatmap.Value.BeatmapInfo.Ruleset },
RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID,
};
Child = new FillFlowContainer

View File

@ -13,6 +13,7 @@ using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Models;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
@ -161,12 +162,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
BeatmapSet = new BeatmapSetInfo()
}
},
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RequiredMods =
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
RequiredMods = new[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
new OsuModAutoplay()
new APIMod(new OsuModHardRock()),
new APIMod(new OsuModDoubleTime()),
new APIMod(new OsuModAutoplay())
}
});
}

View File

@ -115,8 +115,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("change mod rate", () => ((OsuModDoubleTime)SelectedMods.Value[0]).SpeedChange.Value = 2);
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
AddAssert("item 1 has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0]).SpeedChange.Value));
AddAssert("item 2 has rate 2", () => Precision.AlmostEquals(2, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.Last().RequiredMods[0]).SpeedChange.Value));
AddAssert("item 1 has rate 1.5", () =>
{
var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(1.5, mod.SpeedChange.Value);
});
AddAssert("item 2 has rate 2", () =>
{
var mod = (OsuModDoubleTime)SelectedRoom.Value.Playlist.Last().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(2, mod.SpeedChange.Value);
});
}
/// <summary>
@ -138,7 +147,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
AddAssert("item has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0]).SpeedChange.Value));
AddAssert("item has rate 1.5", () =>
{
var m = (OsuModDoubleTime)SelectedRoom.Value.Playlist.First().RequiredMods[0].ToMod(new OsuRuleset());
return Precision.AlmostEquals(1.5, m.SpeedChange.Value);
});
}
private class TestPlaylistsSongSelect : PlaylistsSongSelect

View File

@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});
@ -159,7 +159,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo },
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}
}
});

View File

@ -0,0 +1,43 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.IO;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Models;
using osu.Game.Scoring;
using osu.Game.Skinning;
using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestEFToRealmMigration : OsuGameTestScene
{
public override void RecycleLocalStorage(bool isDisposing)
{
base.RecycleLocalStorage(isDisposing);
if (isDisposing)
return;
using (var outStream = LocalStorage.GetStream(DatabaseContextFactory.DATABASE_NAME, FileAccess.Write, FileMode.Create))
using (var stream = TestResources.OpenResource(DatabaseContextFactory.DATABASE_NAME))
stream.CopyTo(outStream);
}
[Test]
public void TestMigration()
{
// Numbers are taken from the test database (see commit f03de16ee5a46deac3b5f2ca1edfba5c4c5dca7d).
AddAssert("Check beatmaps", () => Game.Dependencies.Get<RealmAccess>().Run(r => r.All<BeatmapSetInfo>().Count(s => !s.Protected) == 1));
AddAssert("Check skins", () => Game.Dependencies.Get<RealmAccess>().Run(r => r.All<SkinInfo>().Count(s => !s.Protected) == 1));
AddAssert("Check scores", () => Game.Dependencies.Get<RealmAccess>().Run(r => r.All<ScoreInfo>().Count() == 1));
// One extra file is created during realm migration / startup due to the circles intro import.
AddAssert("Check files", () => Game.Dependencies.Get<RealmAccess>().Run(r => r.All<RealmFile>().Count() == 271));
}
}
}

View File

@ -168,7 +168,7 @@ namespace osu.Game.Tests.Visual.Playlists
LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
}));
});

View File

@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Playlists
room.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = importedBeatmap.Beatmaps.First() },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Playlists
room.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = importedBeatmap.Beatmaps.First() },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@ -109,7 +109,7 @@ namespace osu.Game.Tests.Visual.Playlists
room.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = importedBeatmap.Beatmaps.First() },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});
@ -173,7 +173,7 @@ namespace osu.Game.Tests.Visual.Playlists
}
}
},
Ruleset = { Value = new OsuRuleset().RulesetInfo }
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
});
});

View File

@ -0,0 +1,158 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Settings.Sections;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneExpandingContainer : OsuManualInputManagerTestScene
{
private TestExpandingContainer container;
private SettingsToolboxGroup toolboxGroup;
private ExpandableSlider<float, SizeSlider> slider1;
private ExpandableSlider<double> slider2;
[SetUp]
public void SetUp() => Schedule(() =>
{
Child = container = new TestExpandingContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Height = 0.33f,
Child = toolboxGroup = new SettingsToolboxGroup("sliders")
{
RelativeSizeAxes = Axes.X,
Width = 1,
Children = new Drawable[]
{
slider1 = new ExpandableSlider<float, SizeSlider>
{
Current = new BindableFloat
{
Default = 1.0f,
MinValue = 1.0f,
MaxValue = 10.0f,
Precision = 0.01f,
},
},
slider2 = new ExpandableSlider<double>
{
Current = new BindableDouble
{
Default = 1.0,
MinValue = 1.0,
MaxValue = 10.0,
Precision = 0.01,
},
},
}
}
};
slider1.Current.BindValueChanged(v =>
{
slider1.ExpandedLabelText = $"Slider One ({v.NewValue:0.##x})";
slider1.ContractedLabelText = $"S. 1. ({v.NewValue:0.##x})";
}, true);
slider2.Current.BindValueChanged(v =>
{
slider2.ExpandedLabelText = $"Slider Two ({v.NewValue:N2})";
slider2.ContractedLabelText = $"S. 2. ({v.NewValue:N2})";
}, true);
});
[Test]
public void TestDisplay()
{
AddStep("switch to contracted", () => container.Expanded.Value = false);
AddStep("switch to expanded", () => container.Expanded.Value = true);
AddStep("set left origin", () => container.Origin = Anchor.CentreLeft);
AddStep("set centre origin", () => container.Origin = Anchor.Centre);
AddStep("set right origin", () => container.Origin = Anchor.CentreRight);
}
/// <summary>
/// Tests hovering expands the container and does not contract until hover is lost.
/// </summary>
[Test]
public void TestHoveringExpandsContainer()
{
AddAssert("ensure container contracted", () => !container.Expanded.Value);
AddStep("hover container", () => InputManager.MoveMouseTo(container));
AddAssert("container expanded", () => container.Expanded.Value);
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
AddAssert("container contracted", () => !container.Expanded.Value);
AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
}
/// <summary>
/// Tests expanding a container will expand underlying groups if contracted.
/// </summary>
[Test]
public void TestExpandingContainerExpandsContractedGroup()
{
AddStep("contract group", () => toolboxGroup.Expanded.Value = false);
AddStep("expand container", () => container.Expanded.Value = true);
AddAssert("group expanded", () => toolboxGroup.Expanded.Value);
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
AddStep("contract container", () => container.Expanded.Value = false);
AddAssert("group contracted", () => !toolboxGroup.Expanded.Value);
AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
}
/// <summary>
/// Tests contracting a container does not contract underlying groups if expanded by user (i.e. by setting <see cref="SettingsToolboxGroup.Expanded"/> directly).
/// </summary>
[Test]
public void TestContractingContainerDoesntContractUserExpandedGroup()
{
AddAssert("ensure group expanded", () => toolboxGroup.Expanded.Value);
AddStep("expand container", () => container.Expanded.Value = true);
AddAssert("group still expanded", () => toolboxGroup.Expanded.Value);
AddAssert("controls expanded", () => slider1.Expanded.Value && slider2.Expanded.Value);
AddStep("contract container", () => container.Expanded.Value = false);
AddAssert("group still expanded", () => toolboxGroup.Expanded.Value);
AddAssert("controls contracted", () => !slider1.Expanded.Value && !slider2.Expanded.Value);
}
/// <summary>
/// Tests expanding a container via <see cref="ExpandingContainer.Expanded"/> does not get contracted by losing hover.
/// </summary>
[Test]
public void TestExpandingContainerDoesntGetContractedByHover()
{
AddStep("expand container", () => container.Expanded.Value = true);
AddStep("hover container", () => InputManager.MoveMouseTo(container));
AddAssert("container still expanded", () => container.Expanded.Value);
AddStep("hover away", () => InputManager.MoveMouseTo(Vector2.Zero));
AddAssert("container still expanded", () => container.Expanded.Value);
}
private class TestExpandingContainer : ExpandingContainer
{
public TestExpandingContainer()
: base(120, 250)
{
}
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// An <see cref="ExpandingContainer"/> with a long hover expansion delay.
/// </summary>
/// <remarks>
/// Mostly used for buttons with explanatory labels, in which the label would display after a "long hover".
/// </remarks>
public class ExpandingButtonContainer : ExpandingContainer
{
protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
: base(contractedWidth, expandedWidth)
{
}
protected override double HoverExpansionDelay => 400;
}
}

View File

@ -0,0 +1,100 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Threading;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// Represents a <see cref="Container"/> with the ability to expand/contract on hover.
/// </summary>
public class ExpandingContainer : Container, IExpandingContainer
{
private readonly float contractedWidth;
private readonly float expandedWidth;
public BindableBool Expanded { get; } = new BindableBool();
/// <summary>
/// Delay before the container switches to expanded state from hover.
/// </summary>
protected virtual double HoverExpansionDelay => 0;
protected override Container<Drawable> Content => FillFlow;
protected FillFlowContainer FillFlow { get; }
protected ExpandingContainer(float contractedWidth, float expandedWidth)
{
this.contractedWidth = contractedWidth;
this.expandedWidth = expandedWidth;
RelativeSizeAxes = Axes.Y;
Width = contractedWidth;
InternalChild = new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = FillFlow = new FillFlowContainer
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
},
};
}
private ScheduledDelegate hoverExpandEvent;
protected override void LoadComplete()
{
base.LoadComplete();
Expanded.BindValueChanged(v =>
{
this.ResizeWidthTo(v.NewValue ? expandedWidth : contractedWidth, 500, Easing.OutQuint);
}, true);
}
protected override bool OnHover(HoverEvent e)
{
updateHoverExpansion();
return true;
}
protected override bool OnMouseMove(MouseMoveEvent e)
{
updateHoverExpansion();
return base.OnMouseMove(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
if (hoverExpandEvent != null)
{
hoverExpandEvent?.Cancel();
hoverExpandEvent = null;
Expanded.Value = false;
return;
}
base.OnHoverLost(e);
}
private void updateHoverExpansion()
{
hoverExpandEvent?.Cancel();
if (IsHovered && !Expanded.Value)
hoverExpandEvent = Scheduler.AddDelayed(() => Expanded.Value = true, HoverExpansionDelay);
}
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// An interface for drawables with ability to expand/contract.
/// </summary>
public interface IExpandable : IDrawable
{
/// <summary>
/// Whether this drawable is in an expanded state.
/// </summary>
BindableBool Expanded { get; }
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// A target expanding container that should be resolved by children <see cref="IExpandable"/>s to propagate state changes.
/// </summary>
[Cached(typeof(IExpandingContainer))]
public interface IExpandingContainer : IContainer, IExpandable
{
}
}

View File

@ -0,0 +1,126 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Localisation;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osuTK;
namespace osu.Game.Graphics.UserInterface
{
/// <summary>
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
/// </summary>
public class ExpandableSlider<T, TSlider> : CompositeDrawable, IExpandable, IHasCurrentValue<T>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
where TSlider : OsuSliderBar<T>, new()
{
private readonly OsuSpriteText label;
private readonly TSlider slider;
private LocalisableString contractedLabelText;
/// <summary>
/// The label text to display when this slider is in a contracted state.
/// </summary>
public LocalisableString ContractedLabelText
{
get => contractedLabelText;
set
{
if (value == contractedLabelText)
return;
contractedLabelText = value;
if (!Expanded.Value)
label.Text = value;
}
}
private LocalisableString expandedLabelText;
/// <summary>
/// The label text to display when this slider is in an expanded state.
/// </summary>
public LocalisableString ExpandedLabelText
{
get => expandedLabelText;
set
{
if (value == expandedLabelText)
return;
expandedLabelText = value;
if (Expanded.Value)
label.Text = value;
}
}
public Bindable<T> Current
{
get => slider.Current;
set => slider.Current = value;
}
public BindableBool Expanded { get; } = new BindableBool();
public override bool HandlePositionalInput => true;
public ExpandableSlider()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
label = new OsuSpriteText(),
slider = new TSlider
{
RelativeSizeAxes = Axes.X,
},
}
};
}
[Resolved(canBeNull: true)]
private IExpandingContainer expandingContainer { get; set; }
protected override void LoadComplete()
{
base.LoadComplete();
expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
{
Expanded.Value = containerExpanded.NewValue;
}, true);
Expanded.BindValueChanged(v =>
{
label.Text = v.NewValue ? expandedLabelText : contractedLabelText;
slider.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint);
slider.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
}, true);
}
}
/// <summary>
/// An <see cref="IExpandable"/> implementation for the UI slider bar control.
/// </summary>
public class ExpandableSlider<T> : ExpandableSlider<T, OsuSliderBar<T>>
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
{
}
}

View File

@ -727,30 +727,18 @@ namespace osu.Game.Online.Multiplayer
RoomUpdated?.Invoke();
}
private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item)
private PlaylistItem createPlaylistItem(MultiplayerPlaylistItem item) => new PlaylistItem
{
var ruleset = Rulesets.GetRuleset(item.RulesetID);
Debug.Assert(ruleset != null);
var rulesetInstance = ruleset.CreateInstance();
var playlistItem = new PlaylistItem
{
ID = item.ID,
BeatmapID = item.BeatmapID,
OwnerID = item.OwnerID,
Ruleset = { Value = ruleset },
Expired = item.Expired,
PlaylistOrder = item.PlaylistOrder,
PlayedAt = item.PlayedAt
};
playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance)));
playlistItem.AllowedMods.AddRange(item.AllowedMods.Select(m => m.ToMod(rulesetInstance)));
return playlistItem;
}
ID = item.ID,
BeatmapID = item.BeatmapID,
OwnerID = item.OwnerID,
RulesetID = item.RulesetID,
Expired = item.Expired,
PlaylistOrder = item.PlaylistOrder,
PlayedAt = item.PlayedAt,
RequiredMods = item.RequiredMods.ToArray(),
AllowedMods = item.AllowedMods.ToArray()
};
/// <summary>
/// Retrieves a <see cref="APIBeatmap"/> from an online source.

View File

@ -66,8 +66,8 @@ namespace osu.Game.Online.Rooms
BeatmapID = item.BeatmapID;
BeatmapChecksum = item.Beatmap.Value?.MD5Hash ?? string.Empty;
RulesetID = item.RulesetID;
RequiredMods = item.RequiredMods.Select(m => new APIMod(m)).ToArray();
AllowedMods = item.AllowedMods.Select(m => new APIMod(m)).ToArray();
RequiredMods = item.RequiredMods.ToArray();
AllowedMods = item.AllowedMods.ToArray();
Expired = item.Expired;
PlaylistOrder = item.PlaylistOrder ?? 0;
PlayedAt = item.PlayedAt;

View File

@ -65,7 +65,11 @@ namespace osu.Game.Online.Rooms
public ScoreInfo CreateScoreInfo(RulesetStore rulesets, PlaylistItem playlistItem, [NotNull] BeatmapInfo beatmap)
{
var rulesetInstance = playlistItem.Ruleset.Value.CreateInstance();
var ruleset = rulesets.GetRuleset(playlistItem.RulesetID);
if (ruleset == null)
throw new InvalidOperationException($"Couldn't create score with unknown ruleset: {playlistItem.RulesetID}");
var rulesetInstance = ruleset.CreateInstance();
var scoreInfo = new ScoreInfo
{

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
using Newtonsoft.Json;
@ -10,8 +9,6 @@ using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Online.Rooms
{
@ -49,68 +46,25 @@ namespace osu.Game.Online.Rooms
[JsonIgnore]
public readonly Bindable<IBeatmapInfo> Beatmap = new Bindable<IBeatmapInfo>();
[JsonIgnore]
public readonly Bindable<IRulesetInfo> Ruleset = new Bindable<IRulesetInfo>();
[JsonIgnore]
public readonly BindableList<Mod> AllowedMods = new BindableList<Mod>();
[JsonIgnore]
public readonly BindableList<Mod> RequiredMods = new BindableList<Mod>();
[JsonProperty("beatmap")]
private APIBeatmap apiBeatmap { get; set; }
private APIMod[] allowedModsBacking;
[JsonProperty("allowed_mods")]
private APIMod[] allowedMods
{
get => AllowedMods.Select(m => new APIMod(m)).ToArray();
set => allowedModsBacking = value;
}
private APIMod[] requiredModsBacking;
public APIMod[] AllowedMods { get; set; } = Array.Empty<APIMod>();
[JsonProperty("required_mods")]
private APIMod[] requiredMods
{
get => RequiredMods.Select(m => new APIMod(m)).ToArray();
set => requiredModsBacking = value;
}
public APIMod[] RequiredMods { get; set; } = Array.Empty<APIMod>();
public PlaylistItem()
{
Beatmap.BindValueChanged(beatmap => BeatmapID = beatmap.NewValue?.OnlineID ?? -1);
Ruleset.BindValueChanged(ruleset => RulesetID = ruleset.NewValue?.OnlineID ?? 0);
}
public void MarkInvalid() => valid.Value = false;
public void MapObjects(IRulesetStore rulesets)
public void MapObjects()
{
Beatmap.Value ??= apiBeatmap;
Ruleset.Value ??= rulesets.GetRuleset(RulesetID);
Debug.Assert(Ruleset.Value != null);
Ruleset rulesetInstance = Ruleset.Value.CreateInstance();
if (allowedModsBacking != null)
{
AllowedMods.Clear();
AllowedMods.AddRange(allowedModsBacking.Select(m => m.ToMod(rulesetInstance)));
allowedModsBacking = null;
}
if (requiredModsBacking != null)
{
RequiredMods.Clear();
RequiredMods.AddRange(requiredModsBacking.Select(m => m.ToMod(rulesetInstance)));
requiredModsBacking = null;
}
}
#region Newtonsoft.Json implicit ShouldSerialize() methods
@ -133,7 +87,7 @@ namespace osu.Game.Online.Rooms
&& BeatmapID == other.BeatmapID
&& RulesetID == other.RulesetID
&& Expired == other.Expired
&& allowedMods.SequenceEqual(other.allowedMods)
&& requiredMods.SequenceEqual(other.requiredMods);
&& AllowedMods.SequenceEqual(other.AllowedMods)
&& RequiredMods.SequenceEqual(other.RequiredMods);
}
}

View File

@ -1,141 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osu.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Testing;
using osu.Framework.Threading;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Overlays
{
public abstract class ExpandingButtonContainer : Container, IStateful<ExpandedState>
{
private readonly float contractedWidth;
private readonly float expandedWidth;
public event Action<ExpandedState> StateChanged;
protected override Container<Drawable> Content => FillFlow;
protected FillFlowContainer FillFlow { get; }
protected ExpandingButtonContainer(float contractedWidth, float expandedWidth)
{
this.contractedWidth = contractedWidth;
this.expandedWidth = expandedWidth;
RelativeSizeAxes = Axes.Y;
Width = contractedWidth;
InternalChildren = new Drawable[]
{
new SidebarScrollContainer
{
Children = new[]
{
FillFlow = new FillFlowContainer
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
}
}
},
};
}
private ScheduledDelegate expandEvent;
private ExpandedState state;
protected override bool OnHover(HoverEvent e)
{
queueExpandIfHovering();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
expandEvent?.Cancel();
hoveredButton = null;
State = ExpandedState.Contracted;
base.OnHoverLost(e);
}
protected override bool OnMouseMove(MouseMoveEvent e)
{
queueExpandIfHovering();
return base.OnMouseMove(e);
}
private class SidebarScrollContainer : OsuScrollContainer
{
public SidebarScrollContainer()
{
RelativeSizeAxes = Axes.Both;
ScrollbarVisible = false;
}
}
public ExpandedState State
{
get => state;
set
{
expandEvent?.Cancel();
if (state == value) return;
state = value;
switch (state)
{
default:
this.ResizeTo(new Vector2(contractedWidth, Height), 500, Easing.OutQuint);
break;
case ExpandedState.Expanded:
this.ResizeTo(new Vector2(expandedWidth, Height), 500, Easing.OutQuint);
break;
}
StateChanged?.Invoke(State);
}
}
private Drawable hoveredButton;
private void queueExpandIfHovering()
{
// if the same button is hovered, let the scheduled expand play out..
if (hoveredButton?.IsHovered == true)
return;
// ..otherwise check whether a new button is hovered, and if so, queue a new hover operation.
// usually we wouldn't use ChildrenOfType in implementations, but this is the simplest way
// to handle cases like the editor where the buttons may be nested within a child hierarchy.
hoveredButton = FillFlow.ChildrenOfType<OsuButton>().FirstOrDefault(c => c.IsHovered);
expandEvent?.Cancel();
if (hoveredButton?.IsHovered == true && State != ExpandedState.Expanded)
expandEvent = Scheduler.AddDelayed(() => State = ExpandedState.Expanded, 750);
}
}
public enum ExpandedState
{
Contracted,
Expanded,
}
}

View File

@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Settings
{

View File

@ -265,7 +265,7 @@ namespace osu.Game.Overlays
return;
SectionsContainer.ScrollTo(section);
Sidebar.State = ExpandedState.Contracted;
Sidebar.Expanded.Value = false;
},
};
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Caching;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics;
@ -11,6 +12,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Framework.Layout;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
@ -18,7 +20,7 @@ using osuTK.Graphics;
namespace osu.Game.Overlays
{
public abstract class SettingsToolboxGroup : Container
public class SettingsToolboxGroup : Container, IExpandable
{
private const float transition_duration = 250;
private const int container_width = 270;
@ -34,30 +36,7 @@ namespace osu.Game.Overlays
private readonly FillFlowContainer content;
private readonly IconButton button;
private bool expanded = true;
public bool Expanded
{
get => expanded;
set
{
if (expanded == value) return;
expanded = value;
content.ClearTransforms();
if (expanded)
content.AutoSizeAxes = Axes.Y;
else
{
content.AutoSizeAxes = Axes.None;
content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
}
updateExpanded();
}
}
public BindableBool Expanded { get; } = new BindableBool(true);
private Color4 expandedColour;
@ -67,7 +46,7 @@ namespace osu.Game.Overlays
/// Create a new instance.
/// </summary>
/// <param name="title">The title to be displayed in the header of this group.</param>
protected SettingsToolboxGroup(string title)
public SettingsToolboxGroup(string title)
{
AutoSizeAxes = Axes.Y;
Width = container_width;
@ -115,7 +94,7 @@ namespace osu.Game.Overlays
Position = new Vector2(-15, 0),
Icon = FontAwesome.Solid.Bars,
Scale = new Vector2(0.75f),
Action = () => Expanded = !Expanded,
Action = () => Expanded.Toggle(),
},
}
},
@ -155,23 +134,58 @@ namespace osu.Game.Overlays
headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint);
}
[Resolved(canBeNull: true)]
private IExpandingContainer expandingContainer { get; set; }
private bool expandedByContainer;
protected override void LoadComplete()
{
base.LoadComplete();
this.Delay(600).FadeTo(inactive_alpha, fade_duration, Easing.OutQuint);
updateExpanded();
expandingContainer?.Expanded.BindValueChanged(containerExpanded =>
{
if (containerExpanded.NewValue && !Expanded.Value)
{
Expanded.Value = true;
expandedByContainer = true;
}
else if (!containerExpanded.NewValue && expandedByContainer)
{
Expanded.Value = false;
expandedByContainer = false;
}
updateActiveState();
}, true);
Expanded.BindValueChanged(v =>
{
content.ClearTransforms();
if (v.NewValue)
content.AutoSizeAxes = Axes.Y;
else
{
content.AutoSizeAxes = Axes.None;
content.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
}
button.FadeColour(Expanded.Value ? expandedColour : Color4.White, 200, Easing.InOutQuint);
}, true);
this.Delay(600).Schedule(updateActiveState);
}
protected override bool OnHover(HoverEvent e)
{
this.FadeIn(fade_duration, Easing.OutQuint);
updateActiveState();
return false;
}
protected override void OnHoverLost(HoverLostEvent e)
{
this.FadeTo(inactive_alpha, fade_duration, Easing.OutQuint);
updateActiveState();
base.OnHoverLost(e);
}
@ -181,7 +195,10 @@ namespace osu.Game.Overlays
expandedColour = colours.Yellow;
}
private void updateExpanded() => button.FadeColour(expanded ? expandedColour : Color4.White, 200, Easing.InOutQuint);
private void updateActiveState()
{
this.FadeTo(IsHovered || expandingContainer?.Expanded.Value == true ? 1 : inactive_alpha, fade_duration, Easing.OutQuint);
}
protected override Container<Drawable> Content => content;

View File

@ -13,7 +13,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;

View File

@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Screens.OnlinePlay.Components
@ -15,6 +16,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
private const float height = 28;
private const float transition_duration = 100;
[Resolved]
private RulesetStore rulesets { get; set; }
private Container drawableRuleset;
public ModeTypeInfo()
@ -56,11 +60,14 @@ namespace osu.Game.Screens.OnlinePlay.Components
private void updateBeatmap()
{
var item = Playlist.FirstOrDefault();
var ruleset = item == null ? null : rulesets.GetRuleset(item.RulesetID)?.CreateInstance();
if (item?.Beatmap != null)
if (item?.Beatmap != null && ruleset != null)
{
var mods = item.RequiredMods.Select(m => m.ToMod(ruleset)).ToArray();
drawableRuleset.FadeIn(transition_duration);
drawableRuleset.Child = new DifficultyIcon(item.Beatmap.Value, item.Ruleset.Value, item.RequiredMods) { Size = new Vector2(height) };
drawableRuleset.Child = new DifficultyIcon(item.Beatmap.Value, ruleset.RulesetInfo, mods) { Size = new Vector2(height) };
}
else
drawableRuleset.FadeOut(transition_duration);

View File

@ -12,7 +12,6 @@ using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
namespace osu.Game.Screens.OnlinePlay.Components
{
@ -27,9 +26,6 @@ namespace osu.Game.Screens.OnlinePlay.Components
protected IBindable<Room> JoinedRoom => joinedRoom;
private readonly Bindable<Room> joinedRoom = new Bindable<Room>();
[Resolved]
private IRulesetStore rulesets { get; set; }
[Resolved]
private IAPIProvider api { get; set; }
@ -117,7 +113,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
try
{
foreach (var pi in room.Playlist)
pi.MapObjects(rulesets);
pi.MapObjects();
var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value);
if (existing == null)

View File

@ -69,8 +69,9 @@ namespace osu.Game.Screens.OnlinePlay
private readonly DelayedLoadWrapper onScreenLoader = new DelayedLoadWrapper(Empty) { RelativeSizeAxes = Axes.Both };
private readonly IBindable<bool> valid = new Bindable<bool>();
private readonly Bindable<IBeatmapInfo> beatmap = new Bindable<IBeatmapInfo>();
private readonly Bindable<IRulesetInfo> ruleset = new Bindable<IRulesetInfo>();
private readonly BindableList<Mod> requiredMods = new BindableList<Mod>();
private IRulesetInfo ruleset;
private Mod[] requiredMods;
private Container maskingContainer;
private Container difficultyIconContainer;
@ -86,6 +87,9 @@ namespace osu.Game.Screens.OnlinePlay
private PanelBackground panelBackground;
private FillFlowContainer mainFillFlow;
[Resolved]
private RulesetStore rulesets { get; set; }
[Resolved]
private OsuColour colours { get; set; }
@ -108,8 +112,6 @@ namespace osu.Game.Screens.OnlinePlay
beatmap.BindTo(item.Beatmap);
valid.BindTo(item.Valid);
ruleset.BindTo(item.Ruleset);
requiredMods.BindTo(item.RequiredMods);
if (item.Expired)
Colour = OsuColour.Gray(0.5f);
@ -119,6 +121,11 @@ namespace osu.Game.Screens.OnlinePlay
private void load()
{
maskingContainer.BorderColour = colours.Yellow;
ruleset = rulesets.GetRuleset(Item.RulesetID);
var rulesetInstance = ruleset?.CreateInstance();
requiredMods = Item.RequiredMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
}
protected override void LoadComplete()
@ -145,9 +152,7 @@ namespace osu.Game.Screens.OnlinePlay
}, true);
beatmap.BindValueChanged(_ => Scheduler.AddOnce(refresh));
ruleset.BindValueChanged(_ => Scheduler.AddOnce(refresh));
valid.BindValueChanged(_ => Scheduler.AddOnce(refresh));
requiredMods.CollectionChanged += (_, __) => Scheduler.AddOnce(refresh);
onScreenLoader.DelayedLoadStarted += _ =>
{
@ -276,7 +281,7 @@ namespace osu.Game.Screens.OnlinePlay
}
if (Item.Beatmap.Value != null)
difficultyIconContainer.Child = new DifficultyIcon(Item.Beatmap.Value, ruleset.Value, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(icon_height) };
difficultyIconContainer.Child = new DifficultyIcon(Item.Beatmap.Value, ruleset, requiredMods, performBackgroundDifficultyLookup: false) { Size = new Vector2(icon_height) };
else
difficultyIconContainer.Clear();

View File

@ -12,7 +12,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Extensions;
using osu.Game.Graphics.Cursor;
using osu.Game.Input.Bindings;
using osu.Game.Online.Rooms;
@ -78,7 +77,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
{
bool matchingFilter = true;
matchingFilter &= r.Room.Playlist.Count == 0 || criteria.Ruleset == null || r.Room.Playlist.Any(i => i.Ruleset.Value.MatchesOnlineID(criteria.Ruleset));
matchingFilter &= r.Room.Playlist.Count == 0 || criteria.Ruleset == null || r.Room.Playlist.Any(i => i.RulesetID == criteria.Ruleset.OnlineID);
if (!string.IsNullOrEmpty(criteria.SearchString))
matchingFilter &= r.FilterTerms.Any(term => term.Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase));

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@ -350,10 +351,12 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (selected == null)
return;
var rulesetInstance = rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance();
Debug.Assert(rulesetInstance != null);
var allowedMods = SelectedItem.Value.AllowedMods.Select(m => m.ToMod(rulesetInstance));
// Remove any user mods that are no longer allowed.
UserMods.Value = UserMods.Value
.Where(m => selected.AllowedMods.Any(a => m.GetType() == a.GetType()))
.ToList();
UserMods.Value = UserMods.Value.Where(m => allowedMods.Any(a => m.GetType() == a.GetType())).ToList();
UpdateMods();
updateRuleset();
@ -367,7 +370,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
else
{
UserModsSection?.Show();
userModsSelectOverlay.IsValidMod = m => selected.AllowedMods.Any(a => a.GetType() == m.GetType());
userModsSelectOverlay.IsValidMod = m => allowedMods.Any(a => a.GetType() == m.GetType());
}
}
@ -386,7 +389,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
if (SelectedItem.Value == null || !this.IsCurrentScreen())
return;
Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList();
var rulesetInstance = rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance();
Debug.Assert(rulesetInstance != null);
Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToList();
}
private void updateRuleset()

View File

@ -11,7 +11,6 @@ using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
@ -71,8 +70,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
BeatmapID = item.BeatmapID,
BeatmapChecksum = item.Beatmap.Value.MD5Hash,
RulesetID = item.RulesetID,
RequiredMods = item.RequiredMods.Select(m => new APIMod(m)).ToArray(),
AllowedMods = item.AllowedMods.Select(m => new APIMod(m)).ToArray()
RequiredMods = item.RequiredMods.ToArray(),
AllowedMods = item.AllowedMods.ToArray()
};
Task task = itemToEdit != null ? client.EditPlaylistItem(multiplayerItem) : client.AddPlaylistItem(multiplayerItem);

View File

@ -247,7 +247,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
// update local mods based on room's reported status for the local user (omitting the base call implementation).
// this makes the server authoritative, and avoids the local user potentially setting mods that the server is not aware of (ie. if the match was started during the selection being changed).
var ruleset = Ruleset.Value.CreateInstance();
Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods).ToList();
Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods.Select(m => m.ToMod(ruleset))).ToList();
}
[Resolved(canBeNull: true)]

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
@ -37,6 +38,9 @@ namespace osu.Game.Screens.OnlinePlay
[Resolved(CanBeNull = true)]
protected IBindable<PlaylistItem> SelectedItem { get; private set; }
[Resolved]
private RulesetStore rulesets { get; set; }
protected override UserActivity InitialActivity => new UserActivity.InLobby(room);
protected readonly Bindable<IReadOnlyList<Mod>> FreeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
@ -78,10 +82,15 @@ namespace osu.Game.Screens.OnlinePlay
{
base.LoadComplete();
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
// Similarly, freeMods is currently empty but should only contain the allowed mods.
Mods.Value = SelectedItem?.Value?.RequiredMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty<Mod>();
FreeMods.Value = SelectedItem?.Value?.AllowedMods.Select(m => m.DeepClone()).ToArray() ?? Array.Empty<Mod>();
var rulesetInstance = SelectedItem?.Value?.RulesetID == null ? null : rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance();
if (rulesetInstance != null)
{
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
// Similarly, freeMods is currently empty but should only contain the allowed mods.
Mods.Value = SelectedItem.Value.RequiredMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
FreeMods.Value = SelectedItem.Value.AllowedMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
}
Mods.BindValueChanged(onModsChanged);
Ruleset.BindValueChanged(onRulesetChanged);
@ -110,15 +119,11 @@ namespace osu.Game.Screens.OnlinePlay
{
Value = Beatmap.Value.BeatmapInfo
},
Ruleset =
{
Value = Ruleset.Value
}
RulesetID = Ruleset.Value.OnlineID,
RequiredMods = Mods.Value.Select(m => new APIMod(m)).ToArray(),
AllowedMods = FreeMods.Value.Select(m => new APIMod(m)).ToArray()
};
item.RequiredMods.AddRange(Mods.Value.Select(m => m.DeepClone()));
item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.DeepClone()));
SelectItem(item);
return true;
}

View File

@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Game.Extensions;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Scoring;
@ -36,10 +37,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
if (!Beatmap.Value.BeatmapInfo.MatchesOnlineID(PlaylistItem.Beatmap.Value))
throw new InvalidOperationException("Current Beatmap does not match PlaylistItem's Beatmap");
if (!ruleset.Value.MatchesOnlineID(PlaylistItem.Ruleset.Value))
if (ruleset.Value.OnlineID != PlaylistItem.RulesetID)
throw new InvalidOperationException("Current Ruleset does not match PlaylistItem's Ruleset");
if (!PlaylistItem.RequiredMods.All(m => Mods.Value.Any(m.Equals)))
var localMods = Mods.Value.Select(m => new APIMod(m)).ToArray();
if (!PlaylistItem.RequiredMods.All(m => localMods.Any(m.Equals)))
throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods");
}

View File

@ -3,6 +3,7 @@
using System.Linq;
using osu.Framework.Screens;
using osu.Game.Online.API;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.Select;
@ -30,7 +31,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
break;
case 1:
populateItemFromCurrent(Playlist.Single());
Playlist.Clear();
createNewItem();
break;
}
@ -41,24 +43,14 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
{
PlaylistItem item = new PlaylistItem
{
ID = Playlist.Count == 0 ? 0 : Playlist.Max(p => p.ID) + 1
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()
};
populateItemFromCurrent(item);
Playlist.Add(item);
}
private void populateItemFromCurrent(PlaylistItem item)
{
item.Beatmap.Value = Beatmap.Value.BeatmapInfo;
item.Ruleset.Value = Ruleset.Value;
item.RequiredMods.Clear();
item.RequiredMods.AddRange(Mods.Value.Select(m => m.DeepClone()));
item.AllowedMods.Clear();
item.AllowedMods.AddRange(FreeMods.Value.Select(m => m.DeepClone()));
}
}
}

View File

@ -40,7 +40,7 @@ namespace osu.Game.Screens.Play.HUD
//CollectionSettings = new CollectionSettings(),
//DiscussionSettings = new DiscussionSettings(),
PlaybackSettings = new PlaybackSettings(),
VisualSettings = new VisualSettings { Expanded = false }
VisualSettings = new VisualSettings { Expanded = { Value = false } }
}
};
}

View File

@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
Ruleset = { Value = Ruleset.Value }
RulesetID = Ruleset.Value.OnlineID
}
}
};

View File

@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
{
room.Playlist.Add(new PlaylistItem
{
Ruleset = { Value = ruleset },
RulesetID = ruleset.OnlineID,
Beatmap =
{
Value = new BeatmapInfo