From ea4dce845491503da0fd86a3c1b59a4a9c3d966e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Dec 2018 21:08:14 +0900 Subject: [PATCH 1/8] Add a polling component model --- .../Visual/TestCasePollingComponent.cs | 98 ++++++++++++++++ osu.Game/Online/Chat/ChannelManager.cs | 93 +++++++-------- osu.Game/Online/PollingComponent.cs | 108 ++++++++++++++++++ osu.Game/OsuGame.cs | 2 + 4 files changed, 249 insertions(+), 52 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestCasePollingComponent.cs create mode 100644 osu.Game/Online/PollingComponent.cs diff --git a/osu.Game.Tests/Visual/TestCasePollingComponent.cs b/osu.Game.Tests/Visual/TestCasePollingComponent.cs new file mode 100644 index 0000000000..928c92cb2b --- /dev/null +++ b/osu.Game.Tests/Visual/TestCasePollingComponent.cs @@ -0,0 +1,98 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; +using osu.Game.Online; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCasePollingComponent : OsuTestCase + { + private Container pollBox; + private TestPoller poller; + + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + poller = new TestPoller(), + pollBox = new Container + { + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.4f), + Colour = Color4.LimeGreen, + RelativeSizeAxes = Axes.Both, + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Poll!", + } + } + } + }; + + int count = 0; + + poller.OnPoll += () => + { + pollBox.FadeOutFromOne(500); + count++; + }; + + AddStep("set poll to 1 second", () => poller.TimeBetweenPolls = TimePerAction); + + void checkCount(int checkValue) => AddAssert($"count is {checkValue}", () => count == checkValue); + + checkCount(1); + checkCount(2); + checkCount(3); + + AddStep("set poll to 5 second", () => poller.TimeBetweenPolls = TimePerAction * 5); + + checkCount(4); + checkCount(4); + checkCount(4); + checkCount(4); + + checkCount(5); + checkCount(5); + checkCount(5); + + AddStep("set poll to 5 second", () => poller.TimeBetweenPolls = TimePerAction); + + AddAssert("count is 6", () => count == 6); + + } + + protected override double TimePerAction => 500; + + public class TestPoller : PollingComponent + { + public event Action OnPoll; + + protected override Task Poll() + { + OnPoll?.Invoke(); + return base.Poll(); + } + } + } +} diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 863ad3042f..a63af0f7a3 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -4,11 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Graphics; using osu.Framework.Logging; -using osu.Framework.Threading; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Users; @@ -18,7 +17,7 @@ namespace osu.Game.Online.Chat /// /// Manages everything channel related /// - public class ChannelManager : Component, IOnlineComponent + public class ChannelManager : PollingComponent { /// /// The channels the player joins on startup @@ -49,11 +48,14 @@ namespace osu.Game.Online.Chat public IBindableCollection AvailableChannels => availableChannels; private IAPIProvider api; - private ScheduledDelegate fetchMessagesScheduleder; + + public readonly BindableBool HighPollRate = new BindableBool(); public ChannelManager() { CurrentChannel.ValueChanged += currentChannelChanged; + + HighPollRate.BindValueChanged(high => TimeBetweenPolls = high ? 1000 : 6000, true); } /// @@ -360,73 +362,60 @@ namespace osu.Game.Online.Chat } } - public void APIStateChanged(APIAccess api, APIState state) - { - switch (state) - { - case APIState.Online: - fetchUpdates(); - break; - default: - fetchMessagesScheduleder?.Cancel(); - fetchMessagesScheduleder = null; - break; - } - } - private long lastMessageId; - private const int update_poll_interval = 1000; private bool channelsInitialised; - private void fetchUpdates() + protected override Task Poll() { - fetchMessagesScheduleder?.Cancel(); - fetchMessagesScheduleder = Scheduler.AddDelayed(() => + if (!api.IsLoggedIn) + return base.Poll(); + + var fetchReq = new GetUpdatesRequest(lastMessageId); + + var tcs = new TaskCompletionSource(); + + fetchReq.Success += updates => { - var fetchReq = new GetUpdatesRequest(lastMessageId); - - fetchReq.Success += updates => + if (updates?.Presence != null) { - if (updates?.Presence != null) + foreach (var channel in updates.Presence) { - foreach (var channel in updates.Presence) - { - // we received this from the server so should mark the channel already joined. - JoinChannel(channel, true); - } - - //todo: handle left channels - - handleChannelMessages(updates.Messages); - - foreach (var group in updates.Messages.GroupBy(m => m.ChannelId)) - JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); - - lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId; + // we received this from the server so should mark the channel already joined. + JoinChannel(channel, true); } - if (!channelsInitialised) - { - channelsInitialised = true; - // we want this to run after the first presence so we can see if the user is in any channels already. - initializeChannels(); - } + //todo: handle left channels - fetchUpdates(); - }; + handleChannelMessages(updates.Messages); - fetchReq.Failure += delegate { fetchUpdates(); }; + foreach (var group in updates.Messages.GroupBy(m => m.ChannelId)) + JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); - api.Queue(fetchReq); - }, update_poll_interval); + lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId; + } + + if (!channelsInitialised) + { + channelsInitialised = true; + // we want this to run after the first presence so we can see if the user is in any channels already. + initializeChannels(); + } + + tcs.SetResult(true); + }; + + fetchReq.Failure += _ => tcs.SetResult(false); + + api.Queue(fetchReq); + + return tcs.Task; } [BackgroundDependencyLoader] private void load(IAPIProvider api) { this.api = api; - api.Register(this); } } diff --git a/osu.Game/Online/PollingComponent.cs b/osu.Game/Online/PollingComponent.cs new file mode 100644 index 0000000000..d9dcfc40c2 --- /dev/null +++ b/osu.Game/Online/PollingComponent.cs @@ -0,0 +1,108 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Threading.Tasks; +using osu.Framework.Graphics; +using osu.Framework.Threading; + +namespace osu.Game.Online +{ + /// + /// A component which requires a constant polling process. + /// + public abstract class PollingComponent : Component + { + private double? lastTimePolled; + + private ScheduledDelegate scheduledPoll; + + private bool pollingActive; + + private double timeBetweenPolls; + + /// + /// The time that should be waited between polls. + /// + public double TimeBetweenPolls + { + get => timeBetweenPolls; + set + { + timeBetweenPolls = value; + scheduledPoll?.Cancel(); + pollIfNecessary(); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + pollIfNecessary(); + } + + private bool pollIfNecessary() + { + // we must be loaded so we have access to clock. + if (!IsLoaded) return false; + + // there's already a poll process running. + if (pollingActive) return false; + + // don't try polling if the time between polls hasn't been set. + if (timeBetweenPolls == 0) return false; + + if (!lastTimePolled.HasValue) + { + doPoll(); + return true; + } + + if (Time.Current - lastTimePolled.Value > timeBetweenPolls) + { + doPoll(); + return true; + } + + // not ennough time has passed since the last poll. we do want to schedule a poll to happen, though. + scheduleNextPoll(); + return false; + } + + private void doPoll() + { + scheduledPoll = null; + pollingActive = true; + Poll().ContinueWith(_ => pollComplete()); + } + + /// + /// Perform the polling in this method. Call when done. + /// + protected virtual Task Poll() + { + return Task.CompletedTask; + } + + /// + /// Call when a poll operation has completed. + /// + private void pollComplete() + { + lastTimePolled = Time.Current; + pollingActive = false; + + if (scheduledPoll == null) + scheduleNextPoll(); + } + + private void scheduleNextPoll() + { + scheduledPoll?.Cancel(); + + double lastPollDuration = lastTimePolled.HasValue ? Time.Current - lastTimePolled.Value : 0; + + scheduledPoll = Scheduler.AddDelayed(doPoll, Math.Max(0, timeBetweenPolls - lastPollDuration)); + } + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 73ecbafb9e..31a00e68ac 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -418,6 +418,8 @@ namespace osu.Game dependencies.Cache(notifications); dependencies.Cache(dialogOverlay); + chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible; + Add(externalLinkOpener = new ExternalLinkOpener()); var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; From 7dc1f5b771fa1f5c9bd88ab53a3162da7278029e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Dec 2018 20:12:30 +0900 Subject: [PATCH 2/8] Add more tests --- .../Visual/TestCasePollingComponent.cs | 110 +++++++++++++----- 1 file changed, 78 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePollingComponent.cs b/osu.Game.Tests/Visual/TestCasePollingComponent.cs index 928c92cb2b..bf129bfd53 100644 --- a/osu.Game.Tests/Visual/TestCasePollingComponent.cs +++ b/osu.Game.Tests/Visual/TestCasePollingComponent.cs @@ -3,10 +3,11 @@ using System; using System.Threading.Tasks; -using osu.Framework.Allocation; +using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; using osu.Game.Graphics.Sprites; using osu.Game.Online; using osuTK; @@ -19,12 +20,16 @@ namespace osu.Game.Tests.Visual private Container pollBox; private TestPoller poller; - [BackgroundDependencyLoader] - private void load() + private const float safety_adjust = 1f; + private int count; + + [SetUp] + public void SetUp() => Schedule(() => { + count = 0; + Children = new Drawable[] { - poller = new TestPoller(), pollBox = new Container { Alpha = 0, @@ -48,41 +53,77 @@ namespace osu.Game.Tests.Visual } } }; + }); - int count = 0; + //[Test] + public void TestInstantPolling() + { + createPoller(true); + AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust); + checkCount(1); + checkCount(2); + checkCount(3); + + AddStep("set poll interval to 5", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5); + checkCount(4); + checkCount(4); + checkCount(4); + + skip(); + + checkCount(5); + checkCount(5); + + AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust); + checkCount(6); + checkCount(7); + } + + [Test] + public void TestSlowPolling() + { + createPoller(false); + + AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5); + checkCount(0); + skip(); + checkCount(0); + skip(); + skip(); + skip(); + skip(); + checkCount(0); + skip(); + skip(); + checkCount(0); + } + + private void skip() => AddStep("skip", () => + { + // could be 4 or 5 at this point due to timing discrepancies (safety_adjust @ 0.2 * 5 ~= 1) + // easiest to just ignore the value at this point and move on. + }); + + private void checkCount(int checkValue) + { + Logger.Log($"value is {count}"); + AddAssert($"count is {checkValue}", () => count == checkValue); + } + + private void createPoller(bool instant) => AddStep("create poller", () => + { + poller?.Expire(); + + Add(poller = instant ? new TestPoller() : new TestSlowPoller()); poller.OnPoll += () => { pollBox.FadeOutFromOne(500); count++; }; + }); - AddStep("set poll to 1 second", () => poller.TimeBetweenPolls = TimePerAction); - - void checkCount(int checkValue) => AddAssert($"count is {checkValue}", () => count == checkValue); - - checkCount(1); - checkCount(2); - checkCount(3); - - AddStep("set poll to 5 second", () => poller.TimeBetweenPolls = TimePerAction * 5); - - checkCount(4); - checkCount(4); - checkCount(4); - checkCount(4); - - checkCount(5); - checkCount(5); - checkCount(5); - - AddStep("set poll to 5 second", () => poller.TimeBetweenPolls = TimePerAction); - - AddAssert("count is 6", () => count == 6); - - } - - protected override double TimePerAction => 500; + protected override double TimePerAction => 5000; public class TestPoller : PollingComponent { @@ -90,9 +131,14 @@ namespace osu.Game.Tests.Visual protected override Task Poll() { - OnPoll?.Invoke(); + Schedule(() => OnPoll?.Invoke()); return base.Poll(); } } + + public class TestSlowPoller : TestPoller + { + protected override Task Poll() => Task.Delay((int)(TimeBetweenPolls / 2f / Clock.Rate)).ContinueWith(_ => base.Poll()); + } } } From 439d741dee62f95c67547afe116c858d1c28e484 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Dec 2018 16:06:56 +0900 Subject: [PATCH 3/8] Implement basic api structure for rooms --- osu.Game/Online/Multiplayer/Room.cs | 92 ++++++++++++++++++++++++++++- osu.Game/Rulesets/Mods/IMod.cs | 3 + 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 67ddb60823..71668e4c4b 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -3,16 +3,28 @@ using System.Collections.Generic; using System.Linq; +using Newtonsoft.Json; using osu.Framework.Configuration; using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Users; namespace osu.Game.Online.Multiplayer { public class Room { - public Bindable Name = new Bindable("My awesome room!"); - public Bindable Host = new Bindable(); + public Bindable RoomID { get; } = new Bindable(); + + [JsonProperty("name")] + public readonly Bindable Name = new Bindable("My awesome room!"); + + [JsonProperty("host")] + public readonly Bindable Host = new Bindable(); + + [JsonProperty("playlist")] + public readonly BindableCollection Playlist = new BindableCollection(); + public Bindable Status = new Bindable(new RoomStatusOpen()); public Bindable Availability = new Bindable(); public Bindable Type = new Bindable(new GameTypeTimeshift()); @@ -20,6 +32,82 @@ namespace osu.Game.Online.Multiplayer public Bindable MaxParticipants = new Bindable(); public Bindable> Participants = new Bindable>(Enumerable.Empty()); + public void CopyFrom(Room other) + { + Name.Value = other.Name; + Host.Value = other.Host; + Status.Value = other.Status; + Availability.Value = other.Availability; + Type.Value = other.Type; + Beatmap.Value = other.Beatmap; + MaxParticipants.Value = other.MaxParticipants; + Participants.Value = other.Participants.Value.ToArray(); + } + public Bindable Created = new Bindable(); } + + public class PlaylistItem + { + [JsonProperty("beatmap")] + public BeatmapInfo Beatmap; + + [JsonProperty("ruleset_id")] + public int RulesetID { get; set; } + + public readonly BindableCollection AllowedMods = new BindableCollection(); + + public readonly BindableCollection RequiredMods = new BindableCollection(); + + private APIMod[] _allowedMods; + + [JsonProperty("allowed_mods")] + private APIMod[] allowedMods + { + get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray(); + set => _allowedMods = value; + } + + private APIMod[] _requiredMods; + + [JsonProperty("required_mods")] + private APIMod[] requiredMods + { + get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray(); + set => _requiredMods = value; + } + + private RulesetInfo ruleset; + + public RulesetInfo Ruleset + { + get => ruleset; + set + { + ruleset = value; + + if (_allowedMods != null) + { + AllowedMods.Clear(); + AllowedMods.AddRange(value.CreateInstance().GetAllMods().Where(mod => _allowedMods.Any(m => m.Acronym == mod.Acronym))); + + _allowedMods = null; + } + + if (_requiredMods != null) + { + RequiredMods.Clear(); + RequiredMods.AddRange(value.CreateInstance().GetAllMods().Where(mod => _requiredMods.Any(m => m.Acronym == mod.Acronym))); + + _requiredMods = null; + } + } + } + + // Todo: Move this elsewhere for reusability + private class APIMod : IMod + { + public string Acronym { get; set; } + } + } } diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index d0c4ce2f4c..4a95ce8111 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using Newtonsoft.Json; + namespace osu.Game.Rulesets.Mods { public interface IMod @@ -8,6 +10,7 @@ namespace osu.Game.Rulesets.Mods /// /// The shortened name of this mod. /// + [JsonProperty] string Acronym { get; } } } From 6123a11b67b6425606d45889807f9fdecdf83c1c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Dec 2018 16:20:11 +0900 Subject: [PATCH 4/8] Use RoomID for creation --- osu.Game/Online/Multiplayer/Room.cs | 5 ++--- osu.Game/Screens/Multi/Match/Components/Header.cs | 6 +++--- .../Screens/Multi/Match/Components/MatchTabControl.cs | 8 ++++---- osu.Game/Screens/Multi/RoomManager.cs | 1 - 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 71668e4c4b..6dae9d2d74 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -14,7 +14,7 @@ namespace osu.Game.Online.Multiplayer { public class Room { - public Bindable RoomID { get; } = new Bindable(); + public Bindable RoomID { get; } = new Bindable(); [JsonProperty("name")] public readonly Bindable Name = new Bindable("My awesome room!"); @@ -34,6 +34,7 @@ namespace osu.Game.Online.Multiplayer public void CopyFrom(Room other) { + RoomID.Value = other.RoomID; Name.Value = other.Name; Host.Value = other.Host; Status.Value = other.Status; @@ -43,8 +44,6 @@ namespace osu.Game.Online.Multiplayer MaxParticipants.Value = other.MaxParticipants; Participants.Value = other.Participants.Value.ToArray(); } - - public Bindable Created = new Bindable(); } public class PlaylistItem diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 9c72403806..b5ae3baa15 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Multi.Match.Components private class BeatmapSelectButton : TriangleButton { - private readonly IBindable createdBind = new Bindable(); + private readonly IBindable roomIDBind = new Bindable(); [Resolved] private Room room { get; set; } @@ -113,8 +113,8 @@ namespace osu.Game.Screens.Multi.Match.Components [BackgroundDependencyLoader] private void load() { - createdBind.BindTo(room.Created); - createdBind.BindValueChanged(v => Enabled.Value = !v, true); + roomIDBind.BindTo(room.RoomID); + roomIDBind.BindValueChanged(v => Enabled.Value = !v.HasValue, true); } } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs b/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs index 51698fa50f..fa760c97e3 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Multi.Match.Components { public class MatchTabControl : PageTabControl { - private readonly IBindable created = new Bindable(); + private readonly IBindable roomIdBind = new Bindable(); [Resolved] private Room room { get; set; } @@ -28,10 +28,10 @@ namespace osu.Game.Screens.Multi.Match.Components [BackgroundDependencyLoader] private void load() { - created.BindTo(room.Created); - created.BindValueChanged(v => + roomIdBind.BindTo(room.RoomID); + roomIdBind.BindValueChanged(v => { - if (v) + if (v.HasValue) { Items.ForEach(t => t.Enabled.Value = !(t is SettingsMatchPage)); Current.Value = new RoomMatchPage(); diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index 9e233f278b..e2d132b8bb 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -25,7 +25,6 @@ namespace osu.Game.Screens.Multi // Todo: Perform API request - room.Created.Value = true; rooms.Add(room); } } From 87ebb00f1c5787a0a1629eac9b0a5c8f48d4a035 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Dec 2018 19:03:27 +0900 Subject: [PATCH 5/8] Make Target abstract (should absolutely always be set) --- osu.Game/Online/API/APIRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index adbedb2aac..baa5ab3267 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -40,7 +40,7 @@ namespace osu.Game.Online.API /// public int Timeout = WebRequest.DEFAULT_TIMEOUT; - protected virtual string Target => string.Empty; + protected abstract string Target { get; } protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri); From 450e4cd2235a6250245f9336f9c22ec16eca7982 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Dec 2018 19:04:11 +0900 Subject: [PATCH 6/8] Hook up API --- osu.Game.Tests/Visual/TestCaseDrawableRoom.cs | 135 ------------ osu.Game.Tests/Visual/TestCaseLounge.cs | 208 ------------------ osu.Game.Tests/Visual/TestCaseMatch.cs | 152 ------------- .../Visual/TestCaseRoomInspector.cs | 141 ------------ osu.Game/Online/Multiplayer/Room.cs | 45 +++- osu.Game/Overlays/Music/PlaylistItem.cs | 1 + osu.Game/Screens/Multi/RoomManager.cs | 105 ++++++++- 7 files changed, 144 insertions(+), 643 deletions(-) delete mode 100644 osu.Game.Tests/Visual/TestCaseDrawableRoom.cs delete mode 100644 osu.Game.Tests/Visual/TestCaseLounge.cs delete mode 100644 osu.Game.Tests/Visual/TestCaseMatch.cs delete mode 100644 osu.Game.Tests/Visual/TestCaseRoomInspector.cs diff --git a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs b/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs deleted file mode 100644 index bcebf0a8d9..0000000000 --- a/osu.Game.Tests/Visual/TestCaseDrawableRoom.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseDrawableRoom : OsuTestCase - { - private RulesetStore rulesets; - - protected override void LoadComplete() - { - base.LoadComplete(); - - DrawableRoom first; - Add(new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Y, - Width = 580f, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - first = new DrawableRoom(new Room - { - Name = { Value = @"Great Room Right Here" }, - Host = { Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } } }, - Status = { Value = new RoomStatusOpen() }, - Type = { Value = new GameTypeTeamVersus() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 4.65, - Ruleset = rulesets.GetRuleset(3), - Metadata = new BeatmapMetadata - { - Title = @"Critical Crystal", - Artist = @"Seiryu", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh//beatmaps/376340/covers/cover.jpg?1456478455", - }, - }, - }, - }, - }, - Participants = - { - Value = new[] - { - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1355 } } }, - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 8756 } } }, - }, - }, - }), - new DrawableRoom(new Room - { - Name = { Value = @"Relax It's The Weekend" }, - Host = { Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } } }, - Status = { Value = new RoomStatusPlaying() }, - Type = { Value = new GameTypeTagTeam() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 1.96, - Ruleset = rulesets.GetRuleset(0), - Metadata = new BeatmapMetadata - { - Title = @"Serendipity", - Artist = @"ZAQ", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh//beatmaps/526839/covers/cover.jpg?1493815706", - }, - }, - }, - }, - }, - Participants = - { - Value = new[] - { - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 578975 } } }, - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24554 } } }, - }, - }, - }), - } - }); - - AddStep(@"select", () => first.State = SelectionState.Selected); - AddStep(@"change title", () => first.Room.Name.Value = @"I Changed Name"); - AddStep(@"change host", () => first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } }); - AddStep(@"change status", () => first.Room.Status.Value = new RoomStatusPlaying()); - AddStep(@"change type", () => first.Room.Type.Value = new GameTypeVersus()); - AddStep(@"change beatmap", () => first.Room.Beatmap.Value = null); - AddStep(@"change participants", () => first.Room.Participants.Value = new[] - { - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 1254 } } }, - new User { Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 123189 } } }, - }); - AddStep(@"deselect", () => first.State = SelectionState.NotSelected); - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - } -} diff --git a/osu.Game.Tests/Visual/TestCaseLounge.cs b/osu.Game.Tests/Visual/TestCaseLounge.cs deleted file mode 100644 index c1253e4f5c..0000000000 --- a/osu.Game.Tests/Visual/TestCaseLounge.cs +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Screens; -using osu.Game.Screens.Backgrounds; -using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Lounge; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Users; -using osuTK.Input; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseLounge : ManualInputManagerTestCase - { - private TestLoungeScreen loungeScreen; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - loungeScreen = new TestLoungeScreen(); - - Room[] rooms = - { - new Room - { - Name = { Value = @"Just Another Room" }, - Host = { Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } } }, - Status = { Value = new RoomStatusPlaying() }, - Availability = { Value = RoomAvailability.Public }, - Type = { Value = new GameTypeTagTeam() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 5.65, - Ruleset = rulesets.GetRuleset(0), - Metadata = new BeatmapMetadata - { - Title = @"Sidetracked Day (Short Ver.)", - Artist = @"VINXIS", - AuthorString = @"Hobbes2", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/767600/covers/cover.jpg?1526243446", - }, - }, - }, - } - }, - MaxParticipants = { Value = 10 }, - Participants = - { - Value = new[] - { - new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } }, - new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } }, - new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } }, - new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } }, - new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } }, - new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } }, - } - } - }, - new Room - { - Name = { Value = @"Not Just Any Room" }, - Host = { Value = new User { Username = @"Monstrata", Id = 2706438, Country = new Country { FlagName = @"CA" } } }, - Status = { Value = new RoomStatusOpen() }, - Availability = { Value = RoomAvailability.FriendsOnly }, - Type = { Value = new GameTypeTeamVersus() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 2.73, - Ruleset = rulesets.GetRuleset(0), - Metadata = new BeatmapMetadata - { - Title = @"lit(var)", - Artist = @"kensuke ushio", - AuthorString = @"Monstrata", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/623972/covers/cover.jpg?1521167183", - }, - }, - }, - } - }, - Participants = - { - Value = new[] - { - new User { Username = @"Jeby", Id = 3136279, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 3497 } } }, - new User { Username = @"DualAkira", Id = 5220933, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 643 } } }, - new User { Username = @"Datenshi Yohane", Id = 7171857, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 10555 } } }, - } - } - }, - new Room - { - Name = { Value = @"room THE FINAL" }, - Host = { Value = new User { Username = @"Delis", Id = 1603923, Country = new Country { FlagName = @"JP" } } }, - Status = { Value = new RoomStatusPlaying() }, - Availability = { Value = RoomAvailability.Public }, - Type = { Value = new GameTypeTagTeam() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 4.48, - Ruleset = rulesets.GetRuleset(3), - Metadata = new BeatmapMetadata - { - Title = @"ONIGIRI FREEWAY", - Artist = @"OISHII", - AuthorString = @"Mentholzzz", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/663098/covers/cover.jpg?1521898837", - }, - }, - }, - } - }, - MaxParticipants = { Value = 30 }, - Participants = - { - Value = new[] - { - new User { Username = @"KizuA", Id = 6510442, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 5372 } } }, - new User { Username = @"Colored", Id = 827563, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 810 } } }, - new User { Username = @"Beryl", Id = 3817591, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 10096 } } }, - } - } - }, - }; - - AddStep(@"show", () => Add(loungeScreen)); - selectAssert(0); - AddAssert(@"no room selected", () => loungeScreen.SelectedRoom == null); - selectAssert(1); - AddStep(@"open room 1", () => clickRoom(1)); - AddUntilStep(() => loungeScreen.ChildScreen?.IsCurrentScreen == true, "wait until room current"); - AddStep(@"make lounge current", loungeScreen.MakeCurrent); - filterAssert(@"THE FINAL", LoungeTab.Public, 1); - filterAssert(string.Empty, LoungeTab.Public, 2); - filterAssert(string.Empty, LoungeTab.Private, 1); - filterAssert(string.Empty, LoungeTab.Public, 2); - filterAssert(@"no matches", LoungeTab.Public, 0); - filterAssert(string.Empty, LoungeTab.Public, 2); - AddStep(@"exit", loungeScreen.Exit); - } - - private void clickRoom(int n) - { - InputManager.Click(MouseButton.Left); - } - - private void selectAssert(int n) - { - AddStep($@"select room {n}", () => clickRoom(n)); - } - - private void filterAssert(string filter, LoungeTab tab, int endCount) - { - AddStep($@"filter '{filter}', {tab}", () => loungeScreen.SetFilter(filter, tab)); - } - - private class TestLoungeScreen : LoungeScreen - { - protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); - - [Resolved] - private RoomManager manager { get; set; } - - public Room SelectedRoom => manager.Current.Value; - - public void SetFilter(string filter, LoungeTab tab) - { - Filter.Search.Current.Value = filter; - Filter.Tabs.Current.Value = tab; - } - } - } -} diff --git a/osu.Game.Tests/Visual/TestCaseMatch.cs b/osu.Game.Tests/Visual/TestCaseMatch.cs deleted file mode 100644 index fd4806ac90..0000000000 --- a/osu.Game.Tests/Visual/TestCaseMatch.cs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.Collections.Generic; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Screens.Multi.Match; -using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseMatch : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] - { - typeof(TestCaseMatch), - typeof(GameTypePicker), - typeof(RoomSettingsOverlay) - }; - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - Room room = new Room - { - Name = { Value = @"One Awesome Room" }, - Status = { Value = new RoomStatusOpen() }, - Availability = { Value = RoomAvailability.Public }, - Type = { Value = new GameTypeTeamVersus() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 5.02, - Ruleset = rulesets.GetRuleset(1), - Metadata = new BeatmapMetadata - { - Title = @"Paradigm Shift", - Artist = @"Morimori Atsushi", - AuthorString = @"eiri-", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/765055/covers/cover.jpg?1526955337", - }, - }, - }, - }, - }, - MaxParticipants = { Value = 5 }, - Participants = - { - Value = new[] - { - new User - { - Username = @"eiri-", - Id = 3388410, - Country = new Country { FlagName = @"US" }, - CoverUrl = @"https://assets.ppy.sh/user-profile-covers/3388410/00a8486a247831e1cc4375db519f611ac970bda8bc0057d78b0f540ea38c3e58.jpeg", - IsSupporter = true, - }, - new User - { - Username = @"Nepuri", - Id = 6637817, - Country = new Country { FlagName = @"DE" }, - CoverUrl = @"https://assets.ppy.sh/user-profile-covers/6637817/9085fc60248b6b5327a72c1dcdecf2dbedba810ae0ab6bcf7224e46b1339632a.jpeg", - IsSupporter = true, - }, - new User - { - Username = @"goheegy", - Id = 8057655, - Country = new Country { FlagName = @"GB" }, - CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8057655/21cec27c25a11dc197a4ec6a74253dbabb495949b0e0697113352f12007018c5.jpeg", - }, - new User - { - Username = @"Alumetri", - Id = 5371497, - Country = new Country { FlagName = @"RU" }, - CoverUrl = @"https://assets.ppy.sh/user-profile-covers/5371497/e023b8c7fbe3613e64bd4856703517ea50fbed8a5805dc9acda9efe9897c67e2.jpeg", - }, - } - }, - }; - - MatchScreen matchScreen = new MatchScreen(room); - - AddStep(@"show", () => Add(matchScreen)); - AddStep(@"null beatmap", () => room.Beatmap.Value = null); - AddStep(@"change name", () => room.Name.Value = @"Two Awesome Rooms"); - AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying()); - AddStep(@"change availability", () => room.Availability.Value = RoomAvailability.FriendsOnly); - AddStep(@"change type", () => room.Type.Value = new GameTypeTag()); - AddStep(@"change beatmap", () => room.Beatmap.Value = new BeatmapInfo - { - StarDifficulty = 4.33, - Ruleset = rulesets.GetRuleset(2), - Metadata = new BeatmapMetadata - { - Title = @"Yasashisa no Riyuu", - Artist = @"ChouCho", - AuthorString = @"celerih", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/685391/covers/cover.jpg?1524597970", - }, - }, - }, - }); - - AddStep(@"null max participants", () => room.MaxParticipants.Value = null); - AddStep(@"change participants", () => room.Participants.Value = new[] - { - new User - { - Username = @"Spectator", - Id = 702598, - Country = new Country { FlagName = @"KR" }, - CoverUrl = @"https://assets.ppy.sh/user-profile-covers/702598/3bbf4cb8b8d2cf8b03145000a975ff27e191ab99b0920832e7dd67386280e288.jpeg", - IsSupporter = true, - }, - new User - { - Username = @"celerih", - Id = 4696296, - Country = new Country { FlagName = @"CA" }, - CoverUrl = @"https://assets.ppy.sh/user-profile-covers/4696296/7f8500731d0ac66d5472569d146a7be07d9460273361913f22c038867baddaef.jpeg", - }, - }); - - AddStep(@"exit", matchScreen.Exit); - } - } -} diff --git a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs b/osu.Game.Tests/Visual/TestCaseRoomInspector.cs deleted file mode 100644 index 9f6eea68e7..0000000000 --- a/osu.Game.Tests/Visual/TestCaseRoomInspector.cs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Screens.Multi.Lounge.Components; -using osu.Game.Users; - -namespace osu.Game.Tests.Visual -{ - [TestFixture] - public class TestCaseRoomInspector : OsuTestCase - { - private RulesetStore rulesets; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Room room = new Room - { - Name = { Value = @"My Awesome Room" }, - Host = { Value = new User { Username = @"flyte", Id = 3103765, Country = new Country { FlagName = @"JP" } } }, - Status = { Value = new RoomStatusOpen() }, - Type = { Value = new GameTypeTeamVersus() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 3.7, - Ruleset = rulesets.GetRuleset(3), - Metadata = new BeatmapMetadata - { - Title = @"Platina", - Artist = @"Maaya Sakamoto", - AuthorString = @"uwutm8", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/560573/covers/cover.jpg?1492722343", - }, - }, - }, - } - }, - MaxParticipants = { Value = 200 }, - Participants = - { - Value = new[] - { - new User { Username = @"flyte", Id = 3103765, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 142 } } }, - new User { Username = @"Cookiezi", Id = 124493, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 546 } } }, - new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 287 } } }, - new User { Username = @"Rafis", Id = 2558286, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 468 } } }, - new User { Username = @"hvick225", Id = 50265, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 325 } } }, - new User { Username = @"peppy", Id = 2, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 625 } } }, - } - } - }; - - Add(new RoomInspector - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Width = 0.5f, - }); - - AddStep(@"change title", () => room.Name.Value = @"A Better Room Than The Above"); - AddStep(@"change host", () => room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } }); - AddStep(@"change status", () => room.Status.Value = new RoomStatusPlaying()); - AddStep(@"change type", () => room.Type.Value = new GameTypeTag()); - AddStep(@"change beatmap", () => room.Beatmap.Value = null); - AddStep(@"change max participants", () => room.MaxParticipants.Value = null); - AddStep(@"change participants", () => room.Participants.Value = new[] - { - new User { Username = @"filsdelama", Id = 2831793, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 854 } } }, - new User { Username = @"_index", Id = 652457, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 150 } } } - }); - - AddStep(@"change room", () => - { - Room newRoom = new Room - { - Name = { Value = @"My New, Better Than Ever Room" }, - Host = { Value = new User { Username = @"Angelsim", Id = 1777162, Country = new Country { FlagName = @"KR" } } }, - Status = { Value = new RoomStatusOpen() }, - Type = { Value = new GameTypeTagTeam() }, - Beatmap = - { - Value = new BeatmapInfo - { - StarDifficulty = 7.07, - Ruleset = rulesets.GetRuleset(0), - Metadata = new BeatmapMetadata - { - Title = @"FREEDOM DIVE", - Artist = @"xi", - AuthorString = @"Nakagawa-Kanon", - }, - BeatmapSet = new BeatmapSetInfo - { - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = @"https://assets.ppy.sh/beatmaps/39804/covers/cover.jpg?1456506845", - }, - }, - }, - }, - }, - MaxParticipants = { Value = 10 }, - Participants = - { - Value = new[] - { - new User { Username = @"Angelsim", Id = 1777162, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 4 } } }, - new User { Username = @"HappyStick", Id = 256802, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 752 } } }, - new User { Username = @"-Konpaku-", Id = 2258797, Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 571 } } } - } - } - }; - }); - } - - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) - { - this.rulesets = rulesets; - } - } -} diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 6dae9d2d74..56c4585b2a 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -6,6 +6,7 @@ using System.Linq; using Newtonsoft.Json; using osu.Framework.Configuration; using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Users; @@ -14,24 +15,44 @@ namespace osu.Game.Online.Multiplayer { public class Room { + [JsonProperty("id")] public Bindable RoomID { get; } = new Bindable(); + [JsonIgnore] + public readonly Bindable Beatmap = new Bindable(); + [JsonProperty("name")] public readonly Bindable Name = new Bindable("My awesome room!"); [JsonProperty("host")] public readonly Bindable Host = new Bindable(); + public bool ShouldSerializeHost() => false; + [JsonProperty("playlist")] public readonly BindableCollection Playlist = new BindableCollection(); + [JsonProperty("duration")] + public readonly Bindable Duration = new Bindable(100); + + [JsonProperty("max_attempts")] + public readonly Bindable MaxAttempts = new Bindable(null); + public Bindable Status = new Bindable(new RoomStatusOpen()); public Bindable Availability = new Bindable(); public Bindable Type = new Bindable(new GameTypeTimeshift()); - public Bindable Beatmap = new Bindable(); public Bindable MaxParticipants = new Bindable(); public Bindable> Participants = new Bindable>(Enumerable.Empty()); + public Room() + { + Beatmap.BindValueChanged(b => + { + Playlist.Clear(); + Playlist.Add(new PlaylistItem { Beatmap = b }); + }); + } + public void CopyFrom(Room other) { RoomID.Value = other.RoomID; @@ -40,22 +61,34 @@ namespace osu.Game.Online.Multiplayer Status.Value = other.Status; Availability.Value = other.Availability; Type.Value = other.Type; - Beatmap.Value = other.Beatmap; MaxParticipants.Value = other.MaxParticipants; Participants.Value = other.Participants.Value.ToArray(); + + // Temp: + Beatmap.Value = Playlist.FirstOrDefault()?.Beatmap; } } public class PlaylistItem { [JsonProperty("beatmap")] - public BeatmapInfo Beatmap; + private APIBeatmap beatmap { get; set; } + + public bool ShouldSerializebeatmap() => false; + + [JsonIgnore] + public BeatmapInfo Beatmap { get; set; } + + [JsonProperty("beatmap_id")] + public int BeatmapID => 847296; //Beatmap.OnlineBeatmapID ?? 0; [JsonProperty("ruleset_id")] public int RulesetID { get; set; } + [JsonIgnore] public readonly BindableCollection AllowedMods = new BindableCollection(); + [JsonIgnore] public readonly BindableCollection RequiredMods = new BindableCollection(); private APIMod[] _allowedMods; @@ -78,6 +111,7 @@ namespace osu.Game.Online.Multiplayer private RulesetInfo ruleset; + [JsonIgnore] public RulesetInfo Ruleset { get => ruleset; @@ -103,6 +137,11 @@ namespace osu.Game.Online.Multiplayer } } + public void SetRulesets(RulesetStore rulesets) + { + Beatmap = beatmap.ToBeatmap(rulesets); + } + // Todo: Move this elsewhere for reusability private class APIMod : IMod { diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index b4828e41f6..9368bee81c 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -8,6 +8,7 @@ using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index e2d132b8bb..fead90f9a2 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -1,15 +1,24 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Graphics; +using osu.Framework.IO.Network; +using osu.Framework.Logging; +using osu.Game.Beatmaps; +using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets; namespace osu.Game.Screens.Multi { - public class RoomManager : Component + public class RoomManager : PollingComponent { public IBindableCollection Rooms => rooms; private readonly BindableCollection rooms = new BindableCollection(); @@ -19,13 +28,101 @@ namespace osu.Game.Screens.Multi [Resolved] private APIAccess api { get; set; } + [Resolved] + private RulesetStore rulesets { get; set; } + + [Resolved] + private BeatmapManager beatmaps { get; set; } + + public RoomManager() + { + TimeBetweenPolls = 5000; + } + public void CreateRoom(Room room) { room.Host.Value = api.LocalUser; - // Todo: Perform API request + var req = new CreateRoomRequest(room); - rooms.Add(room); + req.Success += result => addRoom(room, result); + req.Failure += exception => Logger.Log($"Failed to create room: {exception}"); + api.Queue(req); + } + + protected override Task Poll() + { + if (!api.IsLoggedIn) + return base.Poll(); + + var tcs = new TaskCompletionSource(); + + var pollReq = new GetRoomsRequest(); + + pollReq.Success += result => + { + foreach (var r in result) + { + foreach (var pi in r.Playlist) + { + pi.Ruleset = rulesets.GetRuleset(pi.RulesetID); + pi.SetRulesets(rulesets); + } + + var existing = rooms.FirstOrDefault(e => e.RoomID.Value == r.RoomID.Value); + if (existing == null) + rooms.Add(r); + else + existing.CopyFrom(r); + } + + tcs.SetResult(true); + }; + + pollReq.Failure += _ => tcs.SetResult(false); + + api.Queue(pollReq); + + return tcs.Task; + } + + private void addRoom(Room local, Room remote) + { + local.CopyFrom(remote); + + var existing = rooms.FirstOrDefault(e => e.RoomID.Value == local.RoomID.Value); + if (existing != null) + rooms.Remove(existing); + rooms.Add(local); + } + + private class CreateRoomRequest : APIRequest + { + private readonly Room room; + + public CreateRoomRequest(Room room) + { + this.room = room; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + req.ContentType = "application/json"; + req.Method = HttpMethod.Post; + + req.AddRaw(JsonConvert.SerializeObject(room)); + + return req; + } + + protected override string Target => "rooms"; + } + + private class GetRoomsRequest : APIRequest> + { + protected override string Target => "rooms"; } } } From bac4f42eac5015c9b20edc22933138fcf4b95f03 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 12 Dec 2018 19:34:37 +0900 Subject: [PATCH 7/8] Fix backgrounds not quite working --- .../UpdateableBeatmapBackgroundSprite.cs | 17 ++++++++++++----- .../Online/API/Requests/Responses/APIBeatmap.cs | 6 +----- osu.Game/Online/Multiplayer/Room.cs | 3 --- .../Multi/Lounge/Components/DrawableRoom.cs | 3 +-- .../Multi/Lounge/Components/RoomInspector.cs | 2 +- .../Screens/Multi/Match/Components/Header.cs | 2 +- osu.Game/Screens/Multi/Match/MatchScreen.cs | 2 +- osu.Game/Screens/Multi/RoomManager.cs | 3 +++ 8 files changed, 20 insertions(+), 18 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 5ced21e436..0a9726b121 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -9,21 +9,28 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Beatmaps.Drawables { - public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable + public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable { - public readonly IBindable Beatmap = new Bindable(); + public readonly IBindable Beatmap = new Bindable(); [Resolved] - private OsuGameBase game { get; set; } + private BeatmapManager beatmaps { get; set; } public UpdateableBeatmapBackgroundSprite() { Beatmap.BindValueChanged(b => Schedule(() => Model = b)); } - protected override Drawable CreateDrawable(WorkingBeatmap model) + protected override Drawable CreateDrawable(BeatmapInfo model) { - Drawable drawable = model == null ? (Drawable)new DefaultSprite() : new BeatmapBackgroundSprite(model); + Drawable drawable; + + if (model == null) + drawable = new DefaultSprite(); + else if (model.BeatmapSet?.OnlineInfo != null) + drawable = new BeatmapSetCover(model.BeatmapSet); + else + drawable = new BeatmapBackgroundSprite(beatmaps.GetWorkingBeatmap(model)); drawable.RelativeSizeAxes = Axes.Both; drawable.Anchor = Anchor.Centre; diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 193ccf1f6b..b96e4bedf6 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -67,11 +67,7 @@ namespace osu.Game.Online.API.Requests.Responses OnlineBeatmapID = OnlineBeatmapID, Version = version, Status = Status, - BeatmapSet = new BeatmapSetInfo - { - OnlineBeatmapSetID = OnlineBeatmapSetID, - Status = BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None - }, + BeatmapSet = BeatmapSet.ToBeatmapSet(rulesets), BaseDifficulty = new BeatmapDifficulty { DrainRate = drainRate, diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 56c4585b2a..4af92f68ea 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -63,9 +63,6 @@ namespace osu.Game.Online.Multiplayer Type.Value = other.Type; MaxParticipants.Value = other.MaxParticipants; Participants.Value = other.Participants.Value.ToArray(); - - // Temp: - Beatmap.Value = Playlist.FirstOrDefault()?.Beatmap; } } diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index 57ce9fc0b9..647913308d 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -213,8 +213,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components d.FadeColour(s.GetAppropriateColour(colours), transition_duration); }; - background.Beatmap.BindTo(beatmap); - beatmapBind.BindValueChanged(b => beatmap.Value = beatmaps.GetWorkingBeatmap(b)); nameBind.BindValueChanged(n => name.Text = n); @@ -224,6 +222,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components typeBind.BindTo(Room.Type); beatmapBind.BindTo(Room.Beatmap); participantsBind.BindTo(Room.Participants); + background.Beatmap.BindTo(beatmapBind); modeTypeInfo.Beatmap.BindTo(beatmapBind); modeTypeInfo.Type.BindTo(typeBind); diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index e8de201b8f..5b796b5a3d 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -180,7 +180,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components nameBind.BindValueChanged(n => name.Text = n); - background.Beatmap.BindTo(beatmap); + background.Beatmap.BindTo(beatmapBind); participantInfo.Host.BindTo(hostBind); participantInfo.Participants.BindTo(participantsBind); diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index b5ae3baa15..005378756e 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Multi.Match.Components { public const float HEIGHT = 200; - public readonly IBindable Beatmap = new Bindable(); + public readonly IBindable Beatmap = new Bindable(); private readonly Box tabStrip; diff --git a/osu.Game/Screens/Multi/Match/MatchScreen.cs b/osu.Game/Screens/Multi/Match/MatchScreen.cs index 755b071f30..a2a7c4e70d 100644 --- a/osu.Game/Screens/Multi/Match/MatchScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchScreen.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Multi.Match }; header.OnRequestSelectBeatmap = () => Push(new MatchSongSelect()); - header.Beatmap.BindTo(Beatmap); + header.Beatmap.BindTo(beatmapBind); header.Tabs.Current.ValueChanged += t => { diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index fead90f9a2..d6c5a12d33 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -69,6 +69,9 @@ namespace osu.Game.Screens.Multi pi.SetRulesets(rulesets); } + // Temporarily + r.Beatmap.Value = r.Playlist.FirstOrDefault()?.Beatmap; + var existing = rooms.FirstOrDefault(e => e.RoomID.Value == r.RoomID.Value); if (existing == null) rooms.Add(r); From 264bd0e2aafd1283c1d7e45f5d8c5f0018260dc1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 13 Dec 2018 16:06:30 +0900 Subject: [PATCH 8/8] Fix invalid room values --- osu.Game/Online/Multiplayer/Room.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 4af92f68ea..e2322db397 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -35,8 +35,16 @@ namespace osu.Game.Online.Multiplayer [JsonProperty("duration")] public readonly Bindable Duration = new Bindable(100); - [JsonProperty("max_attempts")] - public readonly Bindable MaxAttempts = new Bindable(null); + [JsonIgnore] + public readonly Bindable MaxAttempts = new Bindable(); + + // Todo: Find a better way to do this (https://github.com/ppy/osu-framework/issues/1930) + [JsonProperty("max_attempts", DefaultValueHandling = DefaultValueHandling.Ignore)] + private int? maxAttempts + { + get => MaxAttempts; + set => MaxAttempts.Value = value; + } public Bindable Status = new Bindable(new RoomStatusOpen()); public Bindable Availability = new Bindable();