1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-06 06:57:39 +08:00

Use CMC for all of multiplayer

This commit is contained in:
smoogipoo 2019-02-05 19:00:01 +09:00
parent be51ee4ed5
commit aac371ba6e
38 changed files with 652 additions and 768 deletions

View File

@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
AddStep("select first room", () => container.Rooms.First().Action?.Invoke());
AddAssert("first room selected", () => container.SelectedRoom.Value == roomManager.Rooms.First());
AddAssert("first room selected", () => roomManager.CurrentRoom.Value == roomManager.Rooms.First());
AddStep("join first room", () => container.Rooms.First().Action?.Invoke());
AddAssert("first room joined", () => roomManager.Rooms.First().Status.Value is JoinedRoomStatus);
@ -76,6 +76,8 @@ namespace osu.Game.Tests.Visual
public readonly BindableList<Room> Rooms = new BindableList<Room>();
IBindableList<Room> IRoomManager.Rooms => Rooms;
public Bindable<Room> CurrentRoom { get; } = new Bindable<Room>();
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) => Rooms.Add(room);
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)

View File

@ -12,7 +12,7 @@ using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchHeader : OsuTestCase
public class TestCaseMatchHeader : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -21,11 +21,7 @@ namespace osu.Game.Tests.Visual
public TestCaseMatchHeader()
{
var room = new Room();
var header = new Header(room);
room.Playlist.Add(new PlaylistItem
Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{
@ -46,9 +42,9 @@ namespace osu.Game.Tests.Visual
}
});
room.Type.Value = new GameTypeTimeshift();
Room.Type.Value = new GameTypeTimeshift();
Child = header;
Child = new Header();
}
}
}

View File

@ -14,7 +14,7 @@ using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMatchInfo : OsuTestCase
public class TestCaseMatchInfo : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -27,18 +27,15 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
var room = new Room();
Add(new Info());
Info info = new Info(room);
Add(info);
AddStep(@"set name", () => room.Name.Value = @"Room Name?");
AddStep(@"set availability", () => room.Availability.Value = RoomAvailability.FriendsOnly);
AddStep(@"set status", () => room.Status.Value = new RoomStatusPlaying());
AddStep(@"set name", () => Room.Name.Value = @"Room Name?");
AddStep(@"set availability", () => Room.Availability.Value = RoomAvailability.FriendsOnly);
AddStep(@"set status", () => Room.Status.Value = new RoomStatusPlaying());
AddStep(@"set beatmap", () =>
{
room.Playlist.Clear();
room.Playlist.Add(new PlaylistItem
Room.Playlist.Clear();
Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{
@ -54,14 +51,14 @@ namespace osu.Game.Tests.Visual
});
});
AddStep(@"change name", () => room.Name.Value = @"Room Name!");
AddStep(@"change availability", () => room.Availability.Value = RoomAvailability.InviteOnly);
AddStep(@"change status", () => room.Status.Value = new RoomStatusOpen());
AddStep(@"null beatmap", () => room.Playlist.Clear());
AddStep(@"change name", () => Room.Name.Value = @"Room Name!");
AddStep(@"change availability", () => Room.Availability.Value = RoomAvailability.InviteOnly);
AddStep(@"change status", () => Room.Status.Value = new RoomStatusOpen());
AddStep(@"null beatmap", () => Room.Playlist.Clear());
AddStep(@"change beatmap", () =>
{
room.Playlist.Clear();
room.Playlist.Add(new PlaylistItem
Room.Playlist.Clear();
Room.Playlist.Add(new PlaylistItem
{
Beatmap = new BeatmapInfo
{

View File

@ -6,24 +6,24 @@ using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchLeaderboard : OsuTestCase
public class TestCaseMatchLeaderboard : MultiplayerTestCase
{
public TestCaseMatchLeaderboard()
{
Room.RoomID.Value = 3;
Add(new MatchLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = MatchLeaderboardScope.Overall,
Room = new Room { RoomID = { Value = 3 } }
});
}

View File

@ -1,9 +1,7 @@
// 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.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Users;
@ -11,22 +9,14 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseMatchParticipants : OsuTestCase
public class TestCaseMatchParticipants : MultiplayerTestCase
{
private readonly Bindable<int?> maxParticipants = new Bindable<int?>();
private readonly Bindable<IEnumerable<User>> users = new Bindable<IEnumerable<User>>();
public TestCaseMatchParticipants()
{
Participants participants;
Add(new Participants { RelativeSizeAxes = Axes.Both });
Add(participants = new Participants { RelativeSizeAxes = Axes.Both });
participants.MaxParticipants.BindTo(maxParticipants);
participants.Users.BindTo(users);
AddStep(@"set max to null", () => maxParticipants.Value = null);
AddStep(@"set users", () => users.Value = new[]
AddStep(@"set max to null", () => Room.MaxParticipants.Value = null);
AddStep(@"set users", () => Room.Participants.Value = new[]
{
new User
{
@ -54,9 +44,9 @@ namespace osu.Game.Tests.Visual
},
});
AddStep(@"set max", () => maxParticipants.Value = 10);
AddStep(@"clear users", () => users.Value = new User[] { });
AddStep(@"set max to null", () => maxParticipants.Value = null);
AddStep(@"set max", () => Room.MaxParticipants.Value = 10);
AddStep(@"clear users", () => Room.Participants.Value = new User[] { });
AddStep(@"set max to null", () => Room.MaxParticipants.Value = null);
}
}
}

View File

@ -8,7 +8,6 @@ using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Multiplayer;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Screens.Multi.Ranking;
@ -19,7 +18,7 @@ using osu.Game.Users;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchResults : OsuTestCase
public class TestCaseMatchResults : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -38,6 +37,9 @@ namespace osu.Game.Tests.Visual
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Room.RoomID.Value = 1;
Room.Name.Value = "an awesome room";
Child = new TestMatchResults(new ScoreInfo
{
User = new User { Id = 10 },
@ -46,60 +48,41 @@ namespace osu.Game.Tests.Visual
private class TestMatchResults : MatchResults
{
private readonly Room room;
public TestMatchResults(ScoreInfo score)
: this(score, new Room
{
RoomID = { Value = 1 },
Name = { Value = "an awesome room" }
})
: base(score)
{
}
public TestMatchResults(ScoreInfo score, Room room)
: base(score, room)
{
this.room = room;
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap, room) };
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap) };
}
private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
private readonly Room room;
public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap, Room room)
: base(score, beatmap, room)
public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
this.score = score;
this.beatmap = beatmap;
this.room = room;
}
public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap, room);
public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap);
}
private class TestRoomLeaderboardPage : RoomLeaderboardPage
{
public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room)
: base(score, beatmap, room)
public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
}
protected override MatchLeaderboard CreateLeaderboard(Room room) => new TestMatchLeaderboard(room);
protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard();
}
private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
{
public TestMatchLeaderboard(Room room)
: base(room)
{
}
protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback)
{
var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();

View File

@ -18,7 +18,7 @@ using osu.Game.Screens.Multi.Match.Components;
namespace osu.Game.Tests.Visual
{
public class TestCaseMatchSettingsOverlay : OsuTestCase
public class TestCaseMatchSettingsOverlay : MultiplayerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -28,14 +28,14 @@ namespace osu.Game.Tests.Visual
[Cached(Type = typeof(IRoomManager))]
private TestRoomManager roomManager = new TestRoomManager();
private Room room;
private TestRoomSettings settings;
[SetUp]
public void Setup() => Schedule(() =>
{
room = new Room();
settings = new TestRoomSettings(room)
Room = new Room();
settings = new TestRoomSettings
{
RelativeSizeAxes = Axes.Both,
State = Visibility.Visible
@ -49,19 +49,19 @@ namespace osu.Game.Tests.Visual
{
AddStep("clear name and beatmap", () =>
{
room.Name.Value = "";
room.Playlist.Clear();
Room.Name.Value = "";
Room.Playlist.Clear();
});
AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
AddStep("set name", () => room.Name.Value = "Room name");
AddStep("set name", () => Room.Name.Value = "Room name");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
AddStep("set beatmap", () => room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo }));
AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo }));
AddAssert("button enabled", () => settings.ApplyButton.Enabled);
AddStep("clear name", () => room.Name.Value = "");
AddStep("clear name", () => Room.Name.Value = "");
AddAssert("button disabled", () => !settings.ApplyButton.Enabled);
}
@ -117,17 +117,12 @@ namespace osu.Game.Tests.Visual
private class TestRoomSettings : MatchSettingsOverlay
{
public new TriangleButton ApplyButton => base.ApplyButton;
public TriangleButton ApplyButton => Settings.ApplyButton;
public new OsuTextBox NameField => base.NameField;
public new OsuDropdown<TimeSpan> DurationField => base.DurationField;
public OsuTextBox NameField => Settings.NameField;
public OsuDropdown<TimeSpan> DurationField => Settings.DurationField;
public new OsuSpriteText ErrorText => base.ErrorText;
public TestRoomSettings(Room room)
: base(room)
{
}
public OsuSpriteText ErrorText => Settings.ErrorText;
}
private class TestRoomManager : IRoomManager
@ -140,6 +135,8 @@ namespace osu.Game.Tests.Visual
public IBindableList<Room> Rooms { get; } = null;
public Bindable<Room> CurrentRoom { get; } = new Bindable<Room>();
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{
if (CreateRequested == null)

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Online.Multiplayer.GameTypes;
using osu.Game.Online.Multiplayer.RoomStatuses;
@ -14,42 +15,55 @@ namespace osu.Game.Online.Multiplayer
{
public class Room
{
[Cached]
[JsonProperty("id")]
public Bindable<int?> RoomID { get; private set; } = new Bindable<int?>();
[Cached]
[JsonProperty("name")]
public Bindable<string> Name { get; private set; } = new Bindable<string>();
[Cached]
[JsonProperty("host")]
public Bindable<User> Host { get; private set; } = new Bindable<User>();
[Cached]
[JsonProperty("playlist")]
public BindableList<PlaylistItem> Playlist { get; set; } = new BindableList<PlaylistItem>();
public BindableList<PlaylistItem> Playlist { get; private set; } = new BindableList<PlaylistItem>();
[Cached]
[JsonProperty("channel_id")]
public Bindable<int> ChannelId { get; private set; } = new Bindable<int>();
[Cached]
[JsonIgnore]
public Bindable<TimeSpan> Duration { get; private set; } = new Bindable<TimeSpan>(TimeSpan.FromMinutes(30));
[Cached]
[JsonIgnore]
public Bindable<int?> MaxAttempts { get; private set; } = new Bindable<int?>();
[Cached]
[JsonIgnore]
public Bindable<RoomStatus> Status { get; private set; } = new Bindable<RoomStatus>(new RoomStatusOpen());
[Cached]
[JsonIgnore]
public Bindable<RoomAvailability> Availability { get; private set; } = new Bindable<RoomAvailability>();
[Cached]
[JsonIgnore]
public Bindable<GameType> Type { get; private set; } = new Bindable<GameType>(new GameTypeTimeshift());
[Cached]
[JsonIgnore]
public Bindable<int?> MaxParticipants { get; private set; } = new Bindable<int?>();
[Cached]
[JsonIgnore]
public Bindable<IEnumerable<User>> Participants { get; private set; } = new Bindable<IEnumerable<User>>(Enumerable.Empty<User>());
[Cached]
public Bindable<int> ParticipantCount { get; private set; } = new Bindable<int>();
// todo: TEMPORARY
@ -68,6 +82,7 @@ namespace osu.Game.Online.Multiplayer
}
// Only supports retrieval for now
[Cached]
[JsonProperty("ends_at")]
public Bindable<DateTimeOffset> EndDate { get; private set; } = new Bindable<DateTimeOffset>();

View File

@ -2,11 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
@ -14,10 +11,8 @@ using osu.Game.Online.Chat;
namespace osu.Game.Screens.Multi.Components
{
public class BeatmapTitle : CompositeDrawable
public class BeatmapTitle : MultiplayerComposite
{
public readonly IBindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
private readonly LinkFlowContainer textFlow;
public BeatmapTitle()
@ -27,10 +22,10 @@ namespace osu.Game.Screens.Multi.Components
InternalChild = textFlow = new LinkFlowContainer { AutoSizeAxes = Axes.Both };
}
protected override void LoadComplete()
[BackgroundDependencyLoader]
private void load()
{
base.LoadComplete();
Beatmap.BindValueChanged(v => updateText(), true);
CurrentBeatmap.BindValueChanged(v => updateText(), true);
}
private float textSize = OsuSpriteText.FONT_SIZE;
@ -58,7 +53,7 @@ namespace osu.Game.Screens.Multi.Components
textFlow.Clear();
if (Beatmap.Value == null)
if (CurrentBeatmap.Value == null)
textFlow.AddText("No beatmap selected", s =>
{
s.TextSize = TextSize;
@ -70,7 +65,7 @@ namespace osu.Game.Screens.Multi.Components
{
new OsuSpriteText
{
Text = new LocalisedString((Beatmap.Value.Metadata.ArtistUnicode, Beatmap.Value.Metadata.Artist)),
Text = new LocalisedString((CurrentBeatmap.Value.Metadata.ArtistUnicode, CurrentBeatmap.Value.Metadata.Artist)),
TextSize = TextSize,
},
new OsuSpriteText
@ -80,10 +75,10 @@ namespace osu.Game.Screens.Multi.Components
},
new OsuSpriteText
{
Text = new LocalisedString((Beatmap.Value.Metadata.TitleUnicode, Beatmap.Value.Metadata.Title)),
Text = new LocalisedString((CurrentBeatmap.Value.Metadata.TitleUnicode, CurrentBeatmap.Value.Metadata.Title)),
TextSize = TextSize,
}
}, null, LinkAction.OpenBeatmap, Beatmap.Value.OnlineBeatmapID.ToString(), "Open beatmap");
}, null, LinkAction.OpenBeatmap, CurrentBeatmap.Value.OnlineBeatmapID.ToString(), "Open beatmap");
}
}
}

View File

@ -1,31 +1,26 @@
// 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.Configuration;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.Chat;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Screens.Multi.Components
{
public class BeatmapTypeInfo : CompositeDrawable
public class BeatmapTypeInfo : MultiplayerComposite
{
public readonly IBindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
public readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
public readonly IBindable<GameType> Type = new Bindable<GameType>();
public BeatmapTypeInfo()
{
AutoSizeAxes = Axes.Both;
}
BeatmapTitle beatmapTitle;
ModeTypeInfo modeTypeInfo;
[BackgroundDependencyLoader]
private void load()
{
LinkFlowContainer beatmapAuthor;
InternalChild = new FillFlowContainer
@ -36,7 +31,7 @@ namespace osu.Game.Screens.Multi.Components
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
modeTypeInfo = new ModeTypeInfo(),
new ModeTypeInfo(),
new Container
{
AutoSizeAxes = Axes.X,
@ -44,7 +39,7 @@ namespace osu.Game.Screens.Multi.Components
Margin = new MarginPadding { Left = 5 },
Children = new Drawable[]
{
beatmapTitle = new BeatmapTitle(),
new BeatmapTitle(),
beatmapAuthor = new LinkFlowContainer(s => s.TextSize = 14)
{
Anchor = Anchor.BottomLeft,
@ -56,13 +51,7 @@ namespace osu.Game.Screens.Multi.Components
}
};
modeTypeInfo.Beatmap.BindTo(Beatmap);
modeTypeInfo.Ruleset.BindTo(Ruleset);
modeTypeInfo.Type.BindTo(Type);
beatmapTitle.Beatmap.BindTo(Beatmap);
Beatmap.BindValueChanged(v =>
CurrentBeatmap.BindValueChanged(v =>
{
beatmapAuthor.Clear();

View File

@ -1,32 +1,29 @@
// 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.Configuration;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Screens.Multi.Components
{
public class ModeTypeInfo : CompositeDrawable
public class ModeTypeInfo : MultiplayerComposite
{
private const float height = 30;
private const float transition_duration = 100;
private readonly Container rulesetContainer;
public readonly IBindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
public readonly IBindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
public readonly IBindable<GameType> Type = new Bindable<GameType>();
private Container rulesetContainer;
public ModeTypeInfo()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
Container gameTypeContainer;
InternalChild = new FillFlowContainer
@ -48,17 +45,17 @@ namespace osu.Game.Screens.Multi.Components
},
};
Beatmap.BindValueChanged(updateBeatmap);
Ruleset.BindValueChanged(_ => updateBeatmap(Beatmap.Value));
CurrentBeatmap.BindValueChanged(_ => updateBeatmap());
CurrentRuleset.BindValueChanged(_ => updateBeatmap());
Type.BindValueChanged(v => gameTypeContainer.Child = new DrawableGameType(v) { Size = new Vector2(height) });
}
private void updateBeatmap(BeatmapInfo beatmap)
private void updateBeatmap()
{
if (beatmap != null)
if (CurrentBeatmap.Value != null)
{
rulesetContainer.FadeIn(transition_duration);
rulesetContainer.Child = new DifficultyIcon(beatmap, Ruleset.Value) { Size = new Vector2(height) };
rulesetContainer.Child = new DifficultyIcon(CurrentBeatmap.Value, CurrentRuleset.Value) { Size = new Vector2(height) };
}
else
rulesetContainer.FadeOut(transition_duration);

View File

@ -0,0 +1,24 @@
// 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;
using osu.Game.Beatmaps.Drawables;
namespace osu.Game.Screens.Multi.Components
{
public class MultiplayerBackgroundSprite : MultiplayerComposite
{
[BackgroundDependencyLoader]
private void load()
{
UpdateableBeatmapBackgroundSprite sprite;
InternalChild = sprite = CreateBackgroundSprite();
sprite.Beatmap.BindTo(CurrentBeatmap);
}
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both };
}
}

View File

@ -1,31 +1,29 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
namespace osu.Game.Screens.Multi.Components
{
public class ParticipantCountDisplay : CompositeDrawable
public class ParticipantCountDisplay : MultiplayerComposite
{
private const float text_size = 30;
private const float transition_duration = 100;
private readonly OsuSpriteText slash, maxText;
public readonly IBindable<IEnumerable<User>> Participants = new Bindable<IEnumerable<User>>();
public readonly IBindable<int> ParticipantCount = new Bindable<int>();
public readonly IBindable<int?> MaxParticipants = new Bindable<int?>();
private OsuSpriteText slash, maxText;
public ParticipantCountDisplay()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
OsuSpriteText count;
InternalChild = new FillFlowContainer

View File

@ -13,16 +13,16 @@ using osu.Game.Online.Multiplayer.RoomStatuses;
namespace osu.Game.Screens.Multi.Components
{
public class RoomStatusInfo : CompositeDrawable
public class RoomStatusInfo : MultiplayerComposite
{
private readonly RoomBindings bindings = new RoomBindings();
public RoomStatusInfo(Room room)
public RoomStatusInfo()
{
bindings.Room = room;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
StatusPart statusPart;
EndDatePart endDatePart;
@ -41,10 +41,10 @@ namespace osu.Game.Screens.Multi.Components
}
};
statusPart.EndDate.BindTo(bindings.EndDate);
statusPart.Status.BindTo(bindings.Status);
statusPart.Availability.BindTo(bindings.Availability);
endDatePart.EndDate.BindTo(bindings.EndDate);
statusPart.EndDate.BindTo(EndDate);
statusPart.Status.BindTo(Status);
statusPart.Availability.BindTo(Availability);
endDatePart.EndDate.BindTo(EndDate);
}
private class EndDatePart : DrawableDate

View File

@ -19,6 +19,11 @@ namespace osu.Game.Screens.Multi
/// </summary>
IBindableList<Room> Rooms { get; }
/// <summary>
/// The currently-active <see cref="Room"/>.
/// </summary>
Bindable<Room> CurrentRoom { get; }
/// <summary>
/// Creates a new <see cref="Room"/>.
/// </summary>

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
@ -34,12 +33,8 @@ namespace osu.Game.Screens.Multi.Lounge.Components
public event Action<SelectionState> StateChanged;
private readonly RoomBindings bindings = new RoomBindings();
private readonly Box selectionBox;
private UpdateableBeatmapBackgroundSprite background;
private BeatmapTitle beatmapTitle;
private ModeTypeInfo modeTypeInfo;
private CachedModelDependencyContainer<Room> dependencies;
[Resolved]
private BeatmapManager beatmaps { get; set; }
@ -80,7 +75,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components
public DrawableRoom(Room room)
{
Room = room;
bindings.Room = room;
RelativeSizeAxes = Axes.X;
Height = height + SELECTION_BORDER_WIDTH * 2;
@ -99,7 +93,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components
private void load(OsuColour colours)
{
Box sideStrip;
ParticipantInfo participantInfo;
OsuSpriteText name;
Children = new Drawable[]
@ -138,7 +131,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
Width = cover_width,
Masking = true,
Margin = new MarginPadding { Left = side_strip_width },
Child = background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }
Child = new MultiplayerBackgroundSprite { RelativeSizeAxes = Axes.Both }
},
new Container
{
@ -159,8 +152,11 @@ namespace osu.Game.Screens.Multi.Lounge.Components
Spacing = new Vector2(5f),
Children = new Drawable[]
{
name = new OsuSpriteText { TextSize = 18 },
participantInfo = new ParticipantInfo(),
name = new OsuSpriteText
{
TextSize = 18
},
new ParticipantInfo(),
},
},
new FillFlowContainer
@ -173,11 +169,11 @@ namespace osu.Game.Screens.Multi.Lounge.Components
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new RoomStatusInfo(Room),
beatmapTitle = new BeatmapTitle { TextSize = 14 },
new RoomStatusInfo(),
new BeatmapTitle { TextSize = 14 },
},
},
modeTypeInfo = new ModeTypeInfo
new ModeTypeInfo
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
@ -189,23 +185,21 @@ namespace osu.Game.Screens.Multi.Lounge.Components
},
};
background.Beatmap.BindTo(bindings.CurrentBeatmap);
modeTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap);
modeTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset);
modeTypeInfo.Type.BindTo(bindings.Type);
beatmapTitle.Beatmap.BindTo(bindings.CurrentBeatmap);
participantInfo.Host.BindTo(bindings.Host);
participantInfo.Participants.BindTo(bindings.Participants);
participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount);
bindings.Name.BindValueChanged(n => name.Text = n, true);
bindings.Status.BindValueChanged(s =>
dependencies.ShadowModel.Name.BindValueChanged(n => name.Text = n, true);
dependencies.ShadowModel.Status.BindValueChanged(s =>
{
foreach (Drawable d in new Drawable[] { selectionBox, sideStrip })
d.FadeColour(s.GetAppropriateColour(colours), transition_duration);
}, true);
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
dependencies = new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent));
dependencies.Model.Value = Room;
return dependencies;
}
protected override void LoadComplete()
{
base.LoadComplete();

View File

@ -1,10 +1,8 @@
// 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.Collections.Generic;
using Humanizer;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
@ -16,26 +14,24 @@ using osuTK;
namespace osu.Game.Screens.Multi.Lounge.Components
{
public class ParticipantInfo : Container
public class ParticipantInfo : MultiplayerComposite
{
private readonly FillFlowContainer summaryContainer;
public readonly IBindable<User> Host = new Bindable<User>();
public readonly IBindable<IEnumerable<User>> Participants = new Bindable<IEnumerable<User>>();
public readonly IBindable<int> ParticipantCount = new Bindable<int>();
public ParticipantInfo()
{
OsuSpriteText summary;
RelativeSizeAxes = Axes.X;
Height = 15f;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
OsuSpriteText summary;
OsuSpriteText levelRangeHigher;
OsuSpriteText levelRangeLower;
Container flagContainer;
LinkFlowContainer hostText;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
@ -73,12 +69,13 @@ namespace osu.Game.Screens.Multi.Lounge.Components
}
},
},
summaryContainer = new FillFlowContainer
new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Colour = colours.Gray9,
Children = new[]
{
summary = new OsuSpriteText
@ -101,9 +98,9 @@ namespace osu.Game.Screens.Multi.Lounge.Components
hostText.AddLink(v.Username, null, LinkAction.OpenUserProfile, v.Id.ToString(), "Open profile", s => s.Font = "Exo2.0-BoldItalic");
flagContainer.Child = new DrawableFlag(v.Country) { RelativeSizeAxes = Axes.Both };
}
});
}, true);
ParticipantCount.BindValueChanged(v => summary.Text = $"{v:#,0}{" participant".Pluralize(v == 1)}");
ParticipantCount.BindValueChanged(v => summary.Text = $"{v:#,0}{" participant".Pluralize(v == 1)}", true);
/*Participants.BindValueChanged(v =>
{
@ -112,11 +109,5 @@ namespace osu.Game.Screens.Multi.Lounge.Components
levelRangeHigher.Text = ranks.Max().ToString();
});*/
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
summaryContainer.Colour = colours.Gray9;
}
}
}

View File

@ -3,7 +3,6 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@ -11,7 +10,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
@ -24,24 +22,18 @@ using osuTK.Graphics;
namespace osu.Game.Screens.Multi.Lounge.Components
{
public class RoomInspector : Container
public class RoomInspector : MultiplayerComposite
{
private const float transition_duration = 100;
public readonly IBindable<Room> Room = new Bindable<Room>();
private readonly MarginPadding contentPadding = new MarginPadding { Horizontal = 20, Vertical = 10 };
private readonly RoomBindings bindings = new RoomBindings();
private OsuColour colours;
private Box statusStrip;
private UpdateableBeatmapBackgroundSprite background;
private ParticipantCountDisplay participantCount;
private OsuSpriteText name, status;
private BeatmapTypeInfo beatmapTypeInfo;
private ParticipantInfo participantInfo;
private MatchParticipants participants;
[Resolved]
private BeatmapManager beatmaps { get; set; }
@ -51,7 +43,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
{
this.colours = colours;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new Box
{
@ -84,7 +76,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
Masking = true,
Children = new Drawable[]
{
background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both },
new MultiplayerBackgroundSprite { RelativeSizeAxes = Axes.Both },
new Box
{
RelativeSizeAxes = Axes.Both,
@ -162,7 +154,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
},
new Drawable[]
{
participants = new MatchParticipants
new MatchParticipants
{
RelativeSizeAxes = Axes.Both,
}
@ -171,27 +163,15 @@ namespace osu.Game.Screens.Multi.Lounge.Components
}
};
participantInfo.Host.BindTo(bindings.Host);
participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount);
participantInfo.Participants.BindTo(bindings.Participants);
participantCount.Participants.BindTo(bindings.Participants);
participantCount.ParticipantCount.BindTo(bindings.ParticipantCount);
participantCount.MaxParticipants.BindTo(bindings.MaxParticipants);
beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap);
beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset);
beatmapTypeInfo.Type.BindTo(bindings.Type);
background.Beatmap.BindTo(bindings.CurrentBeatmap);
bindings.Status.BindValueChanged(displayStatus);
bindings.Name.BindValueChanged(n => name.Text = n);
Room.BindValueChanged(updateRoom, true);
Status.BindValueChanged(displayStatus);
Name.BindValueChanged(n => name.Text = n);
RoomID.BindValueChanged(updateRoom);
}
private void updateRoom(Room room)
private void updateRoom(int? roomId)
{
bindings.Room = room;
participants.Room = room;
if (room != null)
if (roomId != null)
{
participantCount.FadeIn(transition_duration);
beatmapTypeInfo.FadeIn(transition_duration);
@ -224,24 +204,10 @@ namespace osu.Game.Screens.Multi.Lounge.Components
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray8;
}
private class MatchParticipants : CompositeDrawable
private class MatchParticipants : MultiplayerComposite
{
private Room room;
private readonly FillFlowContainer fill;
public Room Room
{
get { return room; }
set
{
if (room == value)
return;
room = value;
updateParticipants();
}
}
public MatchParticipants()
{
Padding = new MarginPadding { Horizontal = 10 };
@ -259,6 +225,12 @@ namespace osu.Game.Screens.Multi.Lounge.Components
};
}
[BackgroundDependencyLoader]
private void load()
{
RoomID.BindValueChanged(_ => updateParticipants(), true);
}
[Resolved]
private APIAccess api { get; set; }
@ -266,7 +238,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
private void updateParticipants()
{
var roomId = room?.RoomID.Value ?? 0;
var roomId = RoomID.Value ?? 0;
request?.Cancel();
@ -284,7 +256,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
request = new GetRoomScoresRequest(roomId);
request.Success += scores =>
{
if (roomId != room.RoomID.Value)
if (roomId != RoomID.Value)
return;
fill.Clear();

View File

@ -19,8 +19,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
{
public Action<Room> JoinRequested;
private readonly Bindable<Room> selectedRoom = new Bindable<Room>();
public IBindable<Room> SelectedRoom => selectedRoom;
private readonly Bindable<Room> currentRoom = new Bindable<Room>();
private readonly IBindableList<Room> rooms = new BindableList<Room>();
@ -47,6 +46,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
[BackgroundDependencyLoader]
private void load()
{
currentRoom.BindTo(roomManager.CurrentRoom);
rooms.BindTo(roomManager.Rooms);
rooms.ItemsAdded += addRooms;
@ -121,7 +121,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
else
roomFlow.Children.ForEach(r => r.State = r.Room == room ? SelectionState.Selected : SelectionState.NotSelected);
selectedRoom.Value = room;
currentRoom.Value = room;
}
protected override void Dispose(bool isDisposing)

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@ -24,13 +26,12 @@ namespace osu.Game.Screens.Multi.Lounge
private readonly Action<Screen> pushGameplayScreen;
private readonly ProcessingOverlay processingOverlay;
private readonly Bindable<Room> currentRoom = new Bindable<Room>();
public LoungeSubScreen(Action<Screen> pushGameplayScreen)
{
this.pushGameplayScreen = pushGameplayScreen;
RoomInspector inspector;
RoomsContainer rooms;
InternalChildren = new Drawable[]
{
Filter = new FilterControl { Depth = -1 },
@ -54,13 +55,13 @@ namespace osu.Game.Screens.Multi.Lounge
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = rooms = new RoomsContainer { JoinRequested = joinRequested }
Child = new RoomsContainer { JoinRequested = joinRequested }
},
},
processingOverlay = new ProcessingOverlay { Alpha = 0 }
}
},
inspector = new RoomInspector
new RoomInspector
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
@ -71,11 +72,15 @@ namespace osu.Game.Screens.Multi.Lounge
},
};
inspector.Room.BindTo(rooms.SelectedRoom);
Filter.Search.Exit += this.Exit;
}
[BackgroundDependencyLoader]
private void load(IRoomManager roomManager)
{
currentRoom.BindTo(roomManager.CurrentRoom);
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
@ -116,7 +121,7 @@ namespace osu.Game.Screens.Multi.Lounge
processingOverlay.Show();
Manager?.JoinRoom(room, r =>
{
Push(room);
Open(room);
processingOverlay.Hide();
}, _ => processingOverlay.Hide());
}
@ -124,13 +129,15 @@ namespace osu.Game.Screens.Multi.Lounge
/// <summary>
/// Push a room as a new subscreen.
/// </summary>
public void Push(Room room)
public void Open(Room room)
{
// Handles the case where a room is clicked 3 times in quick succession
if (!this.IsCurrentScreen())
return;
this.Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s)));
currentRoom.Value = room;
this.Push(new MatchSubScreen(s => pushGameplayScreen?.Invoke(s)));
}
}
}

View File

@ -20,31 +20,27 @@ using osuTK.Graphics;
namespace osu.Game.Screens.Multi.Match.Components
{
public class Header : Container
public class Header : MultiplayerComposite
{
public const float HEIGHT = 200;
private readonly RoomBindings bindings = new RoomBindings();
public MatchTabControl Tabs;
private readonly Box tabStrip;
public Action RequestBeatmapSelection;
public readonly MatchTabControl Tabs;
public Action OnRequestSelectBeatmap;
public Header(Room room)
public Header()
{
RelativeSizeAxes = Axes.X;
Height = HEIGHT;
}
bindings.Room = room;
BeatmapTypeInfo beatmapTypeInfo;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BeatmapSelectButton beatmapButton;
UpdateableBeatmapBackgroundSprite background;
ModDisplay modDisplay;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new Container
{
@ -52,7 +48,7 @@ namespace osu.Game.Screens.Multi.Match.Components
Masking = true,
Children = new Drawable[]
{
background = new HeaderBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both },
new HeaderBackgroundSprite { RelativeSizeAxes = Axes.Both },
new Box
{
RelativeSizeAxes = Axes.Both,
@ -60,12 +56,13 @@ namespace osu.Game.Screens.Multi.Match.Components
},
}
},
tabStrip = new Box
new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = colours.Yellow
},
new Container
{
@ -80,7 +77,7 @@ namespace osu.Game.Screens.Multi.Match.Components
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
beatmapTypeInfo = new BeatmapTypeInfo(),
new BeatmapTypeInfo(),
modDisplay = new ModDisplay
{
Scale = new Vector2(0.75f),
@ -95,13 +92,13 @@ namespace osu.Game.Screens.Multi.Match.Components
RelativeSizeAxes = Axes.Y,
Width = 200,
Padding = new MarginPadding { Vertical = 10 },
Child = beatmapButton = new BeatmapSelectButton(room)
Child = beatmapButton = new BeatmapSelectButton
{
RelativeSizeAxes = Axes.Both,
Height = 1,
},
},
Tabs = new MatchTabControl(room)
Tabs = new MatchTabControl
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
@ -111,37 +108,36 @@ namespace osu.Game.Screens.Multi.Match.Components
},
};
beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap);
beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset);
beatmapTypeInfo.Type.BindTo(bindings.Type);
background.Beatmap.BindTo(bindings.CurrentBeatmap);
bindings.CurrentMods.BindValueChanged(m => modDisplay.Current.Value = m, true);
CurrentMods.BindValueChanged(m => modDisplay.Current.Value = m, true);
beatmapButton.Action = () => OnRequestSelectBeatmap?.Invoke();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabStrip.Colour = colours.Yellow;
beatmapButton.Action = () => RequestBeatmapSelection?.Invoke();
}
private class BeatmapSelectButton : HeaderButton
{
private readonly IBindable<int?> roomIDBind = new Bindable<int?>();
[Resolved(typeof(Room), nameof(Online.Multiplayer.Room.RoomID))]
private Bindable<int?> roomId { get; set; }
public BeatmapSelectButton(Room room)
public BeatmapSelectButton()
{
Text = "Select beatmap";
}
roomIDBind.BindTo(room.RoomID);
roomIDBind.BindValueChanged(v => this.FadeTo(v.HasValue ? 0 : 1), true);
[BackgroundDependencyLoader]
private void load()
{
roomId.BindValueChanged(v => this.FadeTo(v.HasValue ? 0 : 1), true);
}
}
private class HeaderBeatmapBackgroundSprite : UpdateableBeatmapBackgroundSprite
private class HeaderBackgroundSprite : MultiplayerBackgroundSprite
{
protected override double FadeDuration => 200;
protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BackgroundSprite { RelativeSizeAxes = Axes.Both };
private class BackgroundSprite : UpdateableBeatmapBackgroundSprite
{
protected override double FadeDuration => 200;
}
}
}
}

View File

@ -2,35 +2,37 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer;
using osu.Game.Overlays.SearchableList;
using osu.Game.Screens.Multi.Components;
using osuTK;
namespace osu.Game.Screens.Multi.Match.Components
{
public class Info : Container
public class Info : MultiplayerComposite
{
public Action OnStart;
private readonly RoomBindings bindings = new RoomBindings();
public Info(Room room)
public Info()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load()
{
ReadyButton readyButton;
ViewBeatmapButton viewBeatmapButton;
HostInfo hostInfo;
RoomStatusInfo statusInfo;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new Box
{
@ -61,9 +63,9 @@ namespace osu.Game.Screens.Multi.Match.Components
new OsuSpriteText
{
TextSize = 30,
Current = bindings.Name
Current = Name
},
new RoomStatusInfo(room),
new RoomStatusInfo(),
}
},
hostInfo = new HostInfo(),
@ -80,7 +82,7 @@ namespace osu.Game.Screens.Multi.Match.Components
Children = new Drawable[]
{
viewBeatmapButton = new ViewBeatmapButton(),
readyButton = new ReadyButton(room)
readyButton = new ReadyButton
{
Action = () => OnStart?.Invoke()
}
@ -90,11 +92,9 @@ namespace osu.Game.Screens.Multi.Match.Components
},
};
viewBeatmapButton.Beatmap.BindTo(bindings.CurrentBeatmap);
readyButton.Beatmap.BindTo(bindings.CurrentBeatmap);
hostInfo.Host.BindTo(bindings.Host);
bindings.Room = room;
viewBeatmapButton.Beatmap.BindTo(CurrentBeatmap);
readyButton.Beatmap.BindTo(CurrentBeatmap);
hostInfo.Host.BindTo(Host);
}
}
}

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.Configuration;
using osu.Game.Online.Chat;
using osu.Game.Online.Multiplayer;
@ -9,28 +10,31 @@ namespace osu.Game.Screens.Multi.Match.Components
{
public class MatchChatDisplay : StandAloneChatDisplay
{
private readonly Room room;
[Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; }
[Resolved(typeof(Room), nameof(Room.ChannelId))]
private Bindable<int> channelId { get; set; }
[Resolved(CanBeNull = true)]
private ChannelManager channelManager { get; set; }
public MatchChatDisplay(Room room)
public MatchChatDisplay()
: base(true)
{
this.room = room;
}
protected override void LoadComplete()
{
base.LoadComplete();
room.RoomID.BindValueChanged(v => updateChannel(), true);
roomId.BindValueChanged(v => updateChannel(), true);
}
private void updateChannel()
{
if (room.RoomID.Value != null)
Channel.Value = channelManager?.JoinChannel(new Channel { Id = room.ChannelId, Type = ChannelType.Multiplayer, Name = $"#mp_{room.RoomID}" });
if (roomId.Value != null)
Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId, Type = ChannelType.Multiplayer, Name = $"#mp_{roomId.Value}" });
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
@ -16,18 +17,13 @@ namespace osu.Game.Screens.Multi.Match.Components
{
public Action<IEnumerable<APIRoomScoreInfo>> ScoresLoaded;
public Room Room
{
get => bindings.Room;
set => bindings.Room = value;
}
private readonly RoomBindings bindings = new RoomBindings();
[Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; }
[BackgroundDependencyLoader]
private void load()
{
bindings.RoomID.BindValueChanged(id =>
roomId.BindValueChanged(id =>
{
if (id == null)
return;
@ -39,10 +35,10 @@ namespace osu.Game.Screens.Multi.Match.Components
protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback)
{
if (bindings.RoomID.Value == null)
if (roomId.Value == null)
return null;
var req = new GetRoomScoresRequest(bindings.RoomID.Value ?? 0);
var req = new GetRoomScoresRequest(roomId.Value ?? 0);
req.Success += r =>
{

View File

@ -22,43 +22,53 @@ namespace osu.Game.Screens.Multi.Match.Components
{
private const float transition_duration = 350;
private const float field_padding = 45;
private const float disabled_alpha = 0.2f;
private readonly RoomBindings bindings = new RoomBindings();
protected MatchSettings Settings { get; private set; }
private readonly Container content;
private readonly OsuSpriteText typeLabel;
protected readonly OsuTextBox NameField, MaxParticipantsField;
protected readonly OsuDropdown<TimeSpan> DurationField;
protected readonly RoomAvailabilityPicker AvailabilityPicker;
protected readonly GameTypePicker TypePicker;
protected readonly TriangleButton ApplyButton;
protected readonly OsuPasswordTextBox PasswordField;
protected readonly OsuSpriteText ErrorText;
private readonly ProcessingOverlay processingOverlay;
private readonly Room room;
[Resolved(CanBeNull = true)]
private IRoomManager manager { get; set; }
public MatchSettingsOverlay(Room room)
[BackgroundDependencyLoader]
private void load()
{
this.room = room;
bindings.Room = room;
Masking = true;
Child = content = new Container
Child = Settings = new MatchSettings
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
Children = new Drawable[]
RelativePositionAxes = Axes.Y
};
}
protected override void PopIn()
{
Settings.MoveToY(0, transition_duration, Easing.OutQuint);
}
protected override void PopOut()
{
Settings.MoveToY(-1, transition_duration, Easing.InSine);
}
protected class MatchSettings : MultiplayerComposite
{
private const float disabled_alpha = 0.2f;
public OsuTextBox NameField, MaxParticipantsField;
public OsuDropdown<TimeSpan> DurationField;
public RoomAvailabilityPicker AvailabilityPicker;
public GameTypePicker TypePicker;
public TriangleButton ApplyButton;
public OsuSpriteText ErrorText;
private OsuSpriteText typeLabel;
private ProcessingOverlay processingOverlay;
[Resolved(CanBeNull = true)]
private IRoomManager manager { get; set; }
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
InternalChildren = new Drawable[]
{
new Box
{
@ -111,7 +121,10 @@ namespace osu.Game.Screens.Multi.Match.Components
new Section("Room visibility")
{
Alpha = disabled_alpha,
Child = AvailabilityPicker = new RoomAvailabilityPicker(),
Child = AvailabilityPicker = new RoomAvailabilityPicker
{
Enabled = { Value = false }
},
},
new Section("Game type")
{
@ -127,10 +140,12 @@ namespace osu.Game.Screens.Multi.Match.Components
TypePicker = new GameTypePicker
{
RelativeSizeAxes = Axes.X,
Enabled = { Value = false }
},
typeLabel = new OsuSpriteText
{
TextSize = 14,
Colour = colours.Yellow
},
},
},
@ -151,7 +166,8 @@ namespace osu.Game.Screens.Multi.Match.Components
{
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this,
OnCommit = (sender, text) => apply(),
ReadOnly = true,
OnCommit = (sender, text) => apply()
},
},
new Section("Duration")
@ -177,10 +193,11 @@ namespace osu.Game.Screens.Multi.Match.Components
new Section("Password (optional)")
{
Alpha = disabled_alpha,
Child = PasswordField = new SettingsPasswordTextBox
Child = new SettingsPasswordTextBox
{
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this,
ReadOnly = true,
OnCommit = (sender, text) => apply()
},
},
@ -222,6 +239,7 @@ namespace osu.Game.Screens.Multi.Match.Components
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Size = new Vector2(230, 55),
Enabled = { Value = false },
Action = apply,
},
ErrorText = new OsuSpriteText
@ -229,7 +247,8 @@ namespace osu.Game.Screens.Multi.Match.Components
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Alpha = 0,
Depth = 1
Depth = 1,
Colour = colours.RedDark
}
}
}
@ -239,80 +258,58 @@ namespace osu.Game.Screens.Multi.Match.Components
}
},
processingOverlay = new ProcessingOverlay { Alpha = 0 }
},
};
};
TypePicker.Current.ValueChanged += t => typeLabel.Text = t.Name;
TypePicker.Current.ValueChanged += t => typeLabel.Text = t.Name;
bindings.Name.BindValueChanged(n => NameField.Text = n, true);
bindings.Availability.BindValueChanged(a => AvailabilityPicker.Current.Value = a, true);
bindings.Type.BindValueChanged(t => TypePicker.Current.Value = t, true);
bindings.MaxParticipants.BindValueChanged(m => MaxParticipantsField.Text = m?.ToString(), true);
bindings.Duration.BindValueChanged(d => DurationField.Current.Value = d, true);
}
Name.BindValueChanged(n => NameField.Text = n, true);
Availability.BindValueChanged(a => AvailabilityPicker.Current.Value = a, true);
Type.BindValueChanged(t => TypePicker.Current.Value = t, true);
MaxParticipants.BindValueChanged(m => MaxParticipantsField.Text = m?.ToString(), true);
Duration.BindValueChanged(d => DurationField.Current.Value = d, true);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
typeLabel.Colour = colours.Yellow;
ErrorText.Colour = colours.RedDark;
protected override void Update()
{
base.Update();
MaxParticipantsField.ReadOnly = true;
PasswordField.ReadOnly = true;
AvailabilityPicker.Enabled.Value = false;
TypePicker.Enabled.Value = false;
ApplyButton.Enabled.Value = false;
}
ApplyButton.Enabled.Value = hasValidSettings;
}
protected override void Update()
{
base.Update();
private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0;
ApplyButton.Enabled.Value = hasValidSettings;
}
private bool hasValidSettings => bindings.Room.RoomID.Value == null && NameField.Text.Length > 0 && bindings.Playlist.Count > 0;
private void apply()
{
hideError();
protected override void PopIn()
{
content.MoveToY(0, transition_duration, Easing.OutQuint);
}
Name.Value = NameField.Text;
Availability.Value = AvailabilityPicker.Current.Value;
Type.Value = TypePicker.Current.Value;
protected override void PopOut()
{
content.MoveToY(-1, transition_duration, Easing.InSine);
}
if (int.TryParse(MaxParticipantsField.Text, out int max))
MaxParticipants.Value = max;
else
MaxParticipants.Value = null;
private void apply()
{
hideError();
Duration.Value = DurationField.Current.Value;
bindings.Name.Value = NameField.Text;
bindings.Availability.Value = AvailabilityPicker.Current.Value;
bindings.Type.Value = TypePicker.Current.Value;
manager?.CreateRoom(Room, onSuccess, onError);
if (int.TryParse(MaxParticipantsField.Text, out int max))
bindings.MaxParticipants.Value = max;
else
bindings.MaxParticipants.Value = null;
processingOverlay.Show();
}
bindings.Duration.Value = DurationField.Current.Value;
private void hideError() => ErrorText.FadeOut(50);
manager?.CreateRoom(room, onSuccess, onError);
private void onSuccess(Room room) => processingOverlay.Hide();
processingOverlay.Show();
}
private void onError(string text)
{
ErrorText.Text = text;
ErrorText.FadeIn(50);
private void hideError() => ErrorText.FadeOut(50);
private void onSuccess(Room room) => processingOverlay.Hide();
private void onError(string text)
{
ErrorText.Text = text;
ErrorText.FadeIn(50);
processingOverlay.Hide();
processingOverlay.Hide();
}
}
private class SettingsTextBox : OsuTextBox

View File

@ -14,12 +14,11 @@ namespace osu.Game.Screens.Multi.Match.Components
{
public class MatchTabControl : PageTabControl<MatchPage>
{
private readonly IBindable<int?> roomIdBind = new Bindable<int?>();
[Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; }
public MatchTabControl(Room room)
public MatchTabControl()
{
roomIdBind.BindTo(room.RoomID);
AddItem(new RoomMatchPage());
AddItem(new SettingsMatchPage());
}
@ -27,7 +26,7 @@ namespace osu.Game.Screens.Multi.Match.Components
[BackgroundDependencyLoader]
private void load()
{
roomIdBind.BindValueChanged(v =>
roomId.BindValueChanged(v =>
{
if (v.HasValue)
{

View File

@ -1,9 +1,8 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.SearchableList;
@ -13,16 +12,12 @@ using osuTK;
namespace osu.Game.Screens.Multi.Match.Components
{
public class Participants : CompositeDrawable
public class Participants : MultiplayerComposite
{
public readonly IBindable<IEnumerable<User>> Users = new Bindable<IEnumerable<User>>();
public readonly IBindable<int> ParticipantCount = new Bindable<int>();
public readonly IBindable<int?> MaxParticipants = new Bindable<int?>();
public Participants()
[BackgroundDependencyLoader]
private void load()
{
FillFlowContainer<UserPanel> usersFlow;
ParticipantCountDisplay count;
InternalChild = new Container
{
@ -36,7 +31,7 @@ namespace osu.Game.Screens.Multi.Match.Components
Padding = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
count = new ParticipantCountDisplay
new ParticipantCountDisplay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
@ -55,11 +50,7 @@ namespace osu.Game.Screens.Multi.Match.Components
},
};
count.Participants.BindTo(Users);
count.ParticipantCount.BindTo(ParticipantCount);
count.MaxParticipants.BindTo(MaxParticipants);
Users.BindValueChanged(v =>
Participants.BindValueChanged(v =>
{
usersFlow.Children = v.Select(u => new UserPanel(u)
{

View File

@ -16,7 +16,8 @@ namespace osu.Game.Screens.Multi.Match.Components
{
public readonly IBindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
private readonly Room room;
[Resolved(typeof(Room), nameof(Room.EndDate))]
private Bindable<DateTimeOffset> endDate { get; set; }
[Resolved]
private IBindableBeatmap gameBeatmap { get; set; }
@ -26,9 +27,8 @@ namespace osu.Game.Screens.Multi.Match.Components
private bool hasBeatmap;
public ReadyButton(Room room)
public ReadyButton()
{
this.room = room;
RelativeSizeAxes = Axes.Y;
Size = new Vector2(200, 1);
@ -74,7 +74,7 @@ namespace osu.Game.Screens.Multi.Match.Components
return;
}
bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < room.EndDate;
bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate;
Enabled.Value = hasBeatmap && hasEnoughTime;
}

View File

@ -4,6 +4,7 @@
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
@ -22,130 +23,38 @@ namespace osu.Game.Screens.Multi.Match
{
public override bool AllowBeatmapRulesetChange => false;
public override string Title => room.RoomID.Value == null ? "New room" : room.Name.Value;
public override string Title => roomId?.Value == null ? "New room" : name.Value;
public override string ShortTitle => "room";
private readonly RoomBindings bindings = new RoomBindings();
[Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; }
private readonly MatchLeaderboard leaderboard;
[Resolved(typeof(Room), nameof(Room.Name))]
private Bindable<string> name { get; set; }
private readonly Action<Screen> pushGameplayScreen;
[Resolved(typeof(Room), nameof(Room.Playlist))]
private BindableList<PlaylistItem> playlist { get; set; }
[Cached]
private readonly Room room;
[Resolved]
private BeatmapManager beatmapManager { get; set; }
public MatchSubScreen(Room room, Action<Screen> pushGameplayScreen)
public MatchSubScreen(Action<Screen> pushGameplayScreen)
{
this.room = room;
this.pushGameplayScreen = pushGameplayScreen;
bindings.Room = room;
MatchChatDisplay chat;
Components.Header header;
Info info;
GridContainer bottomRow;
MatchSettingsOverlay settings;
InternalChildren = new Drawable[]
InternalChild = new Match(pushGameplayScreen)
{
new GridContainer
RelativeSizeAxes = Axes.Both,
RequestBeatmapSelection = () => this.Push(new MatchSongSelect
{
RelativeSizeAxes = Axes.Both,
Content = new[]
Selected = item =>
{
new Drawable[] { header = new Components.Header(room) { Depth = -1 } },
new Drawable[] { info = new Info(room) { OnStart = onStart } },
new Drawable[]
{
bottomRow = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
leaderboard = new MatchLeaderboard
{
Padding = new MarginPadding
{
Left = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING,
Right = 10,
Vertical = 10,
},
RelativeSizeAxes = Axes.Both,
Room = room
},
new Container
{
Padding = new MarginPadding
{
Left = 10,
Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING,
Vertical = 10,
},
RelativeSizeAxes = Axes.Both,
Child = chat = new MatchChatDisplay(room)
{
RelativeSizeAxes = Axes.Both
}
},
},
},
}
},
playlist.Clear();
playlist.Add(item);
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Distributed),
}
},
new Container
}),
RequestExit = () =>
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = Components.Header.HEIGHT },
Child = settings = new MatchSettingsOverlay(room) { RelativeSizeAxes = Axes.Both },
},
};
header.OnRequestSelectBeatmap = () => this.Push(new MatchSongSelect
{
Selected = addPlaylistItem,
});
header.Tabs.Current.ValueChanged += t =>
{
const float fade_duration = 500;
if (t is SettingsMatchPage)
{
settings.Show();
info.FadeOut(fade_duration, Easing.OutQuint);
bottomRow.FadeOut(fade_duration, Easing.OutQuint);
}
else
{
settings.Hide();
info.FadeIn(fade_duration, Easing.OutQuint);
bottomRow.FadeIn(fade_duration, Easing.OutQuint);
if (this.IsCurrentScreen())
this.Exit();
}
};
chat.Exit += () =>
{
if (this.IsCurrentScreen())
this.Exit();
};
}
[BackgroundDependencyLoader]
private void load()
{
beatmapManager.ItemAdded += beatmapAdded;
}
public override bool OnExiting(IScreen next)
@ -154,73 +63,191 @@ namespace osu.Game.Screens.Multi.Match
return base.OnExiting(next);
}
protected override void LoadComplete()
private class Match : MultiplayerComposite
{
base.LoadComplete();
public Action RequestBeatmapSelection;
public Action RequestExit;
bindings.CurrentBeatmap.BindValueChanged(setBeatmap, true);
bindings.CurrentRuleset.BindValueChanged(setRuleset, true);
}
private readonly Action<Screen> pushGameplayScreen;
private void setBeatmap(BeatmapInfo beatmap)
{
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID);
private MatchLeaderboard leaderboard;
Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap));
}
[Resolved]
private IBindableBeatmap gameBeatmap { get; set; }
private void setRuleset(RulesetInfo ruleset)
{
if (ruleset == null)
return;
[Resolved]
private BeatmapManager beatmapManager { get; set; }
Game?.ForcefullySetRuleset(ruleset);
}
[Resolved(CanBeNull = true)]
private OsuGame game { get; set; }
private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() =>
{
if (Beatmap.Value != beatmapManager.DefaultBeatmap)
return;
if (bindings.CurrentBeatmap.Value == null)
return;
// Try to retrieve the corresponding local beatmap
var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == bindings.CurrentBeatmap.Value.OnlineBeatmapID);
if (localBeatmap != null)
Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap));
});
private void addPlaylistItem(PlaylistItem item)
{
bindings.Playlist.Clear();
bindings.Playlist.Add(item);
}
private void onStart()
{
Beatmap.Value.Mods.Value = bindings.CurrentMods.Value.ToArray();
switch (bindings.Type.Value)
public Match(Action<Screen> pushGameplayScreen)
{
default:
case GameTypeTimeshift _:
pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(room, room.Playlist.First().ID)
{
Exited = () => leaderboard.RefreshScores()
}));
break;
this.pushGameplayScreen = pushGameplayScreen;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
[BackgroundDependencyLoader]
private void load()
{
MatchChatDisplay chat;
Components.Header header;
Info info;
GridContainer bottomRow;
MatchSettingsOverlay settings;
if (beatmapManager != null)
beatmapManager.ItemAdded -= beatmapAdded;
InternalChildren = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
header = new Components.Header
{
Depth = -1,
RequestBeatmapSelection = () => RequestBeatmapSelection?.Invoke()
}
},
new Drawable[] { info = new Info { OnStart = onStart } },
new Drawable[]
{
bottomRow = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
leaderboard = new MatchLeaderboard
{
Padding = new MarginPadding
{
Left = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING,
Right = 10,
Vertical = 10,
},
RelativeSizeAxes = Axes.Both
},
new Container
{
Padding = new MarginPadding
{
Left = 10,
Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING,
Vertical = 10,
},
RelativeSizeAxes = Axes.Both,
Child = chat = new MatchChatDisplay
{
RelativeSizeAxes = Axes.Both
}
},
},
},
}
},
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Distributed),
}
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = Components.Header.HEIGHT },
Child = settings = new MatchSettingsOverlay { RelativeSizeAxes = Axes.Both },
},
};
header.Tabs.Current.ValueChanged += t =>
{
const float fade_duration = 500;
if (t is SettingsMatchPage)
{
settings.Show();
info.FadeOut(fade_duration, Easing.OutQuint);
bottomRow.FadeOut(fade_duration, Easing.OutQuint);
}
else
{
settings.Hide();
info.FadeIn(fade_duration, Easing.OutQuint);
bottomRow.FadeIn(fade_duration, Easing.OutQuint);
}
};
chat.Exit += () => RequestExit?.Invoke();
beatmapManager.ItemAdded += beatmapAdded;
}
protected override void LoadComplete()
{
base.LoadComplete();
CurrentBeatmap.BindValueChanged(setBeatmap, true);
CurrentRuleset.BindValueChanged(setRuleset, true);
}
private void setBeatmap(BeatmapInfo beatmap)
{
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID);
game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap));
}
private void setRuleset(RulesetInfo ruleset)
{
if (ruleset == null)
return;
game?.ForcefullySetRuleset(ruleset);
}
private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() =>
{
if (gameBeatmap.Value != beatmapManager.DefaultBeatmap)
return;
if (CurrentBeatmap.Value == null)
return;
// Try to retrieve the corresponding local beatmap
var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == CurrentBeatmap.Value.OnlineBeatmapID);
if (localBeatmap != null)
game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap));
});
private void onStart()
{
gameBeatmap.Value.Mods.Value = CurrentMods.Value.ToArray();
switch (Type.Value)
{
default:
case GameTypeTimeshift _:
pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(Playlist.First().ID)
{
Exited = () => leaderboard.RefreshScores()
}));
break;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (beatmapManager != null)
beatmapManager.ItemAdded -= beatmapAdded;
}
}
}
}

View File

@ -123,7 +123,7 @@ namespace osu.Game.Screens.Multi
Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING,
},
Text = "Create room",
Action = () => loungeSubScreen.Push(new Room
Action = () => loungeSubScreen.Open(new Room
{
Name = { Value = $"{api.LocalUser}'s awesome room" }
}),

View File

@ -18,52 +18,52 @@ namespace osu.Game.Screens.Multi
public class MultiplayerComposite : CompositeDrawable
{
[Resolved]
public Room Room { get; private set; }
protected Room Room { get; private set; }
[Resolved(typeof(Room))]
public Bindable<int?> RoomID { get; private set; }
protected Bindable<int?> RoomID { get; private set; }
[Resolved(typeof(Room))]
public Bindable<string> Name { get; private set; }
protected Bindable<string> Name { get; private set; }
[Resolved(typeof(Room))]
public Bindable<User> Host { get; private set; }
protected Bindable<User> Host { get; private set; }
[Resolved(typeof(Room))]
public Bindable<RoomStatus> Status { get; private set; }
protected Bindable<RoomStatus> Status { get; private set; }
[Resolved(typeof(Room))]
public Bindable<GameType> Type { get; private set; }
protected Bindable<GameType> Type { get; private set; }
[Resolved(typeof(Room))]
public BindableList<PlaylistItem> Playlist { get; private set; }
protected BindableList<PlaylistItem> Playlist { get; private set; }
[Resolved(typeof(Room))]
public Bindable<IEnumerable<User>> Participants { get; private set; }
protected Bindable<IEnumerable<User>> Participants { get; private set; }
[Resolved(typeof(Room))]
public Bindable<int> ParticipantCount { get; private set; }
protected Bindable<int> ParticipantCount { get; private set; }
[Resolved(typeof(Room))]
public Bindable<int?> MaxParticipants { get; private set; }
protected Bindable<int?> MaxParticipants { get; private set; }
[Resolved(typeof(Room))]
public Bindable<DateTimeOffset> EndDate { get; private set; }
protected Bindable<DateTimeOffset> EndDate { get; private set; }
[Resolved(typeof(Room))]
public Bindable<RoomAvailability> Availability { get; private set; }
protected Bindable<RoomAvailability> Availability { get; private set; }
[Resolved(typeof(Room))]
public Bindable<TimeSpan> Duration { get; private set; }
protected Bindable<TimeSpan> Duration { get; private set; }
private readonly Bindable<BeatmapInfo> currentBeatmap = new Bindable<BeatmapInfo>();
public IBindable<BeatmapInfo> CurrentBeatmap => currentBeatmap;
protected IBindable<BeatmapInfo> CurrentBeatmap => currentBeatmap;
private readonly Bindable<IEnumerable<Mod>> currentMods = new Bindable<IEnumerable<Mod>>();
public IBindable<IEnumerable<Mod>> CurrentMods => currentMods;
protected IBindable<IEnumerable<Mod>> CurrentMods => currentMods;
private readonly Bindable<RulesetInfo> currentRuleset = new Bindable<RulesetInfo>();
public IBindable<RulesetInfo> CurrentRuleset => currentRuleset;
protected IBindable<RulesetInfo> CurrentRuleset => currentRuleset;
protected override void LoadComplete()
{

View File

@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Online.API;
@ -21,15 +22,16 @@ namespace osu.Game.Screens.Multi.Play
{
public Action Exited;
private readonly Room room;
[Resolved(typeof(Room), nameof(Room.RoomID))]
private Bindable<int?> roomId { get; set; }
private readonly int playlistItemId;
[Resolved]
private APIAccess api { get; set; }
public TimeshiftPlayer(Room room, int playlistItemId)
public TimeshiftPlayer(int playlistItemId)
{
this.room = room;
this.playlistItemId = playlistItemId;
}
@ -42,7 +44,7 @@ namespace osu.Game.Screens.Multi.Play
bool failed = false;
var req = new CreateRoomScoreRequest(room.RoomID.Value ?? 0, playlistItemId);
var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItemId);
req.Success += r => token = r.ID;
req.Failure += e =>
{
@ -87,7 +89,7 @@ namespace osu.Game.Screens.Multi.Play
Debug.Assert(token != null);
var request = new SubmitRoomScoreRequest(token.Value, room.RoomID.Value ?? 0, playlistItemId, score);
var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItemId, score);
request.Failure += e => Logger.Error(e, "Failed to submit score");
api.Queue(request);
}
@ -99,6 +101,6 @@ namespace osu.Game.Screens.Multi.Play
Exited = null;
}
protected override Results CreateResults(ScoreInfo score) => new MatchResults(score, room);
protected override Results CreateResults(ScoreInfo score) => new MatchResults(score);
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Online.Multiplayer;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Ranking.Types;
using osu.Game.Screens.Ranking;
@ -12,19 +11,16 @@ namespace osu.Game.Screens.Multi.Ranking
{
public class MatchResults : Results
{
private readonly Room room;
public MatchResults(ScoreInfo score, Room room)
public MatchResults(ScoreInfo score)
: base(score)
{
this.room = room;
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new IResultPageInfo[]
{
new ScoreOverviewPageInfo(Score, Beatmap),
new LocalLeaderboardPageInfo(Score, Beatmap),
new RoomLeaderboardPageInfo(Score, Beatmap, room),
new RoomLeaderboardPageInfo(Score, Beatmap),
};
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Internal;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -23,16 +24,15 @@ namespace osu.Game.Screens.Multi.Ranking.Pages
{
public class RoomLeaderboardPage : ResultsPage
{
private readonly Room room;
private OsuColour colours;
private TextFlowContainer rankText;
public RoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room)
[Resolved(typeof(Room), nameof(Room.Name))]
private Bindable<string> name { get; set; }
public RoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
this.room = room;
}
[BackgroundDependencyLoader]
@ -53,7 +53,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages
{
RelativeSizeAxes = Axes.Both,
BackgroundColour = colours.Gray6,
Child = leaderboard = CreateLeaderboard(room)
Child = leaderboard = CreateLeaderboard()
},
rankText = new TextFlowContainer
{
@ -84,7 +84,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages
s.Colour = colours.GrayF;
};
rankText.AddText(room.Name + "\n", white);
rankText.AddText(name + "\n", white);
rankText.AddText("You are placed ", gray);
int index = scores.IndexOf(new APIRoomScoreInfo { User = Score.User }, new FuncEqualityComparer<APIRoomScoreInfo>((s1, s2) => s1.User.Id.Equals(s2.User.Id)));
@ -98,15 +98,10 @@ namespace osu.Game.Screens.Multi.Ranking.Pages
rankText.AddText("in the room!", gray);
}
protected virtual MatchLeaderboard CreateLeaderboard(Room room) => new ResultsMatchLeaderboard(room);
protected virtual MatchLeaderboard CreateLeaderboard() => new ResultsMatchLeaderboard();
public class ResultsMatchLeaderboard : MatchLeaderboard
{
public ResultsMatchLeaderboard(Room room)
{
Room = room;
}
protected override bool FadeTop => true;
protected override LeaderboardScore CreateDrawableScore(APIRoomScoreInfo model, int index)

View File

@ -3,7 +3,6 @@
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Online.Multiplayer;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Ranking.Pages;
using osu.Game.Screens.Ranking;
@ -14,19 +13,17 @@ namespace osu.Game.Screens.Multi.Ranking.Types
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
private readonly Room room;
public RoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap, Room room)
public RoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
this.room = room;
}
public FontAwesome Icon => FontAwesome.fa_users;
public string Name => "Room Leaderboard";
public virtual ResultsPage CreatePage() => new RoomLeaderboardPage(score, beatmap, room);
public virtual ResultsPage CreatePage() => new RoomLeaderboardPage(score, beatmap);
}
}

View File

@ -1,106 +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.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Users;
namespace osu.Game.Screens.Multi
{
/// <summary>
/// Helper class which binds to values from a <see cref="Room"/>.
/// </summary>
public class RoomBindings
{
public RoomBindings()
{
Playlist.ItemsAdded += _ => updatePlaylist();
Playlist.ItemsRemoved += _ => updatePlaylist();
}
private Room room;
/// <summary>
/// The <see cref="Room"/> to bind to.
/// </summary>
public Room Room
{
get => room;
set
{
if (room == value)
return;
if (room != null)
{
RoomID.UnbindFrom(room.RoomID);
Name.UnbindFrom(room.Name);
Host.UnbindFrom(room.Host);
Status.UnbindFrom(room.Status);
Type.UnbindFrom(room.Type);
Playlist.UnbindFrom(room.Playlist);
Participants.UnbindFrom(room.Participants);
ParticipantCount.UnbindFrom(room.ParticipantCount);
MaxParticipants.UnbindFrom(room.MaxParticipants);
EndDate.UnbindFrom(room.EndDate);
Availability.UnbindFrom(room.Availability);
Duration.UnbindFrom(room.Duration);
}
room = value ?? new Room();
RoomID.BindTo(room.RoomID);
Name.BindTo(room.Name);
Host.BindTo(room.Host);
Status.BindTo(room.Status);
Type.BindTo(room.Type);
Playlist.BindTo(room.Playlist);
Participants.BindTo(room.Participants);
ParticipantCount.BindTo(room.ParticipantCount);
MaxParticipants.BindTo(room.MaxParticipants);
EndDate.BindTo(room.EndDate);
Availability.BindTo(room.Availability);
Duration.BindTo(room.Duration);
}
}
private void updatePlaylist()
{
// Todo: We only ever have one playlist item for now. In the future, this will be user-settable
var playlistItem = Playlist.FirstOrDefault();
currentBeatmap.Value = playlistItem?.Beatmap;
currentMods.Value = playlistItem?.RequiredMods ?? Enumerable.Empty<Mod>();
currentRuleset.Value = playlistItem?.Ruleset;
}
public readonly Bindable<int?> RoomID = new Bindable<int?>();
public readonly Bindable<string> Name = new Bindable<string>();
public readonly Bindable<User> Host = new Bindable<User>();
public readonly Bindable<RoomStatus> Status = new Bindable<RoomStatus>();
public readonly Bindable<GameType> Type = new Bindable<GameType>();
public readonly BindableList<PlaylistItem> Playlist = new BindableList<PlaylistItem>();
public readonly Bindable<IEnumerable<User>> Participants = new Bindable<IEnumerable<User>>();
public readonly Bindable<int> ParticipantCount = new Bindable<int>();
public readonly Bindable<int?> MaxParticipants = new Bindable<int?>();
public readonly Bindable<DateTimeOffset> EndDate = new Bindable<DateTimeOffset>();
public readonly Bindable<RoomAvailability> Availability = new Bindable<RoomAvailability>();
public readonly Bindable<TimeSpan> Duration = new Bindable<TimeSpan>();
private readonly Bindable<BeatmapInfo> currentBeatmap = new Bindable<BeatmapInfo>();
public IBindable<BeatmapInfo> CurrentBeatmap => currentBeatmap;
private readonly Bindable<IEnumerable<Mod>> currentMods = new Bindable<IEnumerable<Mod>>();
public IBindable<IEnumerable<Mod>> CurrentMods => currentMods;
private readonly Bindable<RulesetInfo> currentRuleset = new Bindable<RulesetInfo>();
public IBindable<RulesetInfo> CurrentRuleset => currentRuleset;
}
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Screens.Multi
private readonly BindableList<Room> rooms = new BindableList<Room>();
public IBindableList<Room> Rooms => rooms;
public readonly Bindable<Room> CurrentRoom = new Bindable<Room>();
public Bindable<Room> CurrentRoom { get; } = new Bindable<Room>();
[Resolved]
private APIAccess api { get; set; }
@ -51,6 +51,8 @@ namespace osu.Game.Screens.Multi
update(room, result);
addRoom(room);
CurrentRoom.Value = room;
RoomsUpdated?.Invoke();
onSuccess?.Invoke(room);

View File

@ -0,0 +1,36 @@
// 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.Game.Online.Multiplayer;
namespace osu.Game.Tests.Visual
{
public class MultiplayerTestCase : OsuTestCase
{
private Room room;
protected Room Room
{
get => room;
set
{
if (room == value)
return;
room = value;
if (dependencies != null)
dependencies.Model.Value = value;
}
}
private CachedModelDependencyContainer<Room> dependencies;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
dependencies = new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent));
dependencies.Model.Value = room;
return dependencies;
}
}
}