mirror of
https://github.com/ppy/osu.git
synced 2026-06-02 05:30:17 +08:00
Merge pull request #30634 from smoogipoo/multiplayer-remove-cmc-and-composite
Remove `CachedModelDependencyContainer` and `OnlinePlayComposite` from multiplayer
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1115.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1118.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
var newRoom = new Room();
|
||||
newRoom.CopyFrom(SelectedRoom.Value);
|
||||
|
||||
newRoom.RoomID.Value = null;
|
||||
newRoom.RoomID = null;
|
||||
MultiplayerClient.RoomSetupAction = room =>
|
||||
{
|
||||
room.State = MultiplayerRoomState.Playing;
|
||||
|
||||
@@ -39,18 +39,18 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
{
|
||||
var room = new Room
|
||||
{
|
||||
RoomID = { Value = 1234 },
|
||||
Name = { Value = "Daily Challenge: June 4, 2024" },
|
||||
RoomID = 1234,
|
||||
Name = "Daily Challenge: June 4, 2024",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First())
|
||||
{
|
||||
RequiredMods = [new APIMod(new OsuModTraceable())],
|
||||
AllowedMods = [new APIMod(new OsuModDoubleTime())]
|
||||
}
|
||||
},
|
||||
EndDate = { Value = DateTimeOffset.Now.AddHours(12) },
|
||||
Category = { Value = RoomCategory.DailyChallenge }
|
||||
],
|
||||
EndDate = DateTimeOffset.Now.AddHours(12),
|
||||
Category = RoomCategory.DailyChallenge
|
||||
};
|
||||
|
||||
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
|
||||
@@ -62,18 +62,18 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
{
|
||||
var room = new Room
|
||||
{
|
||||
RoomID = { Value = 1234 },
|
||||
Name = { Value = "Daily Challenge: June 4, 2024" },
|
||||
RoomID = 1234,
|
||||
Name = "Daily Challenge: June 4, 2024",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First())
|
||||
{
|
||||
RequiredMods = [new APIMod(new OsuModTraceable())],
|
||||
AllowedMods = [new APIMod(new OsuModDoubleTime())]
|
||||
}
|
||||
},
|
||||
EndDate = { Value = DateTimeOffset.Now.AddHours(12) },
|
||||
Category = { Value = RoomCategory.DailyChallenge }
|
||||
],
|
||||
EndDate = DateTimeOffset.Now.AddHours(12),
|
||||
Category = RoomCategory.DailyChallenge
|
||||
};
|
||||
|
||||
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
|
||||
@@ -91,18 +91,18 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
{
|
||||
var room = new Room
|
||||
{
|
||||
RoomID = { Value = 1234 },
|
||||
Name = { Value = "Daily Challenge: June 4, 2024" },
|
||||
RoomID = 1234,
|
||||
Name = "Daily Challenge: June 4, 2024",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(TestResources.CreateTestBeatmapSetInfo().Beatmaps.First())
|
||||
{
|
||||
RequiredMods = [new APIMod(new OsuModTraceable())],
|
||||
AllowedMods = [new APIMod(new OsuModDoubleTime())]
|
||||
}
|
||||
},
|
||||
EndDate = { Value = DateTimeOffset.Now.AddHours(12) },
|
||||
Category = { Value = RoomCategory.DailyChallenge }
|
||||
],
|
||||
EndDate = DateTimeOffset.Now.AddHours(12),
|
||||
Category = RoomCategory.DailyChallenge
|
||||
};
|
||||
|
||||
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
|
||||
|
||||
@@ -26,11 +26,6 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
|
||||
private readonly Bindable<Room> room = new Bindable<Room>(new Room());
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent))
|
||||
{
|
||||
Model = { BindTarget = room }
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void TestBasicAppearance()
|
||||
{
|
||||
@@ -98,7 +93,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DailyChallengeTimeRemainingRing(),
|
||||
new DailyChallengeTimeRemainingRing(room.Value),
|
||||
breakdown = new DailyChallengeScoreBreakdown(),
|
||||
}
|
||||
}
|
||||
@@ -125,8 +120,8 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
AddSliderStep("update time remaining", 0f, 1f, 0f, progress =>
|
||||
{
|
||||
var startedTimeAgo = TimeSpan.FromHours(24) * progress;
|
||||
room.Value.StartDate.Value = DateTimeOffset.Now - startedTimeAgo;
|
||||
room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1);
|
||||
room.Value.StartDate = DateTimeOffset.Now - startedTimeAgo;
|
||||
room.Value.EndDate = room.Value.StartDate.Value.AddDays(1);
|
||||
});
|
||||
AddStep("add normal score", () =>
|
||||
{
|
||||
|
||||
@@ -68,19 +68,19 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
{
|
||||
API.Perform(new CreateRoomRequest(room = new Room
|
||||
{
|
||||
RoomID = { Value = roomId },
|
||||
Name = { Value = "Daily Challenge: June 4, 2024" },
|
||||
RoomID = roomId,
|
||||
Name = "Daily Challenge: June 4, 2024",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(CreateAPIBeatmap(new OsuRuleset().RulesetInfo))
|
||||
{
|
||||
RequiredMods = [new APIMod(new OsuModTraceable())],
|
||||
AllowedMods = [new APIMod(new OsuModDoubleTime())]
|
||||
}
|
||||
},
|
||||
StartDate = { Value = DateTimeOffset.Now },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddHours(24) },
|
||||
Category = { Value = RoomCategory.DailyChallenge }
|
||||
],
|
||||
StartDate = DateTimeOffset.Now,
|
||||
EndDate = DateTimeOffset.Now.AddHours(24),
|
||||
Category = RoomCategory.DailyChallenge
|
||||
}));
|
||||
});
|
||||
AddStep("signal client", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo { RoomID = roomId }));
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
return false;
|
||||
};
|
||||
});
|
||||
AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = { Value = 1 } }, new PlaylistItem(Beatmap.Value.BeatmapInfo))
|
||||
AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = 1 }, new PlaylistItem(Beatmap.Value.BeatmapInfo))
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
@@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
return false;
|
||||
};
|
||||
});
|
||||
AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = { Value = 1 } }, new PlaylistItem(Beatmap.Value.BeatmapInfo))
|
||||
AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = 1 }, new PlaylistItem(Beatmap.Value.BeatmapInfo))
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
|
||||
@@ -18,11 +18,6 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
{
|
||||
private readonly Bindable<Room> room = new Bindable<Room>(new Room());
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent))
|
||||
{
|
||||
Model = { BindTarget = room }
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||
|
||||
@@ -38,7 +33,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background4,
|
||||
},
|
||||
ring = new DailyChallengeTimeRemainingRing
|
||||
ring = new DailyChallengeTimeRemainingRing(room.Value)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
@@ -59,29 +54,29 @@ namespace osu.Game.Tests.Visual.DailyChallenge
|
||||
|
||||
AddStep("just started", () =>
|
||||
{
|
||||
room.Value.StartDate.Value = DateTimeOffset.Now.AddMinutes(-1);
|
||||
room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1);
|
||||
room.Value.StartDate = DateTimeOffset.Now.AddMinutes(-1);
|
||||
room.Value.EndDate = room.Value.StartDate.Value.AddDays(1);
|
||||
});
|
||||
AddStep("midway through", () =>
|
||||
{
|
||||
room.Value.StartDate.Value = DateTimeOffset.Now.AddHours(-12);
|
||||
room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1);
|
||||
room.Value.StartDate = DateTimeOffset.Now.AddHours(-12);
|
||||
room.Value.EndDate = room.Value.StartDate.Value.AddDays(1);
|
||||
});
|
||||
AddStep("nearing end", () =>
|
||||
{
|
||||
room.Value.StartDate.Value = DateTimeOffset.Now.AddDays(-1).AddMinutes(8);
|
||||
room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1);
|
||||
room.Value.StartDate = DateTimeOffset.Now.AddDays(-1).AddMinutes(8);
|
||||
room.Value.EndDate = room.Value.StartDate.Value.AddDays(1);
|
||||
});
|
||||
AddStep("already ended", () =>
|
||||
{
|
||||
room.Value.StartDate.Value = DateTimeOffset.Now.AddDays(-2);
|
||||
room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1);
|
||||
room.Value.StartDate = DateTimeOffset.Now.AddDays(-2);
|
||||
room.Value.EndDate = room.Value.StartDate.Value.AddDays(1);
|
||||
});
|
||||
AddSliderStep("manual progress", 0f, 1f, 0f, progress =>
|
||||
{
|
||||
var startedTimeAgo = TimeSpan.FromHours(24) * progress;
|
||||
room.Value.StartDate.Value = DateTimeOffset.Now - startedTimeAgo;
|
||||
room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1);
|
||||
room.Value.StartDate = DateTimeOffset.Now - startedTimeAgo;
|
||||
room.Value.EndDate = room.Value.StartDate.Value.AddDays(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,14 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
beatmap.OnlineID = 1001;
|
||||
getRoomRequest.TriggerSuccess(new Room
|
||||
{
|
||||
RoomID = { Value = 1234 },
|
||||
Name = { Value = "Aug 8, 2024" },
|
||||
RoomID = 1234,
|
||||
Name = "Aug 8, 2024",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmap)
|
||||
},
|
||||
StartDate = { Value = DateTimeOffset.Now.AddMinutes(-30) },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddSeconds(60) }
|
||||
],
|
||||
StartDate = DateTimeOffset.Now.AddMinutes(-30),
|
||||
EndDate = DateTimeOffset.Now.AddSeconds(60)
|
||||
});
|
||||
return true;
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@@ -30,16 +28,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
protected abstract QueueMode Mode { get; }
|
||||
|
||||
protected BeatmapInfo InitialBeatmap { get; private set; }
|
||||
protected BeatmapInfo OtherBeatmap { get; private set; }
|
||||
protected BeatmapInfo InitialBeatmap { get; private set; } = null!;
|
||||
protected BeatmapInfo OtherBeatmap { get; private set; } = null!;
|
||||
|
||||
protected IScreen CurrentScreen => multiplayerComponents.CurrentScreen;
|
||||
protected IScreen CurrentSubScreen => multiplayerComponents.MultiplayerScreen.CurrentSubScreen;
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
private BeatmapSetInfo importedSet;
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
|
||||
private TestMultiplayerComponents multiplayerComponents;
|
||||
private TestMultiplayerComponents multiplayerComponents = null!;
|
||||
|
||||
protected TestMultiplayerClient MultiplayerClient => multiplayerComponents.MultiplayerClient;
|
||||
|
||||
@@ -75,15 +73,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for lounge", () => multiplayerComponents.ChildrenOfType<LoungeSubScreen>().SingleOrDefault()?.IsLoaded == true);
|
||||
AddStep("open room", () => multiplayerComponents.ChildrenOfType<LoungeSubScreen>().Single().Open(new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
QueueMode = { Value = Mode },
|
||||
Name = "Test Room",
|
||||
QueueMode = Mode,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(InitialBeatmap)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
|
||||
@@ -98,7 +96,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestCreatedWithCorrectMode()
|
||||
{
|
||||
AddUntilStep("room created with correct mode", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == Mode);
|
||||
AddUntilStep("room created with correct mode", () => MultiplayerClient.ClientAPIRoom?.QueueMode == Mode);
|
||||
}
|
||||
|
||||
protected void RunGameplay()
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -10,6 +8,7 @@ using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Testing;
|
||||
@@ -25,14 +24,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
private readonly Room room = new Room
|
||||
{
|
||||
HasPassword = { Value = true }
|
||||
Password = "*"
|
||||
};
|
||||
|
||||
[Cached]
|
||||
protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||
|
||||
private DrawableLoungeRoom drawableRoom;
|
||||
private SearchTextBox searchTextBox;
|
||||
private DrawableLoungeRoom drawableRoom = null!;
|
||||
private SearchTextBox searchTextBox = null!;
|
||||
|
||||
private readonly ManualResetEventSlim allowResponseCallback = new ManualResetEventSlim();
|
||||
|
||||
@@ -78,6 +77,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
SelectedRoom = new Bindable<Room?>()
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestFocusViaKeyboardCommit()
|
||||
{
|
||||
DrawableLoungeRoom.PasswordEntryPopover popover = null;
|
||||
DrawableLoungeRoom.PasswordEntryPopover? popover = null;
|
||||
|
||||
AddAssert("search textbox has focus", () => checkFocus(searchTextBox));
|
||||
AddStep("click room twice", () =>
|
||||
@@ -103,11 +103,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("enter password", () => popover.ChildrenOfType<OsuPasswordTextBox>().Single().Text = "password");
|
||||
AddStep("commit via enter", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddAssert("popover has focus", () => checkFocus(popover));
|
||||
AddAssert("popover has focus", () => checkFocus(popover!));
|
||||
|
||||
AddStep("attempt another enter", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddAssert("popover still has focus", () => checkFocus(popover));
|
||||
AddAssert("popover still has focus", () => checkFocus(popover!));
|
||||
|
||||
AddStep("unblock response", () => allowResponseCallback.Set());
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestFocusViaMouseCommit()
|
||||
{
|
||||
DrawableLoungeRoom.PasswordEntryPopover popover = null;
|
||||
DrawableLoungeRoom.PasswordEntryPopover? popover = null;
|
||||
|
||||
AddAssert("search textbox has focus", () => checkFocus(searchTextBox));
|
||||
AddStep("click room twice", () =>
|
||||
@@ -144,11 +144,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddAssert("popover has focus", () => checkFocus(popover));
|
||||
AddAssert("popover has focus", () => checkFocus(popover!));
|
||||
|
||||
AddStep("attempt another click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("popover still has focus", () => checkFocus(popover));
|
||||
AddAssert("popover still has focus", () => checkFocus(popover!));
|
||||
|
||||
AddStep("unblock response", () => allowResponseCallback.Set());
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@@ -32,15 +30,40 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Cached]
|
||||
protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||
|
||||
private readonly Bindable<Room> selectedRoom = new Bindable<Room>();
|
||||
private readonly Bindable<Room?> selectedRoom = new Bindable<Room?>();
|
||||
|
||||
[Test]
|
||||
public void TestMultipleStatuses()
|
||||
{
|
||||
FillFlowContainer rooms = null;
|
||||
FillFlowContainer rooms = null!;
|
||||
|
||||
AddStep("create rooms", () =>
|
||||
{
|
||||
PlaylistItem item1 = new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo = { StarRating = 2.5 }
|
||||
}.BeatmapInfo);
|
||||
|
||||
PlaylistItem item2 = new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo = { StarRating = 4.5 }
|
||||
}.BeatmapInfo);
|
||||
|
||||
PlaylistItem item3 = new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
StarRating = 2.5,
|
||||
Metadata =
|
||||
{
|
||||
Artist = "very very very very very very very very very long artist",
|
||||
ArtistUnicode = "very very very very very very very very very long artist",
|
||||
Title = "very very very very very very very very very very very long title",
|
||||
TitleUnicode = "very very very very very very very very very very very long title",
|
||||
}
|
||||
}
|
||||
}.BeatmapInfo);
|
||||
|
||||
Child = rooms = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@@ -52,86 +75,48 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createLoungeRoom(new Room
|
||||
{
|
||||
Name = { Value = "Multiplayer room" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
|
||||
Type = { Value = MatchType.HeadToHead },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
StarRating = 2.5
|
||||
}
|
||||
}.BeatmapInfo)
|
||||
}
|
||||
Name = "Multiplayer room",
|
||||
Status = new RoomStatusOpen(),
|
||||
EndDate = DateTimeOffset.Now.AddDays(1),
|
||||
Type = MatchType.HeadToHead,
|
||||
Playlist = [item1],
|
||||
CurrentPlaylistItem = item1
|
||||
}),
|
||||
createLoungeRoom(new Room
|
||||
{
|
||||
Name = { Value = "Private room" },
|
||||
Status = { Value = new RoomStatusOpenPrivate() },
|
||||
HasPassword = { Value = true },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
|
||||
Type = { Value = MatchType.HeadToHead },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
StarRating = 2.5,
|
||||
Metadata =
|
||||
{
|
||||
Artist = "very very very very very very very very very long artist",
|
||||
ArtistUnicode = "very very very very very very very very very long artist",
|
||||
Title = "very very very very very very very very very very very long title",
|
||||
TitleUnicode = "very very very very very very very very very very very long title",
|
||||
}
|
||||
}
|
||||
}.BeatmapInfo)
|
||||
}
|
||||
Name = "Private room",
|
||||
Status = new RoomStatusOpenPrivate(),
|
||||
Password = "*",
|
||||
EndDate = DateTimeOffset.Now.AddDays(1),
|
||||
Type = MatchType.HeadToHead,
|
||||
Playlist = [item3],
|
||||
CurrentPlaylistItem = item3
|
||||
}),
|
||||
createLoungeRoom(new Room
|
||||
{
|
||||
Name = { Value = "Playlist room with multiple beatmaps" },
|
||||
Status = { Value = new RoomStatusPlaying() },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
StarRating = 2.5
|
||||
}
|
||||
}.BeatmapInfo),
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
StarRating = 4.5
|
||||
}
|
||||
}.BeatmapInfo)
|
||||
}
|
||||
Name = "Playlist room with multiple beatmaps",
|
||||
Status = new RoomStatusPlaying(),
|
||||
EndDate = DateTimeOffset.Now.AddDays(1),
|
||||
Playlist = [item1, item2],
|
||||
CurrentPlaylistItem = item1
|
||||
}),
|
||||
createLoungeRoom(new Room
|
||||
{
|
||||
Name = { Value = "Finished room" },
|
||||
Status = { Value = new RoomStatusEnded() },
|
||||
EndDate = { Value = DateTimeOffset.Now },
|
||||
Name = "Finished room",
|
||||
Status = new RoomStatusEnded(),
|
||||
EndDate = DateTimeOffset.Now,
|
||||
}),
|
||||
createLoungeRoom(new Room
|
||||
{
|
||||
Name = { Value = "Spotlight room" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Category = { Value = RoomCategory.Spotlight },
|
||||
Name = "Spotlight room",
|
||||
Status = new RoomStatusOpen(),
|
||||
Category = RoomCategory.Spotlight,
|
||||
}),
|
||||
createLoungeRoom(new Room
|
||||
{
|
||||
Name = { Value = "Featured artist room" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Category = { Value = RoomCategory.FeaturedArtist },
|
||||
Name = "Featured artist room",
|
||||
Status = new RoomStatusOpen(),
|
||||
Category = RoomCategory.FeaturedArtist,
|
||||
}),
|
||||
}
|
||||
};
|
||||
@@ -145,24 +130,24 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestEnableAndDisablePassword()
|
||||
{
|
||||
DrawableRoom drawableRoom = null;
|
||||
Room room = null;
|
||||
DrawableRoom drawableRoom = null!;
|
||||
Room room = null!;
|
||||
|
||||
AddStep("create room", () => Child = drawableRoom = createLoungeRoom(room = new Room
|
||||
{
|
||||
Name = { Value = "Room with password" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Type = { Value = MatchType.HeadToHead },
|
||||
Name = "Room with password",
|
||||
Status = new RoomStatusOpen(),
|
||||
Type = MatchType.HeadToHead,
|
||||
}));
|
||||
|
||||
AddUntilStep("wait for panel load", () => drawableRoom.ChildrenOfType<DrawableRoomParticipantsList>().Any());
|
||||
|
||||
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
|
||||
AddStep("set password", () => room.Password.Value = "password");
|
||||
AddStep("set password", () => room.Password = "password");
|
||||
AddAssert("password icon visible", () => Precision.AlmostEquals(1, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
|
||||
AddStep("unset password", () => room.Password.Value = string.Empty);
|
||||
AddStep("unset password", () => room.Password = string.Empty);
|
||||
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
}
|
||||
|
||||
@@ -179,43 +164,52 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
new DrawableMatchRoom(new Room
|
||||
{
|
||||
Name = { Value = "A host-only room" },
|
||||
QueueMode = { Value = QueueMode.HostOnly },
|
||||
Type = { Value = MatchType.HeadToHead }
|
||||
}),
|
||||
Name = "A host-only room",
|
||||
QueueMode = QueueMode.HostOnly,
|
||||
Type = MatchType.HeadToHead,
|
||||
})
|
||||
{
|
||||
SelectedItem = new Bindable<PlaylistItem?>()
|
||||
},
|
||||
new DrawableMatchRoom(new Room
|
||||
{
|
||||
Name = { Value = "An all-players, team-versus room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayers },
|
||||
Type = { Value = MatchType.TeamVersus }
|
||||
}),
|
||||
Name = "An all-players, team-versus room",
|
||||
QueueMode = QueueMode.AllPlayers,
|
||||
Type = MatchType.TeamVersus
|
||||
})
|
||||
{
|
||||
SelectedItem = new Bindable<PlaylistItem?>()
|
||||
},
|
||||
new DrawableMatchRoom(new Room
|
||||
{
|
||||
Name = { Value = "A round-robin room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayersRoundRobin },
|
||||
Type = { Value = MatchType.HeadToHead }
|
||||
}),
|
||||
Name = "A round-robin room",
|
||||
QueueMode = QueueMode.AllPlayersRoundRobin,
|
||||
Type = MatchType.HeadToHead
|
||||
})
|
||||
{
|
||||
SelectedItem = new Bindable<PlaylistItem?>()
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DrawableRoom createLoungeRoom(Room room)
|
||||
{
|
||||
room.Host.Value ??= new APIUser { Username = "peppy", Id = 2 };
|
||||
room.Host ??= new APIUser { Username = "peppy", Id = 2 };
|
||||
|
||||
if (room.RecentParticipants.Count == 0)
|
||||
{
|
||||
room.RecentParticipants.AddRange(Enumerable.Range(0, 20).Select(i => new APIUser
|
||||
room.RecentParticipants = Enumerable.Range(0, 20).Select(i => new APIUser
|
||||
{
|
||||
Id = i,
|
||||
Username = $"User {i}"
|
||||
}));
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
return new DrawableLoungeRoom(room)
|
||||
{
|
||||
MatchingFilter = true,
|
||||
SelectedRoom = { BindTarget = selectedRoom }
|
||||
SelectedRoom = selectedRoom
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -17,7 +15,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneDrawableRoomParticipantsList : OnlinePlayTestScene
|
||||
{
|
||||
private DrawableRoomParticipantsList list;
|
||||
private DrawableRoomParticipantsList list = null!;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
@@ -27,18 +25,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
SelectedRoom.Value = new Room
|
||||
{
|
||||
Name = { Value = "test room" },
|
||||
Host =
|
||||
Name = "test room",
|
||||
Host = new APIUser
|
||||
{
|
||||
Value = new APIUser
|
||||
{
|
||||
Id = 2,
|
||||
Username = "peppy",
|
||||
}
|
||||
Id = 2,
|
||||
Username = "peppy",
|
||||
}
|
||||
};
|
||||
|
||||
Child = list = new DrawableRoomParticipantsList
|
||||
Child = list = new DrawableRoomParticipantsList(SelectedRoom.Value)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -143,18 +138,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void addUser(int id)
|
||||
{
|
||||
SelectedRoom.Value.RecentParticipants.Add(new APIUser
|
||||
SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Append(new APIUser
|
||||
{
|
||||
Id = id,
|
||||
Username = $"User {id}"
|
||||
});
|
||||
SelectedRoom.Value.ParticipantCount.Value++;
|
||||
}).ToArray();
|
||||
SelectedRoom.Value.ParticipantCount++;
|
||||
}
|
||||
|
||||
private void removeUserAt(int index)
|
||||
{
|
||||
SelectedRoom.Value.RecentParticipants.RemoveAt(index);
|
||||
SelectedRoom.Value.ParticipantCount.Value--;
|
||||
SelectedRoom.Value.RecentParticipants = SelectedRoom.Value.RecentParticipants.Where(u => !u.Equals(SelectedRoom.Value.RecentParticipants[index])).ToArray();
|
||||
SelectedRoom.Value.ParticipantCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
QueueMode = QueueMode.AllPlayers
|
||||
}).WaitSafely());
|
||||
|
||||
AddUntilStep("api room updated", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("api room updated", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -55,20 +55,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("has 5 rooms", () => container.Rooms.Count == 5);
|
||||
|
||||
AddAssert("all spotlights at top", () => container.Rooms
|
||||
.SkipWhile(r => r.Room.Category.Value == RoomCategory.Spotlight)
|
||||
.All(r => r.Room.Category.Value == RoomCategory.Normal));
|
||||
.SkipWhile(r => r.Room.Category == RoomCategory.Spotlight)
|
||||
.All(r => r.Room.Category == RoomCategory.Normal));
|
||||
|
||||
AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.First(r => r.RoomID.Value == 0)));
|
||||
AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.First(r => r.RoomID == 0)));
|
||||
AddAssert("has 4 rooms", () => container.Rooms.Count == 4);
|
||||
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
|
||||
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID != 0));
|
||||
|
||||
AddStep("select first room", () => container.Rooms.First().TriggerClick());
|
||||
AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight)));
|
||||
AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
|
||||
|
||||
AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID?.Value)));
|
||||
AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight)));
|
||||
AddStep("remove last room", () => RoomManager.RemoveRoom(RoomManager.Rooms.MinBy(r => r.RoomID)));
|
||||
AddAssert("first spotlight still selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category == RoomCategory.Spotlight)));
|
||||
|
||||
AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category.Value == RoomCategory.Spotlight)));
|
||||
AddStep("remove spotlight room", () => RoomManager.RemoveRoom(RoomManager.Rooms.Single(r => r.Category == RoomCategory.Spotlight)));
|
||||
AddAssert("selection vacated", () => checkRoomSelected(null));
|
||||
}
|
||||
|
||||
@@ -182,11 +182,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("filter public rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Public });
|
||||
|
||||
AddUntilStep("private room hidden", () => container.Rooms.All(r => !r.Room.HasPassword.Value));
|
||||
AddUntilStep("private room hidden", () => container.Rooms.All(r => !r.Room.HasPassword));
|
||||
|
||||
AddStep("filter private rooms", () => container.Filter.Value = new FilterCriteria { Permissions = RoomPermissionsFilter.Private });
|
||||
|
||||
AddUntilStep("public room hidden", () => container.Rooms.All(r => r.Room.HasPassword.Value));
|
||||
AddUntilStep("public room hidden", () => container.Rooms.All(r => r.Room.HasPassword));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,6 +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.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
@@ -23,7 +24,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new MatchBeatmapDetailArea
|
||||
Child = new MatchBeatmapDetailArea(SelectedRoom.Value)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -35,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void createNewItem()
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
SelectedRoom.Value.Playlist = SelectedRoom.Value.Playlist.Append(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
ID = SelectedRoom.Value.Playlist.Count,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
@@ -45,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
new APIMod(new OsuModDoubleTime()),
|
||||
new APIMod(new OsuModAutoplay())
|
||||
}
|
||||
});
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room { RoomID = { Value = 3 } };
|
||||
SelectedRoom.Value = new Room { RoomID = 3 };
|
||||
|
||||
Child = new MatchLeaderboard
|
||||
Child = new MatchLeaderboard(SelectedRoom.Value)
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
|
||||
@@ -43,12 +43,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private OsuButton readyButton => control.ChildrenOfType<OsuButton>().Single();
|
||||
|
||||
[Cached(typeof(IBindable<PlaylistItem>))]
|
||||
private readonly Bindable<PlaylistItem> currentItem = new Bindable<PlaylistItem>();
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
|
||||
new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent)) { Model = { BindTarget = room } };
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@@ -107,31 +101,33 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
PlaylistItem item = null!;
|
||||
|
||||
AddStep("reset state", () =>
|
||||
{
|
||||
multiplayerClient.Invocations.Clear();
|
||||
|
||||
beatmapAvailability.Value = BeatmapAvailability.LocallyAvailable();
|
||||
|
||||
currentItem.Value = new PlaylistItem(Beatmap.Value.BeatmapInfo)
|
||||
item = new PlaylistItem(Beatmap.Value.BeatmapInfo)
|
||||
{
|
||||
RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID
|
||||
};
|
||||
|
||||
room.Value = new Room
|
||||
{
|
||||
Playlist = { currentItem.Value },
|
||||
CurrentPlaylistItem = { BindTarget = currentItem }
|
||||
Playlist = [item],
|
||||
CurrentPlaylistItem = item
|
||||
};
|
||||
|
||||
localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id) { User = API.LocalUser.Value };
|
||||
localUser = new MultiplayerRoomUser(API.LocalUser.Value.Id)
|
||||
{
|
||||
User = API.LocalUser.Value
|
||||
};
|
||||
|
||||
multiplayerRoom = new MultiplayerRoom(0)
|
||||
{
|
||||
Playlist =
|
||||
{
|
||||
TestMultiplayerClient.CreateMultiplayerPlaylistItem(currentItem.Value),
|
||||
},
|
||||
Playlist = { TestMultiplayerClient.CreateMultiplayerPlaylistItem(item) },
|
||||
Users = { localUser },
|
||||
Host = localUser,
|
||||
};
|
||||
@@ -144,6 +140,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(250, 50),
|
||||
SelectedItem = new Bindable<PlaylistItem?>(item)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -103,14 +103,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddRepeatStep("random stuff happens", performRandomAction, 30);
|
||||
@@ -238,17 +238,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1);
|
||||
AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount == 1);
|
||||
AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
|
||||
}
|
||||
|
||||
@@ -259,14 +259,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
}, API.LocalUser.Value);
|
||||
});
|
||||
|
||||
@@ -288,14 +288,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
}, API.LocalUser.Value);
|
||||
});
|
||||
|
||||
@@ -308,7 +308,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
|
||||
AddUntilStep("wait for join", () => multiplayerClient.RoomJoined);
|
||||
|
||||
AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount.Value == 1);
|
||||
AddUntilStep("Check participant count correct", () => multiplayerClient.ClientAPIRoom?.ParticipantCount == 1);
|
||||
AddUntilStep("Check participant list contains user", () => multiplayerClient.ClientAPIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
|
||||
}
|
||||
|
||||
@@ -317,18 +317,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Password = { Value = "password" },
|
||||
Name = "Test Room",
|
||||
Password = "password",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddUntilStep("room has password", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password");
|
||||
AddUntilStep("room has password", () => multiplayerClient.ClientAPIRoom?.Password == "password");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -338,15 +338,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Password = { Value = "password" },
|
||||
Name = "Test Room",
|
||||
Password = "password",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
}, API.LocalUser.Value);
|
||||
});
|
||||
|
||||
@@ -370,19 +370,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Password = { Value = "password" },
|
||||
Name = "Test Room",
|
||||
Password = "password",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("change password", () => multiplayerClient.ChangeSettings(password: "password2"));
|
||||
AddUntilStep("local password changed", () => multiplayerClient.ClientAPIRoom?.Password.Value == "password2");
|
||||
AddUntilStep("local password changed", () => multiplayerClient.ClientAPIRoom?.Password == "password2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -401,14 +401,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
pressReadyButton();
|
||||
@@ -430,8 +430,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
};
|
||||
return new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Playlist = { item }
|
||||
Name = "Test Room",
|
||||
Playlist = [item]
|
||||
};
|
||||
});
|
||||
|
||||
@@ -471,8 +471,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
};
|
||||
return new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Playlist = { item }
|
||||
Name = "Test Room",
|
||||
Playlist = [item]
|
||||
};
|
||||
});
|
||||
|
||||
@@ -512,8 +512,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
};
|
||||
return new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Playlist = { item }
|
||||
Name = "Test Room",
|
||||
Playlist = [item]
|
||||
};
|
||||
});
|
||||
|
||||
@@ -548,14 +548,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("join other user (ready, host)", () =>
|
||||
@@ -581,14 +581,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("delete beatmap", () => beatmaps.Delete(importedSet));
|
||||
@@ -620,14 +620,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("disconnect", () => multiplayerClient.Disconnect());
|
||||
@@ -639,15 +639,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = new[] { new APIMod(new OsuModHidden()) }
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("open mod overlay", () => this.ChildrenOfType<UserModSelectButton>().Single().TriggerClick());
|
||||
@@ -679,14 +679,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
enterGameplay();
|
||||
@@ -724,14 +724,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
enterGameplay();
|
||||
@@ -754,14 +754,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
pressReadyButton();
|
||||
@@ -791,15 +791,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayers },
|
||||
Name = "Test Room",
|
||||
QueueMode = QueueMode.AllPlayers,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
}, API.LocalUser.Value);
|
||||
});
|
||||
|
||||
@@ -810,12 +810,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("disable polling", () => this.ChildrenOfType<ListingPollingComponent>().Single().TimeBetweenPolls.Value = 0);
|
||||
AddStep("change server-side settings", () =>
|
||||
{
|
||||
roomManager.ServerSideRooms[0].Name.Value = "New name";
|
||||
roomManager.ServerSideRooms[0].Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
ID = 2,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
});
|
||||
roomManager.ServerSideRooms[0].Name = "New name";
|
||||
roomManager.ServerSideRooms[0].Playlist =
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
ID = 2,
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
AddStep("join room", () => InputManager.Key(Key.Enter));
|
||||
@@ -825,8 +828,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("local room has correct settings", () =>
|
||||
{
|
||||
var localRoom = this.ChildrenOfType<MultiplayerMatchSubScreen>().Single().Room;
|
||||
return localRoom.Name.Value == roomManager.ServerSideRooms[0].Name.Value
|
||||
&& localRoom.Playlist.SequenceEqual(roomManager.ServerSideRooms[0].Playlist);
|
||||
return localRoom.Name == roomManager.ServerSideRooms[0].Name && localRoom.Playlist.Single().ID == 2;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -836,15 +838,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayers },
|
||||
Name = "Test Room",
|
||||
QueueMode = QueueMode.AllPlayers,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("set spectating state", () => multiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating));
|
||||
@@ -872,15 +874,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayers },
|
||||
Name = "Test Room",
|
||||
QueueMode = QueueMode.AllPlayers,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("set spectating state", () => multiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Spectating));
|
||||
@@ -911,15 +913,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayers },
|
||||
Name = "Test Room",
|
||||
QueueMode = QueueMode.AllPlayers,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
enterGameplay();
|
||||
@@ -942,15 +944,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayers },
|
||||
Name = "Test Room",
|
||||
QueueMode = QueueMode.AllPlayers,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
enterGameplay();
|
||||
@@ -976,14 +978,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("join other user and make host", () =>
|
||||
@@ -1022,10 +1024,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayers },
|
||||
Name = "Test Room",
|
||||
QueueMode = QueueMode.AllPlayers,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
@@ -1036,7 +1038,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = new[] { new APIMod { Acronym = "HD" } },
|
||||
},
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddStep("select hidden", () => multiplayerClient.ChangeUserMods(new[] { new APIMod { Acronym = "HD" } }));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -13,9 +12,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerMatchFooter : MultiplayerTestScene
|
||||
{
|
||||
[Cached(typeof(IBindable<PlaylistItem>))]
|
||||
private readonly Bindable<PlaylistItem> currentItem = new Bindable<PlaylistItem>();
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
@@ -33,7 +29,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 50,
|
||||
Child = new MultiplayerMatchFooter()
|
||||
Child = new MultiplayerMatchFooter
|
||||
{
|
||||
SelectedItem = new Bindable<PlaylistItem?>()
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("load match", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room { Name = { Value = "Test Room" } };
|
||||
SelectedRoom.Value = new Room { Name = "Test Room" };
|
||||
LoadScreen(screen = new TestMultiplayerMatchSubScreen(SelectedRoom.Value));
|
||||
});
|
||||
|
||||
@@ -77,33 +77,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 1.5%
|
||||
*
|
||||
* TearDown : System.AggregateException : One or more errors occurred. (Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index'))
|
||||
----> System.ArgumentOutOfRangeException : Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
|
||||
* --TearDown
|
||||
* at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
|
||||
* at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
|
||||
* at osu.Framework.Extensions.TaskExtensions.WaitSafely(Task task)
|
||||
* at osu.Framework.Testing.TestScene.checkForErrors()
|
||||
* at osu.Framework.Testing.TestScene.RunTestsFromNUnit()
|
||||
*--ArgumentOutOfRangeException
|
||||
* at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
|
||||
* at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
|
||||
* at osu.Framework.Bindables.BindableList`1.removeAt(Int32 index, BindableList`1 caller)
|
||||
* at osu.Game.Online.Multiplayer.MultiplayerClient.<>c__DisplayClass106_0.<PlaylistItemChanged>b__0() in C:\BuildAgent\work\ecd860037212ac52\osu.Game\Online\Multiplayer\MultiplayerClient .cs:line 702
|
||||
* at osu.Framework.Threading.ScheduledDelegate.RunTaskInternal()
|
||||
*/
|
||||
public void TestCreatedRoom()
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
});
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
@@ -112,16 +96,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestTaikoOnlyMod()
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = new[] { new APIMod(new TaikoModSwap()) }
|
||||
});
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new TaikoRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new TaikoRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = new[] { new APIMod(new TaikoModSwap()) }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
@@ -133,32 +119,36 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestSettingValidity()
|
||||
{
|
||||
AddAssert("create button not enabled", () => !this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
});
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
AddAssert("create button enabled", () => this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestStartMatchWhileSpectating()
|
||||
{
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
});
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
@@ -179,16 +169,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestFreeModSelectionHasAllowedMods()
|
||||
{
|
||||
AddStep("add playlist item with allowed mod", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
|
||||
});
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
@@ -206,16 +198,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestModSelectKeyWithAllowedMods()
|
||||
{
|
||||
AddStep("add playlist item with allowed mod", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
|
||||
});
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
AllowedMods = new[] { new APIMod(new OsuModDoubleTime()) }
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
@@ -228,15 +222,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestModSelectKeyWithNoAllowedMods()
|
||||
{
|
||||
AddStep("add playlist item with no allowed mods", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
});
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
];
|
||||
});
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
|
||||
@@ -249,13 +245,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestNextPlaylistItemSelectedAfterCompletion()
|
||||
{
|
||||
AddStep("add two playlist items", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.AddRange(new[]
|
||||
{
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First()).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
@@ -264,7 +259,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
});
|
||||
];
|
||||
});
|
||||
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
@@ -286,24 +281,26 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest] // See above
|
||||
public void TestModSelectOverlay()
|
||||
{
|
||||
AddStep("add playlist item", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
RequiredMods = new[]
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
|
||||
{
|
||||
new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 2.0 } }),
|
||||
new APIMod(new OsuModStrictTracking()),
|
||||
},
|
||||
AllowedMods = new[]
|
||||
{
|
||||
new APIMod(new OsuModFlashlight()),
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
RequiredMods = new[]
|
||||
{
|
||||
new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 2.0 } }),
|
||||
new APIMod(new OsuModStrictTracking()),
|
||||
},
|
||||
AllowedMods = new[]
|
||||
{
|
||||
new APIMod(new OsuModFlashlight()),
|
||||
}
|
||||
}
|
||||
});
|
||||
];
|
||||
});
|
||||
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -22,7 +20,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerPlayer : MultiplayerTestScene
|
||||
{
|
||||
private MultiplayerPlayer player;
|
||||
private MultiplayerPlayer player = null!;
|
||||
|
||||
[Test]
|
||||
public void TestGameplay()
|
||||
@@ -49,7 +47,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("score changed", () => player.GameplayState.ScoreProcessor.TotalScore.Value > 0);
|
||||
}
|
||||
|
||||
private void setup(Func<IReadOnlyList<Mod>> mods = null)
|
||||
private void setup(Func<IReadOnlyList<Mod>>? mods = null)
|
||||
{
|
||||
AddStep("set beatmap", () =>
|
||||
{
|
||||
@@ -64,10 +62,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("initialise gameplay", () =>
|
||||
{
|
||||
Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ServerAPIRoom, new PlaylistItem(Beatmap.Value.BeatmapInfo)
|
||||
Stack.Push(player = new MultiplayerPlayer(MultiplayerClient.ServerAPIRoom!, new PlaylistItem(Beatmap.Value.BeatmapInfo)
|
||||
{
|
||||
RulesetID = Beatmap.Value.BeatmapInfo.Ruleset.OnlineID,
|
||||
}, MultiplayerClient.ServerRoom?.Users.ToArray()));
|
||||
}, MultiplayerClient.ServerRoom!.Users.ToArray()));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen() && player.IsLoaded);
|
||||
|
||||
@@ -28,9 +28,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerPlaylist : MultiplayerTestScene
|
||||
{
|
||||
[Cached(typeof(IBindable<PlaylistItem>))]
|
||||
private readonly Bindable<PlaylistItem> currentItem = new Bindable<PlaylistItem>();
|
||||
|
||||
private MultiplayerPlaylist list = null!;
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
@@ -51,12 +48,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create list", () =>
|
||||
{
|
||||
Child = list = new MultiplayerPlaylist
|
||||
Child = list = new MultiplayerPlaylist(SelectedRoom.Value)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0.4f, 0.8f)
|
||||
Size = new Vector2(0.4f, 0.8f),
|
||||
SelectedItem = new Bindable<PlaylistItem?>()
|
||||
};
|
||||
});
|
||||
|
||||
@@ -166,9 +164,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
RoomManager.CreateRoom(new Room
|
||||
{
|
||||
Name = { Value = "test name" },
|
||||
Name = "test name",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
|
||||
{
|
||||
RulesetID = Ruleset.Value.OnlineID
|
||||
@@ -178,7 +176,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
RulesetID = Ruleset.Value.OnlineID,
|
||||
Expired = true
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@@ -27,10 +25,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerQueueList : MultiplayerTestScene
|
||||
{
|
||||
private MultiplayerQueueList playlist;
|
||||
private BeatmapManager beatmaps;
|
||||
private BeatmapSetInfo importedSet;
|
||||
private BeatmapInfo importedBeatmap;
|
||||
private MultiplayerQueueList playlist = null!;
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
private BeatmapInfo importedBeatmap = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@@ -46,12 +44,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create playlist", () =>
|
||||
{
|
||||
Child = playlist = new MultiplayerQueueList
|
||||
Child = playlist = new MultiplayerQueueList(SelectedRoom.Value)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 300),
|
||||
Items = { BindTarget = MultiplayerClient.ClientAPIRoom!.Playlist }
|
||||
};
|
||||
|
||||
MultiplayerClient.ClientAPIRoom!.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.Playlist))
|
||||
playlist.Items.ReplaceRange(0, playlist.Items.Count, MultiplayerClient.ClientAPIRoom.Playlist);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -69,7 +72,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestDeleteButtonAlwaysVisibleForHost()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
|
||||
|
||||
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
|
||||
assertDeleteButtonVisibility(1, true);
|
||||
@@ -81,7 +84,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestDeleteButtonOnlyVisibleForItemOwnerIfNotHost()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
|
||||
|
||||
AddStep("join other user", () => MultiplayerClient.AddUser(new APIUser { Id = 1234 }));
|
||||
AddStep("set other user as host", () => MultiplayerClient.TransferHost(1234));
|
||||
@@ -100,7 +103,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestSingleItemDoesNotHaveDeleteButton()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
|
||||
|
||||
assertDeleteButtonVisibility(0, false);
|
||||
}
|
||||
@@ -109,7 +112,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestCurrentItemHasDeleteButtonIfNotSingle()
|
||||
{
|
||||
AddStep("set all players queue mode", () => MultiplayerClient.ChangeSettings(new MultiplayerRoomSettings { QueueMode = QueueMode.AllPlayers }).WaitSafely());
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode.Value == QueueMode.AllPlayers);
|
||||
AddUntilStep("wait for queue mode change", () => MultiplayerClient.ClientAPIRoom?.QueueMode == QueueMode.AllPlayers);
|
||||
|
||||
addPlaylistItem(() => API.LocalUser.Value.OnlineID);
|
||||
|
||||
|
||||
@@ -26,9 +26,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerSpectateButton : MultiplayerTestScene
|
||||
{
|
||||
[Cached(typeof(IBindable<PlaylistItem>))]
|
||||
private readonly Bindable<PlaylistItem> currentItem = new Bindable<PlaylistItem>();
|
||||
|
||||
private MultiplayerSpectateButton spectateButton = null!;
|
||||
private MatchStartControl startControl = null!;
|
||||
|
||||
@@ -51,13 +48,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("create button", () =>
|
||||
{
|
||||
AvailabilityTracker.SelectedItem.BindTo(currentItem);
|
||||
PlaylistItem item = SelectedRoom.Value.Playlist.First();
|
||||
|
||||
AvailabilityTracker.SelectedItem.Value = item;
|
||||
|
||||
importedSet = beatmaps.GetAllUsableBeatmapSets().First();
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First());
|
||||
|
||||
currentItem.Value = SelectedRoom.Value.Playlist.First();
|
||||
|
||||
Child = new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@@ -72,12 +69,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50),
|
||||
SelectedItem = new Bindable<PlaylistItem?>(item)
|
||||
},
|
||||
startControl = new MatchStartControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 50),
|
||||
SelectedItem = new Bindable<PlaylistItem?>(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,14 +74,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestItemAddedWhenCreateNewItemClicked()
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestItemNotAddedIfExistingOnStart()
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("finalise selection", () => songSelect.FinaliseSelection());
|
||||
AddAssert("playlist has 1 item", () => SelectedRoom.Value.Playlist.Count == 1);
|
||||
}
|
||||
@@ -89,24 +89,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestAddSameItemMultipleTimes()
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddAssert("playlist has 2 items", () => SelectedRoom.Value.Playlist.Count == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddItemAfterRearrangement()
|
||||
{
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("rearrange", () =>
|
||||
{
|
||||
var item = SelectedRoom.Value.Playlist[0];
|
||||
SelectedRoom.Value.Playlist.RemoveAt(0);
|
||||
SelectedRoom.Value.Playlist.Add(item);
|
||||
});
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("rearrange", () => SelectedRoom.Value.Playlist = SelectedRoom.Value.Playlist.Skip(1).Append(SelectedRoom.Value.Playlist[0]).ToArray());
|
||||
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create new item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddAssert("new item has id 2", () => SelectedRoom.Value.Playlist.Last().ID == 2);
|
||||
}
|
||||
|
||||
@@ -117,9 +112,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestNewItemHasNewModInstances()
|
||||
{
|
||||
AddStep("set dt mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime() });
|
||||
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
AddStep("change mod rate", () => ((OsuModDoubleTime)SelectedMods.Value[0]).SpeedChange.Value = 2);
|
||||
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
|
||||
AddAssert("item 1 has rate 1.5", () =>
|
||||
{
|
||||
@@ -150,7 +145,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
mod = (OsuModDoubleTime)SelectedMods.Value[0];
|
||||
});
|
||||
|
||||
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
|
||||
AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem!());
|
||||
|
||||
AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
|
||||
AddAssert("item has rate 1.5", () =>
|
||||
|
||||
@@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
@@ -18,10 +17,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
private readonly Mock<MultiplayerClient> multiplayerClient = new Mock<MultiplayerClient>();
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) =>
|
||||
// not used directly in component, but required due to it inheriting from OnlinePlayComposite.
|
||||
new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent));
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new StarRatingRangeDisplay
|
||||
Child = new StarRatingRangeDisplay(SelectedRoom.Value)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
@@ -33,11 +33,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.AddRange(new[]
|
||||
{
|
||||
SelectedRoom.Value.Playlist =
|
||||
[
|
||||
new PlaylistItem(new BeatmapInfo { StarRating = min }),
|
||||
new PlaylistItem(new BeatmapInfo { StarRating = max }),
|
||||
});
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@@ -29,10 +27,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneTeamVersus : ScreenTestScene
|
||||
{
|
||||
private BeatmapManager beatmaps;
|
||||
private BeatmapSetInfo importedSet;
|
||||
private BeatmapManager beatmaps = null!;
|
||||
private BeatmapSetInfo importedSet = null!;
|
||||
|
||||
private TestMultiplayerComponents multiplayerComponents;
|
||||
private TestMultiplayerComponents multiplayerComponents = null!;
|
||||
|
||||
private TestMultiplayerClient multiplayerClient => multiplayerComponents.MultiplayerClient;
|
||||
|
||||
@@ -64,15 +62,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Type = { Value = MatchType.TeamVersus },
|
||||
Name = "Test Room",
|
||||
Type = MatchType.TeamVersus,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddUntilStep("room type is team vs", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.TeamVersus);
|
||||
@@ -84,15 +82,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Type = { Value = MatchType.TeamVersus },
|
||||
Name = "Test Room",
|
||||
Type = MatchType.TeamVersus,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddUntilStep("user on team 0", () => (multiplayerClient.ClientRoom?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 0);
|
||||
@@ -121,25 +119,25 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Type = { Value = MatchType.HeadToHead },
|
||||
Name = "Test Room",
|
||||
Type = MatchType.HeadToHead,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddUntilStep("match type head to head", () => multiplayerClient.ClientAPIRoom?.Type.Value == MatchType.HeadToHead);
|
||||
AddUntilStep("match type head to head", () => multiplayerClient.ClientAPIRoom?.Type == MatchType.HeadToHead);
|
||||
|
||||
AddStep("change match type", () => multiplayerClient.ChangeSettings(new MultiplayerRoomSettings
|
||||
{
|
||||
MatchType = MatchType.TeamVersus
|
||||
}).WaitSafely());
|
||||
|
||||
AddUntilStep("api room updated to team versus", () => multiplayerClient.ClientAPIRoom?.Type.Value == MatchType.TeamVersus);
|
||||
AddUntilStep("api room updated to team versus", () => multiplayerClient.ClientAPIRoom?.Type == MatchType.TeamVersus);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -147,14 +145,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Name = "Test Room",
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo)
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
AddUntilStep("room type is head to head", () => multiplayerClient.ClientRoom?.Settings.MatchType == MatchType.HeadToHead);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
@@ -22,7 +20,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
|
||||
|
||||
private TestRoomSettings settings;
|
||||
private TestRoomSettings settings = null!;
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
|
||||
@@ -47,19 +45,19 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("clear name and beatmap", () =>
|
||||
{
|
||||
SelectedRoom.Value.Name.Value = "";
|
||||
SelectedRoom.Value.Playlist.Clear();
|
||||
SelectedRoom.Value.Name = "";
|
||||
SelectedRoom.Value.Playlist = [];
|
||||
});
|
||||
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("set name", () => SelectedRoom.Value.Name.Value = "Room name");
|
||||
AddStep("set name", () => SelectedRoom.Value.Name = "Room name");
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("set beatmap", () => SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)));
|
||||
AddStep("set beatmap", () => SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)]);
|
||||
AddAssert("button enabled", () => settings.ApplyButton.Enabled.Value);
|
||||
|
||||
AddStep("clear name", () => SelectedRoom.Value.Name.Value = "");
|
||||
AddStep("clear name", () => SelectedRoom.Value.Name = "");
|
||||
AddAssert("button disabled", () => !settings.ApplyButton.Enabled.Value);
|
||||
}
|
||||
|
||||
@@ -69,13 +67,13 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
const string expected_name = "expected name";
|
||||
TimeSpan expectedDuration = TimeSpan.FromMinutes(15);
|
||||
|
||||
Room createdRoom = null;
|
||||
Room createdRoom = null!;
|
||||
|
||||
AddStep("setup", () =>
|
||||
{
|
||||
settings.NameField.Current.Value = expected_name;
|
||||
settings.DurationField.Current.Value = expectedDuration;
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo));
|
||||
SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
|
||||
|
||||
RoomManager.CreateRequested = r =>
|
||||
{
|
||||
@@ -85,8 +83,8 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
});
|
||||
|
||||
AddStep("create room", () => settings.ApplyButton.Action.Invoke());
|
||||
AddAssert("has correct name", () => createdRoom.Name.Value == expected_name);
|
||||
AddAssert("has correct duration", () => createdRoom.Duration.Value == expectedDuration);
|
||||
AddAssert("has correct name", () => createdRoom.Name == expected_name);
|
||||
AddAssert("has correct duration", () => createdRoom.Duration == expectedDuration);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -94,14 +92,14 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
const string not_found_prefix = "beatmaps not found:";
|
||||
|
||||
string errorMessage = null;
|
||||
string errorMessage = null!;
|
||||
|
||||
AddStep("setup", () =>
|
||||
{
|
||||
var beatmap = CreateBeatmap(Ruleset.Value).BeatmapInfo;
|
||||
|
||||
SelectedRoom.Value.Name.Value = "Test Room";
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(beatmap));
|
||||
SelectedRoom.Value.Name = "Test Room";
|
||||
SelectedRoom.Value.Playlist = [new PlaylistItem(beatmap)];
|
||||
|
||||
errorMessage = $"{not_found_prefix} {beatmap.OnlineID}";
|
||||
|
||||
@@ -127,8 +125,8 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
AddStep("setup", () =>
|
||||
{
|
||||
SelectedRoom.Value.Name.Value = "Test Room";
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo));
|
||||
SelectedRoom.Value.Name = "Test Room";
|
||||
SelectedRoom.Value.Playlist = [new PlaylistItem(CreateBeatmap(Ruleset.Value).BeatmapInfo)];
|
||||
|
||||
RoomManager.CreateRequested = _ => failText;
|
||||
});
|
||||
@@ -169,7 +167,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
protected class TestRoomManager : IRoomManager
|
||||
{
|
||||
public Func<Room, string> CreateRequested;
|
||||
public Func<Room, string>? CreateRequested;
|
||||
|
||||
public event Action RoomsUpdated
|
||||
{
|
||||
@@ -187,7 +185,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
public void ClearRooms() => throw new NotImplementedException();
|
||||
|
||||
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
public void CreateRoom(Room room, Action<Room>? onSuccess = null, Action<string>? onError = null)
|
||||
{
|
||||
if (CreateRequested == null)
|
||||
return;
|
||||
@@ -200,7 +198,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
onSuccess?.Invoke(room);
|
||||
}
|
||||
|
||||
public void JoinRoom(Room room, string password, Action<Room> onSuccess = null, Action<string> onError = null) => throw new NotImplementedException();
|
||||
public void JoinRoom(Room room, string? password, Action<Room>? onSuccess = null, Action<string>? onError = null) => throw new NotImplementedException();
|
||||
|
||||
public void PartRoom() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -1,6 +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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
@@ -19,17 +20,16 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
AddStep("create list", () =>
|
||||
{
|
||||
SelectedRoom.Value = new Room { RoomID = { Value = 7 } };
|
||||
|
||||
for (int i = 0; i < 50; i++)
|
||||
SelectedRoom.Value = new Room
|
||||
{
|
||||
SelectedRoom.Value.RecentParticipants.Add(new APIUser
|
||||
RoomID = 7,
|
||||
RecentParticipants = Enumerable.Range(0, 50).Select(_ => new APIUser
|
||||
{
|
||||
Username = "peppy",
|
||||
Statistics = new UserStatistics { GlobalRank = 1234 },
|
||||
Id = 2
|
||||
});
|
||||
}
|
||||
}).ToArray()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("create component", () =>
|
||||
{
|
||||
Child = new ParticipantsDisplay(Direction.Horizontal)
|
||||
Child = new ParticipantsDisplay(SelectedRoom.Value, Direction.Horizontal)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
AddStep("create component", () =>
|
||||
{
|
||||
Child = new ParticipantsDisplay(Direction.Vertical)
|
||||
Child = new ParticipantsDisplay(SelectedRoom.Value, Direction.Vertical)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@@ -35,11 +32,9 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public partial class TestScenePlaylistsRoomCreation : OnlinePlayTestScene
|
||||
{
|
||||
private BeatmapManager manager;
|
||||
|
||||
private TestPlaylistsRoomSubScreen match;
|
||||
|
||||
private BeatmapSetInfo importedBeatmap;
|
||||
private BeatmapManager manager = null!;
|
||||
private TestPlaylistsRoomSubScreen match = null!;
|
||||
private BeatmapSetInfo importedBeatmap = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@@ -52,11 +47,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
[SetUpSteps]
|
||||
public void SetupSteps()
|
||||
{
|
||||
AddStep("set room", () => SelectedRoom!.Value = new Room());
|
||||
AddStep("set room", () => SelectedRoom.Value = new Room());
|
||||
|
||||
importBeatmap();
|
||||
|
||||
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom!.Value)));
|
||||
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value)));
|
||||
AddUntilStep("wait for load", () => match.IsCurrentScreen());
|
||||
}
|
||||
|
||||
@@ -65,14 +60,17 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
setupAndCreateRoom(room =>
|
||||
{
|
||||
room.Name.Value = "my awesome room";
|
||||
room.Host.Value = API.LocalUser.Value;
|
||||
room.RecentParticipants.Add(room.Host.Value);
|
||||
room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
|
||||
room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
});
|
||||
room.Name = "my awesome room";
|
||||
room.Host = API.LocalUser.Value;
|
||||
room.RecentParticipants = [room.Host];
|
||||
room.EndDate = DateTimeOffset.Now.AddMinutes(5);
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(importedBeatmap.Beatmaps.First())
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
AddUntilStep("Progress details are hidden", () => match.ChildrenOfType<RoomLocalUserInfo>().FirstOrDefault()?.Parent!.Alpha == 0);
|
||||
@@ -88,15 +86,18 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
setupAndCreateRoom(room =>
|
||||
{
|
||||
room.Name.Value = "my awesome room";
|
||||
room.MaxAttempts.Value = 5;
|
||||
room.Host.Value = API.LocalUser.Value;
|
||||
room.RecentParticipants.Add(room.Host.Value);
|
||||
room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
|
||||
room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
});
|
||||
room.Name = "my awesome room";
|
||||
room.MaxAttempts = 5;
|
||||
room.Host = API.LocalUser.Value;
|
||||
room.RecentParticipants = [room.Host];
|
||||
room.EndDate = DateTimeOffset.Now.AddMinutes(5);
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(importedBeatmap.Beatmaps.First())
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
AddUntilStep("Progress details are visible", () => match.ChildrenOfType<RoomLocalUserInfo>().FirstOrDefault()?.Parent!.Alpha == 1);
|
||||
@@ -107,21 +108,24 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
setupAndCreateRoom(room =>
|
||||
{
|
||||
room.Name.Value = "my awesome room";
|
||||
room.Host.Value = API.LocalUser.Value;
|
||||
room.Playlist.Add(new PlaylistItem(importedBeatmap.Beatmaps.First())
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
});
|
||||
room.Name = "my awesome room";
|
||||
room.Host = API.LocalUser.Value;
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(importedBeatmap.Beatmaps.First())
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom!.Value.Playlist[0]);
|
||||
AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapUpdatedOnReImport()
|
||||
{
|
||||
string realHash = null;
|
||||
string realHash = null!;
|
||||
int realOnlineId = 0;
|
||||
int realOnlineSetId = 0;
|
||||
|
||||
@@ -139,40 +143,40 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
BeatmapInfo =
|
||||
{
|
||||
OnlineID = realOnlineId,
|
||||
Metadata = new BeatmapMetadata(),
|
||||
BeatmapSet =
|
||||
{
|
||||
OnlineID = realOnlineSetId
|
||||
}
|
||||
Metadata = new BeatmapMetadata()
|
||||
},
|
||||
};
|
||||
|
||||
Debug.Assert(modifiedBeatmap.BeatmapInfo.BeatmapSet != null);
|
||||
modifiedBeatmap.BeatmapInfo.BeatmapSet!.OnlineID = realOnlineSetId;
|
||||
|
||||
modifiedBeatmap.HitObjects.Clear();
|
||||
modifiedBeatmap.HitObjects.Add(new HitCircle { StartTime = 5000 });
|
||||
|
||||
Debug.Assert(modifiedBeatmap.BeatmapInfo.BeatmapSet != null);
|
||||
|
||||
manager.Import(modifiedBeatmap.BeatmapInfo.BeatmapSet);
|
||||
});
|
||||
|
||||
// Create the room using the real beatmap values.
|
||||
setupAndCreateRoom(room =>
|
||||
{
|
||||
room.Name.Value = "my awesome room";
|
||||
room.Host.Value = API.LocalUser.Value;
|
||||
room.Playlist.Add(new PlaylistItem(new BeatmapInfo
|
||||
{
|
||||
MD5Hash = realHash,
|
||||
OnlineID = realOnlineId,
|
||||
Metadata = new BeatmapMetadata(),
|
||||
BeatmapSet = new BeatmapSetInfo
|
||||
room.Name = "my awesome room";
|
||||
room.Host = API.LocalUser.Value;
|
||||
room.Playlist =
|
||||
[
|
||||
new PlaylistItem(new BeatmapInfo
|
||||
{
|
||||
OnlineID = realOnlineSetId,
|
||||
MD5Hash = realHash,
|
||||
OnlineID = realOnlineId,
|
||||
Metadata = new BeatmapMetadata(),
|
||||
BeatmapSet = new BeatmapSetInfo
|
||||
{
|
||||
OnlineID = realOnlineSetId,
|
||||
}
|
||||
})
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
}
|
||||
})
|
||||
{
|
||||
RulesetID = new OsuRuleset().RulesetInfo.OnlineID
|
||||
});
|
||||
];
|
||||
});
|
||||
|
||||
AddAssert("match has default beatmap", () => match.Beatmap.IsDefault);
|
||||
@@ -181,17 +185,11 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
var originalBeatmap = new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
OnlineID = realOnlineId,
|
||||
BeatmapSet =
|
||||
{
|
||||
OnlineID = realOnlineSetId
|
||||
}
|
||||
},
|
||||
BeatmapInfo = { OnlineID = realOnlineId },
|
||||
};
|
||||
|
||||
Debug.Assert(originalBeatmap.BeatmapInfo.BeatmapSet != null);
|
||||
originalBeatmap.BeatmapInfo.BeatmapSet.OnlineID = realOnlineSetId;
|
||||
|
||||
manager.Import(originalBeatmap.BeatmapInfo.BeatmapSet);
|
||||
});
|
||||
@@ -201,7 +199,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
private void setupAndCreateRoom(Action<Room> room)
|
||||
{
|
||||
AddStep("setup room", () => room(SelectedRoom!.Value));
|
||||
AddStep("setup room", () => room(SelectedRoom.Value));
|
||||
|
||||
AddStep("click create button", () =>
|
||||
{
|
||||
@@ -215,8 +213,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
var beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
Debug.Assert(beatmap.BeatmapInfo.BeatmapSet != null);
|
||||
|
||||
importedBeatmap = manager.Import(beatmap.BeatmapInfo.BeatmapSet)?.Value.Detach();
|
||||
importedBeatmap = manager.Import(beatmap.BeatmapInfo.BeatmapSet)!.Value.Detach();
|
||||
});
|
||||
|
||||
private partial class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen
|
||||
@@ -226,8 +223,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
[CanBeNull]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
|
||||
public TestPlaylistsRoomSubScreen(Room room)
|
||||
: base(room)
|
||||
|
||||
@@ -53,13 +53,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
beatmap.OnlineID = 1001;
|
||||
getRoomRequest.TriggerSuccess(new Room
|
||||
{
|
||||
RoomID = { Value = 1234 },
|
||||
RoomID = 1234,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmap)
|
||||
},
|
||||
StartDate = { Value = DateTimeOffset.Now.AddMinutes(-5) },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) }
|
||||
],
|
||||
StartDate = DateTimeOffset.Now.AddMinutes(-5),
|
||||
EndDate = DateTimeOffset.Now.AddSeconds(30)
|
||||
});
|
||||
return true;
|
||||
|
||||
@@ -131,13 +131,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
beatmap.OnlineID = 1001;
|
||||
getRoomRequest.TriggerSuccess(new Room
|
||||
{
|
||||
RoomID = { Value = 1234 },
|
||||
RoomID = 1234,
|
||||
Playlist =
|
||||
{
|
||||
[
|
||||
new PlaylistItem(beatmap)
|
||||
},
|
||||
StartDate = { Value = DateTimeOffset.Now.AddMinutes(-50) },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddSeconds(30) }
|
||||
],
|
||||
StartDate = DateTimeOffset.Now.AddMinutes(-50),
|
||||
EndDate = DateTimeOffset.Now.AddSeconds(30)
|
||||
});
|
||||
return true;
|
||||
|
||||
|
||||
Generated
+1
@@ -0,0 +1 @@
|
||||
osu
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="0959115c-c7d5-4a9d-b138-d34f47e6713a" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo"><![CDATA[{
|
||||
"associatedIndex": 1
|
||||
}]]></component>
|
||||
<component name="ProjectId" id="2p6efoueruThVmDy8XXhD8Ibwab" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="autoscrollFromSource" value="true" />
|
||||
<option name="autoscrollToSource" value="true" />
|
||||
<option name="flattenPackages" value="true" />
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
</project>
|
||||
@@ -14,9 +14,17 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
/// </summary>
|
||||
public partial class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable<IBeatmapInfo>
|
||||
{
|
||||
public readonly Bindable<IBeatmapInfo> Beatmap = new Bindable<IBeatmapInfo>();
|
||||
public readonly Bindable<IBeatmapInfo?> Beatmap = new Bindable<IBeatmapInfo?>();
|
||||
|
||||
protected override double LoadDelay => 500;
|
||||
/// <summary>
|
||||
/// Delay before the background is loaded while on-screen.
|
||||
/// </summary>
|
||||
public double BackgroundLoadDelay { get; set; } = 500;
|
||||
|
||||
/// <summary>
|
||||
/// Delay before the background is unloaded while off-screen.
|
||||
/// </summary>
|
||||
public double BackgroundUnloadDelay { get; set; } = 10000;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; } = null!;
|
||||
@@ -29,10 +37,9 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
this.beatmapSetCoverType = beatmapSetCoverType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delay before the background is unloaded while off-screen.
|
||||
/// </summary>
|
||||
protected virtual double UnloadDelay => 10000;
|
||||
protected override double LoadDelay => BackgroundLoadDelay;
|
||||
|
||||
protected virtual double UnloadDelay => BackgroundUnloadDelay;
|
||||
|
||||
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad) =>
|
||||
new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay) { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
@@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
@@ -181,10 +180,10 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
await joinOrLeaveTaskChain.Add(async () =>
|
||||
{
|
||||
Debug.Assert(room.RoomID.Value != null);
|
||||
Debug.Assert(room.RoomID != null);
|
||||
|
||||
// Join the server-side room.
|
||||
var joinedRoom = await JoinRoom(room.RoomID.Value.Value, password ?? room.Password.Value).ConfigureAwait(false);
|
||||
var joinedRoom = await JoinRoom(room.RoomID.Value, password ?? room.Password).ConfigureAwait(false);
|
||||
Debug.Assert(joinedRoom != null);
|
||||
|
||||
// Populate users.
|
||||
@@ -201,12 +200,11 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
Debug.Assert(joinedRoom.Playlist.Count > 0);
|
||||
|
||||
APIRoom.Playlist.Clear();
|
||||
APIRoom.Playlist.AddRange(joinedRoom.Playlist.Select(item => new PlaylistItem(item)));
|
||||
APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == joinedRoom.Settings.PlaylistItemId);
|
||||
APIRoom.Playlist = joinedRoom.Playlist.Select(item => new PlaylistItem(item)).ToArray();
|
||||
APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == joinedRoom.Settings.PlaylistItemId);
|
||||
|
||||
// The server will null out the end date upon the host joining the room, but the null value is never communicated to the client.
|
||||
APIRoom.EndDate.Value = null;
|
||||
APIRoom.EndDate = null;
|
||||
|
||||
Debug.Assert(LocalUser != null);
|
||||
addUserToAPIRoom(LocalUser);
|
||||
@@ -397,15 +395,15 @@ namespace osu.Game.Online.Multiplayer
|
||||
switch (state)
|
||||
{
|
||||
case MultiplayerRoomState.Open:
|
||||
APIRoom.Status.Value = APIRoom.HasPassword.Value ? new RoomStatusOpenPrivate() : new RoomStatusOpen();
|
||||
APIRoom.Status = APIRoom.HasPassword ? new RoomStatusOpenPrivate() : new RoomStatusOpen();
|
||||
break;
|
||||
|
||||
case MultiplayerRoomState.Playing:
|
||||
APIRoom.Status.Value = new RoomStatusPlaying();
|
||||
APIRoom.Status = new RoomStatusPlaying();
|
||||
break;
|
||||
|
||||
case MultiplayerRoomState.Closed:
|
||||
APIRoom.Status.Value = new RoomStatusEnded();
|
||||
APIRoom.Status = new RoomStatusEnded();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -459,7 +457,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
if (apiUser == null || apiRoom == null) return;
|
||||
|
||||
PostNotification?.Invoke(
|
||||
new UserAvatarNotification(apiUser, NotificationsStrings.InvitedYouToTheMultiplayer(apiUser.Username, apiRoom.Name.Value))
|
||||
new UserAvatarNotification(apiUser, NotificationsStrings.InvitedYouToTheMultiplayer(apiUser.Username, apiRoom.Name))
|
||||
{
|
||||
Activated = () =>
|
||||
{
|
||||
@@ -487,12 +485,12 @@ namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
Debug.Assert(APIRoom != null);
|
||||
|
||||
APIRoom.RecentParticipants.Add(user.User ?? new APIUser
|
||||
APIRoom.RecentParticipants = APIRoom.RecentParticipants.Append(user.User ?? new APIUser
|
||||
{
|
||||
Id = user.UserID,
|
||||
Username = "[Unresolved]"
|
||||
});
|
||||
APIRoom.ParticipantCount.Value++;
|
||||
}).ToArray();
|
||||
APIRoom.ParticipantCount++;
|
||||
}
|
||||
|
||||
private Task handleUserLeft(MultiplayerRoomUser user, Action<MultiplayerRoomUser>? callback)
|
||||
@@ -506,8 +504,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
PlayingUserIds.Remove(user.UserID);
|
||||
|
||||
Debug.Assert(APIRoom != null);
|
||||
APIRoom.RecentParticipants.RemoveAll(u => u.Id == user.UserID);
|
||||
APIRoom.ParticipantCount.Value--;
|
||||
APIRoom.RecentParticipants = APIRoom.RecentParticipants.Where(u => u.Id != user.UserID).ToArray();
|
||||
APIRoom.ParticipantCount--;
|
||||
|
||||
callback?.Invoke(user);
|
||||
RoomUpdated?.Invoke();
|
||||
@@ -528,7 +526,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
var user = Room.Users.FirstOrDefault(u => u.UserID == userId);
|
||||
|
||||
Room.Host = user;
|
||||
APIRoom.Host.Value = user?.User;
|
||||
APIRoom.Host = user?.User;
|
||||
|
||||
RoomUpdated?.Invoke();
|
||||
}, false);
|
||||
@@ -734,7 +732,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
Debug.Assert(APIRoom != null);
|
||||
|
||||
Room.Playlist.Add(item);
|
||||
APIRoom.Playlist.Add(new PlaylistItem(item));
|
||||
APIRoom.Playlist = APIRoom.Playlist.Append(new PlaylistItem(item)).ToArray();
|
||||
|
||||
ItemAdded?.Invoke(item);
|
||||
RoomUpdated?.Invoke();
|
||||
@@ -753,7 +751,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
Debug.Assert(APIRoom != null);
|
||||
|
||||
Room.Playlist.Remove(Room.Playlist.Single(existing => existing.ID == playlistItemId));
|
||||
APIRoom.Playlist.RemoveAll(existing => existing.ID == playlistItemId);
|
||||
APIRoom.Playlist = APIRoom.Playlist.Where(i => i.ID != playlistItemId).ToArray();
|
||||
|
||||
Debug.Assert(Room.Playlist.Count > 0);
|
||||
|
||||
@@ -771,30 +769,10 @@ namespace osu.Game.Online.Multiplayer
|
||||
if (Room == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Debug.Assert(APIRoom != null);
|
||||
Debug.Assert(APIRoom != null);
|
||||
|
||||
Room.Playlist[Room.Playlist.IndexOf(Room.Playlist.Single(existing => existing.ID == item.ID))] = item;
|
||||
|
||||
int existingIndex = APIRoom.Playlist.IndexOf(APIRoom.Playlist.Single(existing => existing.ID == item.ID));
|
||||
|
||||
APIRoom.Playlist.RemoveAt(existingIndex);
|
||||
APIRoom.Playlist.Insert(existingIndex, new PlaylistItem(item));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Temporary code to attempt to figure out long-term failing tests.
|
||||
StringBuilder exceptionText = new StringBuilder();
|
||||
|
||||
exceptionText.AppendLine("MultiplayerClient test failure investigation");
|
||||
exceptionText.AppendLine($"Exception : {ex.ToString()}");
|
||||
exceptionText.AppendLine($"Lookup : {item.ID}");
|
||||
exceptionText.AppendLine($"Items in Room.Playlist : {string.Join(',', Room.Playlist.Select(i => i.ID))}");
|
||||
exceptionText.AppendLine($"Items in APIRoom.Playlist: {string.Join(',', APIRoom!.Playlist.Select(i => i.ID))}");
|
||||
|
||||
throw new AggregateException(exceptionText.ToString());
|
||||
}
|
||||
Room.Playlist[Room.Playlist.IndexOf(Room.Playlist.Single(existing => existing.ID == item.ID))] = item;
|
||||
APIRoom.Playlist = APIRoom.Playlist.Select((pi, i) => pi.ID == item.ID ? new PlaylistItem(item) : APIRoom.Playlist[i]).ToArray();
|
||||
|
||||
ItemChanged?.Invoke(item);
|
||||
RoomUpdated?.Invoke();
|
||||
@@ -841,14 +819,14 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
// Update a few properties of the room instantaneously.
|
||||
Room.Settings = settings;
|
||||
APIRoom.Name.Value = Room.Settings.Name;
|
||||
APIRoom.Password.Value = Room.Settings.Password;
|
||||
APIRoom.Status.Value = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate();
|
||||
APIRoom.Type.Value = Room.Settings.MatchType;
|
||||
APIRoom.QueueMode.Value = Room.Settings.QueueMode;
|
||||
APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration;
|
||||
APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId);
|
||||
APIRoom.AutoSkip.Value = Room.Settings.AutoSkip;
|
||||
APIRoom.Name = Room.Settings.Name;
|
||||
APIRoom.Password = Room.Settings.Password;
|
||||
APIRoom.Status = string.IsNullOrEmpty(Room.Settings.Password) ? new RoomStatusOpen() : new RoomStatusOpenPrivate();
|
||||
APIRoom.Type = Room.Settings.MatchType;
|
||||
APIRoom.QueueMode = Room.Settings.QueueMode;
|
||||
APIRoom.AutoStartDuration = Room.Settings.AutoStartDuration;
|
||||
APIRoom.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId);
|
||||
APIRoom.AutoSkip = Room.Settings.AutoSkip;
|
||||
|
||||
RoomUpdated?.Invoke();
|
||||
}
|
||||
|
||||
@@ -44,12 +44,12 @@ namespace osu.Game.Online.Rooms
|
||||
// API doesn't populate status so let's do it here.
|
||||
foreach (var room in Response)
|
||||
{
|
||||
if (room.EndDate.Value != null && DateTimeOffset.Now >= room.EndDate.Value)
|
||||
room.Status.Value = new RoomStatusEnded();
|
||||
else if (room.HasPassword.Value)
|
||||
room.Status.Value = new RoomStatusOpenPrivate();
|
||||
if (room.EndDate != null && DateTimeOffset.Now >= room.EndDate)
|
||||
room.Status = new RoomStatusEnded();
|
||||
else if (room.HasPassword)
|
||||
room.Status = new RoomStatusOpenPrivate();
|
||||
else
|
||||
room.Status.Value = new RoomStatusOpen();
|
||||
room.Status = new RoomStatusOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,6 @@ namespace osu.Game.Online.Rooms
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $@"rooms/{Room.RoomID.Value}/users/{User!.Id}";
|
||||
protected override string Target => $@"rooms/{Room.RoomID}/users/{User!.Id}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,6 @@ namespace osu.Game.Online.Rooms
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => $"rooms/{room.RoomID.Value}/users/{User!.Id}";
|
||||
protected override string Target => $"rooms/{room.RoomID}/users/{User!.Id}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Utils;
|
||||
|
||||
@@ -30,7 +29,7 @@ namespace osu.Game.Online.Rooms
|
||||
/// or the last-played <see cref="PlaylistItem"/> if all items are expired,
|
||||
/// or <see langword="null"/> if <paramref name="playlist"/> was empty.
|
||||
/// </summary>
|
||||
public static PlaylistItem? GetCurrentItem(this ICollection<PlaylistItem> playlist)
|
||||
public static PlaylistItem? GetCurrentItem(this IReadOnlyCollection<PlaylistItem> playlist)
|
||||
{
|
||||
if (playlist.Count == 0)
|
||||
return null;
|
||||
@@ -43,7 +42,7 @@ namespace osu.Game.Online.Rooms
|
||||
/// <summary>
|
||||
/// Returns the total duration from the <see cref="PlaylistItem"/> in playlist order from the supplied <paramref name="playlist"/>,
|
||||
/// </summary>
|
||||
public static string GetTotalDuration(this BindableList<PlaylistItem> playlist, RulesetStore rulesetStore) =>
|
||||
public static string GetTotalDuration(this IReadOnlyList<PlaylistItem> playlist, RulesetStore rulesetStore) =>
|
||||
playlist.Select(p =>
|
||||
{
|
||||
double rate = 1;
|
||||
|
||||
@@ -120,18 +120,21 @@ namespace osu.Game.Online.Rooms
|
||||
|
||||
#endregion
|
||||
|
||||
public PlaylistItem With(Optional<IBeatmapInfo> beatmap = default, Optional<ushort?> playlistOrder = default) => new PlaylistItem(beatmap.GetOr(Beatmap))
|
||||
public PlaylistItem With(Optional<long> id = default, Optional<IBeatmapInfo> beatmap = default, Optional<ushort?> playlistOrder = default)
|
||||
{
|
||||
ID = ID,
|
||||
OwnerID = OwnerID,
|
||||
RulesetID = RulesetID,
|
||||
Expired = Expired,
|
||||
PlaylistOrder = playlistOrder.GetOr(PlaylistOrder),
|
||||
PlayedAt = PlayedAt,
|
||||
AllowedMods = AllowedMods,
|
||||
RequiredMods = RequiredMods,
|
||||
valid = { Value = Valid.Value },
|
||||
};
|
||||
return new PlaylistItem(beatmap.GetOr(Beatmap))
|
||||
{
|
||||
ID = id.GetOr(ID),
|
||||
OwnerID = OwnerID,
|
||||
RulesetID = RulesetID,
|
||||
Expired = Expired,
|
||||
PlaylistOrder = playlistOrder.GetOr(PlaylistOrder),
|
||||
PlayedAt = PlayedAt,
|
||||
AllowedMods = AllowedMods,
|
||||
RequiredMods = RequiredMods,
|
||||
valid = { Value = Valid.Value },
|
||||
};
|
||||
}
|
||||
|
||||
public bool Equals(PlaylistItem? other)
|
||||
=> ID == other?.ID
|
||||
|
||||
+354
-171
@@ -1,13 +1,12 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.IO.Serialization.Converters;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
@@ -16,162 +15,332 @@ using osu.Game.Online.Rooms.RoomStatuses;
|
||||
namespace osu.Game.Online.Rooms
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public partial class Room : IDependencyInjectionCandidate
|
||||
public partial class Room : INotifyPropertyChanged
|
||||
{
|
||||
[Cached]
|
||||
[JsonProperty("id")]
|
||||
public readonly Bindable<long?> RoomID = new Bindable<long?>();
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("name")]
|
||||
public readonly Bindable<string> Name = new Bindable<string>();
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("host")]
|
||||
public readonly Bindable<APIUser> Host = new Bindable<APIUser>();
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("playlist")]
|
||||
public readonly BindableList<PlaylistItem> Playlist = new BindableList<PlaylistItem>();
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("channel_id")]
|
||||
public readonly Bindable<int> ChannelId = new Bindable<int>();
|
||||
|
||||
[JsonProperty("current_playlist_item")]
|
||||
[Cached]
|
||||
public readonly Bindable<PlaylistItem> CurrentPlaylistItem = new Bindable<PlaylistItem>();
|
||||
|
||||
[JsonProperty("playlist_item_stats")]
|
||||
[Cached]
|
||||
public readonly Bindable<RoomPlaylistItemStats> PlaylistItemStats = new Bindable<RoomPlaylistItemStats>();
|
||||
|
||||
[JsonProperty("difficulty_range")]
|
||||
[Cached]
|
||||
public readonly Bindable<RoomDifficultyRange> DifficultyRange = new Bindable<RoomDifficultyRange>();
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<RoomCategory> Category = new Bindable<RoomCategory>();
|
||||
|
||||
// Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
|
||||
[JsonProperty("category")]
|
||||
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
|
||||
private RoomCategory category
|
||||
/// <summary>
|
||||
/// The online room ID. Will be <c>null</c> while the room has not yet been created.
|
||||
/// </summary>
|
||||
public long? RoomID
|
||||
{
|
||||
get => Category.Value;
|
||||
set => Category.Value = value;
|
||||
get => roomId;
|
||||
set => SetField(ref roomId, value);
|
||||
}
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<int?> MaxAttempts = new Bindable<int?>();
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<RoomStatus> Status = new Bindable<RoomStatus>(new RoomStatusOpen());
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<RoomAvailability> Availability = new Bindable<RoomAvailability>();
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<MatchType> Type = new Bindable<MatchType>();
|
||||
|
||||
// Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
|
||||
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
|
||||
[JsonProperty("type")]
|
||||
private MatchType type
|
||||
/// <summary>
|
||||
/// The room name.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => Type.Value;
|
||||
set => Type.Value = value;
|
||||
get => name;
|
||||
set => SetField(ref name, value);
|
||||
}
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<QueueMode> QueueMode = new Bindable<QueueMode>();
|
||||
|
||||
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
|
||||
[JsonProperty("queue_mode")]
|
||||
private QueueMode queueMode
|
||||
/// <summary>
|
||||
/// Sets the room password. Will be <c>null</c> after the room is created.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To check if the room has a password, use <see cref="HasPassword"/>.
|
||||
/// </remarks>
|
||||
public string? Password
|
||||
{
|
||||
get => QueueMode.Value;
|
||||
set => QueueMode.Value = value;
|
||||
}
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<TimeSpan> AutoStartDuration = new Bindable<TimeSpan>();
|
||||
|
||||
[JsonProperty("auto_start_duration")]
|
||||
private ushort autoStartDuration
|
||||
{
|
||||
get => (ushort)AutoStartDuration.Value.TotalSeconds;
|
||||
set => AutoStartDuration.Value = TimeSpan.FromSeconds(value);
|
||||
}
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<int?> MaxParticipants = new Bindable<int?>();
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("current_user_score")]
|
||||
public readonly Bindable<PlaylistAggregateScore> UserScore = new Bindable<PlaylistAggregateScore>();
|
||||
|
||||
[JsonProperty("has_password")]
|
||||
public readonly Bindable<bool> HasPassword = new Bindable<bool>();
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("recent_participants")]
|
||||
public readonly BindableList<APIUser> RecentParticipants = new BindableList<APIUser>();
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("participant_count")]
|
||||
public readonly Bindable<int> ParticipantCount = new Bindable<int>();
|
||||
|
||||
#region Properties only used for room creation request
|
||||
|
||||
[Cached(Name = nameof(Password))]
|
||||
[JsonProperty("password")]
|
||||
public readonly Bindable<string> Password = new Bindable<string>();
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<TimeSpan?> Duration = new Bindable<TimeSpan?>();
|
||||
|
||||
[JsonProperty("duration")]
|
||||
private int? duration
|
||||
{
|
||||
get => (int?)Duration.Value?.TotalMinutes;
|
||||
get => password;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
Duration.Value = null;
|
||||
else
|
||||
Duration.Value = TimeSpan.FromMinutes(value.Value);
|
||||
SetField(ref password, value);
|
||||
HasPassword = !string.IsNullOrEmpty(value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Whether the room has a password.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To set a password, use <see cref="Password"/>.
|
||||
/// </remarks>
|
||||
[JsonProperty("has_password")]
|
||||
public bool HasPassword
|
||||
{
|
||||
get => hasPassword;
|
||||
private set => SetField(ref hasPassword, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The room host. Will be <c>null</c> while the room has not yet been created.
|
||||
/// </summary>
|
||||
public APIUser? Host
|
||||
{
|
||||
get => host;
|
||||
set => SetField(ref host, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The room category.
|
||||
/// </summary>
|
||||
public RoomCategory Category
|
||||
{
|
||||
get => category;
|
||||
set => SetField(ref category, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The duration for which the room will be open. Will be <c>null</c> after the room is created.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To check the room end time, use <see cref="EndDate"/>.
|
||||
/// </remarks>
|
||||
public TimeSpan? Duration
|
||||
{
|
||||
get => duration == null ? null : TimeSpan.FromMinutes(duration.Value);
|
||||
set => SetField(ref duration, value == null ? null : (int)value.Value.TotalMinutes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date at which the room was opened. Will be <c>null</c> while the room has not yet been created.
|
||||
/// </summary>
|
||||
public DateTimeOffset? StartDate
|
||||
{
|
||||
get => startDate;
|
||||
set => SetField(ref startDate, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date at which the room will be closed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To set the room duration, use <see cref="Duration"/>.
|
||||
/// </remarks>
|
||||
public DateTimeOffset? EndDate
|
||||
{
|
||||
get => endDate;
|
||||
set => SetField(ref endDate, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of users allowed in the room.
|
||||
/// </summary>
|
||||
public int? MaxParticipants
|
||||
{
|
||||
get => maxParticipants;
|
||||
set => SetField(ref maxParticipants, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current number of users in the room.
|
||||
/// </summary>
|
||||
public int ParticipantCount
|
||||
{
|
||||
get => participantCount;
|
||||
set => SetField(ref participantCount, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The set of most recent participants in the room.
|
||||
/// </summary>
|
||||
public IReadOnlyList<APIUser> RecentParticipants
|
||||
{
|
||||
get => recentParticipants;
|
||||
set => SetList(ref recentParticipants, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The match type.
|
||||
/// </summary>
|
||||
public MatchType Type
|
||||
{
|
||||
get => type;
|
||||
set => SetField(ref type, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of attempts on the playlist. Only valid for playlist rooms.
|
||||
/// </summary>
|
||||
public int? MaxAttempts
|
||||
{
|
||||
get => maxAttempts;
|
||||
set => SetField(ref maxAttempts, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The room playlist.
|
||||
/// </summary>
|
||||
public IReadOnlyList<PlaylistItem> Playlist
|
||||
{
|
||||
get => playlist;
|
||||
set => SetList(ref playlist, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes the items in the playlist.
|
||||
/// </summary>
|
||||
public RoomPlaylistItemStats? PlaylistItemStats
|
||||
{
|
||||
get => playlistItemStats;
|
||||
set => SetField(ref playlistItemStats, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes the range of difficulty of the room.
|
||||
/// </summary>
|
||||
public RoomDifficultyRange? DifficultyRange
|
||||
{
|
||||
get => difficultyRange;
|
||||
set => SetField(ref difficultyRange, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The playlist queueing mode. Only valid for multiplayer rooms.
|
||||
/// </summary>
|
||||
public QueueMode QueueMode
|
||||
{
|
||||
get => queueMode;
|
||||
set => SetField(ref queueMode, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to automatically skip map intros. Only valid for multiplayer rooms.
|
||||
/// </summary>
|
||||
public bool AutoSkip
|
||||
{
|
||||
get => autoSkip;
|
||||
set => SetField(ref autoSkip, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The amount of time before the match is automatically started. Only valid for multiplayer rooms.
|
||||
/// </summary>
|
||||
public TimeSpan AutoStartDuration
|
||||
{
|
||||
get => TimeSpan.FromSeconds(autoStartDuration);
|
||||
set => SetField(ref autoStartDuration, (ushort)value.TotalSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides some extra scoring statistics for the local user in the room.
|
||||
/// </summary>
|
||||
public PlaylistAggregateScore? UserScore
|
||||
{
|
||||
get => userScore;
|
||||
set => SetField(ref userScore, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the current item selected within the room.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only valid for room listing requests (i.e. in the lounge screen), and may not be valid while inside the room.
|
||||
/// </remarks>
|
||||
public PlaylistItem? CurrentPlaylistItem
|
||||
{
|
||||
get => currentPlaylistItem;
|
||||
set => SetField(ref currentPlaylistItem, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The chat channel id for the room. Will be <c>0</c> while the room has not yet been created.
|
||||
/// </summary>
|
||||
public int ChannelId
|
||||
{
|
||||
get => channelId;
|
||||
private set => SetField(ref channelId, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current room status.
|
||||
/// </summary>
|
||||
public RoomStatus Status
|
||||
{
|
||||
get => status;
|
||||
set => SetField(ref status, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes which players are able to join the room.
|
||||
/// </summary>
|
||||
public RoomAvailability Availability
|
||||
{
|
||||
get => availability;
|
||||
set => SetField(ref availability, value);
|
||||
}
|
||||
|
||||
[JsonProperty("id")]
|
||||
private long? roomId;
|
||||
|
||||
[JsonProperty("name")]
|
||||
private string name = string.Empty;
|
||||
|
||||
[JsonProperty("password")]
|
||||
private string? password;
|
||||
|
||||
// Not serialised (internal use only).
|
||||
private bool hasPassword;
|
||||
|
||||
[JsonProperty("host")]
|
||||
private APIUser? host;
|
||||
|
||||
[JsonProperty("category")]
|
||||
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
|
||||
private RoomCategory category;
|
||||
|
||||
[JsonProperty("duration")]
|
||||
private int? duration;
|
||||
|
||||
// Only supports retrieval for now
|
||||
[Cached]
|
||||
[JsonProperty("starts_at")]
|
||||
public readonly Bindable<DateTimeOffset?> StartDate = new Bindable<DateTimeOffset?>();
|
||||
private DateTimeOffset? startDate;
|
||||
|
||||
// Only supports retrieval for now
|
||||
[Cached]
|
||||
[JsonProperty("ends_at")]
|
||||
public readonly Bindable<DateTimeOffset?> EndDate = new Bindable<DateTimeOffset?>();
|
||||
private DateTimeOffset? endDate;
|
||||
|
||||
// Not yet serialised (not implemented).
|
||||
private int? maxParticipants;
|
||||
|
||||
[JsonProperty("participant_count")]
|
||||
private int participantCount;
|
||||
|
||||
[JsonProperty("recent_participants")]
|
||||
private IReadOnlyList<APIUser> recentParticipants = [];
|
||||
|
||||
// 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.Value;
|
||||
set => MaxAttempts.Value = value;
|
||||
}
|
||||
private int? maxAttempts;
|
||||
|
||||
[JsonProperty("playlist")]
|
||||
private IReadOnlyList<PlaylistItem> playlist = [];
|
||||
|
||||
[JsonProperty("playlist_item_stats")]
|
||||
private RoomPlaylistItemStats? playlistItemStats;
|
||||
|
||||
[JsonProperty("difficulty_range")]
|
||||
private RoomDifficultyRange? difficultyRange;
|
||||
|
||||
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
|
||||
[JsonProperty("type")]
|
||||
private MatchType type;
|
||||
|
||||
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
|
||||
[JsonProperty("queue_mode")]
|
||||
private QueueMode queueMode;
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("auto_skip")]
|
||||
public readonly Bindable<bool> AutoSkip = new Bindable<bool>();
|
||||
private bool autoSkip;
|
||||
|
||||
public Room()
|
||||
{
|
||||
Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue));
|
||||
}
|
||||
[JsonProperty("auto_start_duration")]
|
||||
private ushort autoStartDuration;
|
||||
|
||||
[JsonProperty("current_user_score")]
|
||||
private PlaylistAggregateScore? userScore;
|
||||
|
||||
[JsonProperty("current_playlist_item")]
|
||||
private PlaylistItem? currentPlaylistItem;
|
||||
|
||||
[JsonProperty("channel_id")]
|
||||
private int channelId;
|
||||
|
||||
// Not serialised (see: GetRoomsRequest).
|
||||
private RoomStatus status = new RoomStatusOpen();
|
||||
|
||||
// Not yet serialised (not implemented).
|
||||
private RoomAvailability availability;
|
||||
|
||||
/// <summary>
|
||||
/// Copies values from another <see cref="Room"/> into this one.
|
||||
@@ -182,43 +351,34 @@ namespace osu.Game.Online.Rooms
|
||||
/// <param name="other">The <see cref="Room"/> to copy values from.</param>
|
||||
public void CopyFrom(Room other)
|
||||
{
|
||||
RoomID.Value = other.RoomID.Value;
|
||||
Name.Value = other.Name.Value;
|
||||
RoomID = other.RoomID;
|
||||
Name = other.Name;
|
||||
|
||||
Category.Value = other.Category.Value;
|
||||
Category = other.Category;
|
||||
|
||||
if (other.Host.Value != null && Host.Value?.Id != other.Host.Value.Id)
|
||||
Host.Value = other.Host.Value;
|
||||
if (other.Host != null && Host?.Id != other.Host.Id)
|
||||
Host = other.Host;
|
||||
|
||||
ChannelId.Value = other.ChannelId.Value;
|
||||
Status.Value = other.Status.Value;
|
||||
Availability.Value = other.Availability.Value;
|
||||
HasPassword.Value = other.HasPassword.Value;
|
||||
Type.Value = other.Type.Value;
|
||||
MaxParticipants.Value = other.MaxParticipants.Value;
|
||||
ParticipantCount.Value = other.ParticipantCount.Value;
|
||||
EndDate.Value = other.EndDate.Value;
|
||||
UserScore.Value = other.UserScore.Value;
|
||||
QueueMode.Value = other.QueueMode.Value;
|
||||
AutoStartDuration.Value = other.AutoStartDuration.Value;
|
||||
DifficultyRange.Value = other.DifficultyRange.Value;
|
||||
PlaylistItemStats.Value = other.PlaylistItemStats.Value;
|
||||
CurrentPlaylistItem.Value = other.CurrentPlaylistItem.Value;
|
||||
AutoSkip.Value = other.AutoSkip.Value;
|
||||
ChannelId = other.ChannelId;
|
||||
Status = other.Status;
|
||||
Availability = other.Availability;
|
||||
HasPassword = other.HasPassword;
|
||||
Type = other.Type;
|
||||
MaxParticipants = other.MaxParticipants;
|
||||
ParticipantCount = other.ParticipantCount;
|
||||
EndDate = other.EndDate;
|
||||
UserScore = other.UserScore;
|
||||
QueueMode = other.QueueMode;
|
||||
AutoStartDuration = other.AutoStartDuration;
|
||||
DifficultyRange = other.DifficultyRange;
|
||||
PlaylistItemStats = other.PlaylistItemStats;
|
||||
CurrentPlaylistItem = other.CurrentPlaylistItem;
|
||||
AutoSkip = other.AutoSkip;
|
||||
|
||||
other.RemoveExpiredPlaylistItems();
|
||||
|
||||
if (!Playlist.SequenceEqual(other.Playlist))
|
||||
{
|
||||
Playlist.Clear();
|
||||
Playlist.AddRange(other.Playlist);
|
||||
}
|
||||
|
||||
if (!RecentParticipants.SequenceEqual(other.RecentParticipants))
|
||||
{
|
||||
RecentParticipants.Clear();
|
||||
RecentParticipants.AddRange(other.RecentParticipants);
|
||||
}
|
||||
Playlist = other.Playlist;
|
||||
RecentParticipants = other.RecentParticipants;
|
||||
}
|
||||
|
||||
public void RemoveExpiredPlaylistItems()
|
||||
@@ -226,8 +386,8 @@ namespace osu.Game.Online.Rooms
|
||||
// Todo: This is not the best way/place to do this, but the intention is to display all playlist items when the room has ended,
|
||||
// and display only the non-expired playlist items while the room is still active. In order to achieve this, all expired items are removed from the source Room.
|
||||
// More refactoring is required before this can be done locally instead - DrawableRoomPlaylist is currently directly bound to the playlist to display items in the room.
|
||||
if (!(Status.Value is RoomStatusEnded))
|
||||
Playlist.RemoveAll(i => i.Expired);
|
||||
if (Status is not RoomStatusEnded)
|
||||
Playlist = Playlist.Where(i => !i.Expired).ToArray();
|
||||
}
|
||||
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
@@ -240,7 +400,7 @@ namespace osu.Game.Online.Rooms
|
||||
public int CountTotal;
|
||||
|
||||
[JsonProperty("ruleset_ids")]
|
||||
public int[] RulesetIDs;
|
||||
public int[] RulesetIDs = [];
|
||||
}
|
||||
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
@@ -252,5 +412,28 @@ namespace osu.Game.Online.Rooms
|
||||
[JsonProperty("max")]
|
||||
public double Max;
|
||||
}
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null!)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
protected bool SetList<T>(ref IReadOnlyList<T> list, IReadOnlyList<T> value, [CallerMemberName] string propertyName = null!)
|
||||
{
|
||||
if (list.SequenceEqual(value))
|
||||
return false;
|
||||
|
||||
list = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null!)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||
return false;
|
||||
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,15 +156,15 @@ namespace osu.Game.Screens.Menu
|
||||
Room = room;
|
||||
cover.OnlineInfo = TooltipContent = room.Playlist.FirstOrDefault()?.Beatmap.BeatmapSet as APIBeatmapSet;
|
||||
|
||||
if (room.StartDate.Value != null && room.RoomID.Value != lastDailyChallengeRoomID)
|
||||
if (room.StartDate != null && room.RoomID != lastDailyChallengeRoomID)
|
||||
{
|
||||
lastDailyChallengeRoomID = room.RoomID.Value;
|
||||
lastDailyChallengeRoomID = room.RoomID;
|
||||
|
||||
// new challenge is live, reset intro played static.
|
||||
statics.SetValue(Static.DailyChallengeIntroPlayed, false);
|
||||
|
||||
// we only want to notify the user if the new challenge just went live.
|
||||
if (Math.Abs((DateTimeOffset.Now - room.StartDate.Value!.Value).TotalSeconds) < 1800)
|
||||
if (Math.Abs((DateTimeOffset.Now - room.StartDate.Value).TotalSeconds) < 1800)
|
||||
notificationOverlay?.Post(new NewDailyChallengeNotification(room));
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace osu.Game.Screens.Menu
|
||||
if (Room == null)
|
||||
return;
|
||||
|
||||
var remaining = (Room.EndDate.Value - DateTimeOffset.Now) ?? TimeSpan.Zero;
|
||||
var remaining = (Room.EndDate - DateTimeOffset.Now) ?? TimeSpan.Zero;
|
||||
|
||||
if (remaining <= TimeSpan.Zero)
|
||||
{
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class BeatmapTitle : OnlinePlayComposite
|
||||
public partial class BeatmapTitle : CompositeDrawable
|
||||
{
|
||||
private readonly Room room;
|
||||
private readonly LinkFlowContainer textFlow;
|
||||
|
||||
public BeatmapTitle()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public BeatmapTitle(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
InternalChild = textFlow = new LinkFlowContainer { AutoSizeAxes = Axes.Both };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
Playlist.CollectionChanged += (_, _) => updateText();
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateText();
|
||||
}
|
||||
|
||||
@@ -46,8 +54,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.Playlist))
|
||||
updateText();
|
||||
}
|
||||
|
||||
private void updateText()
|
||||
{
|
||||
@@ -56,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
|
||||
textFlow.Clear();
|
||||
|
||||
var beatmap = Playlist.FirstOrDefault()?.Beatmap;
|
||||
var beatmap = room.Playlist.FirstOrDefault()?.Beatmap;
|
||||
|
||||
if (beatmap == null)
|
||||
{
|
||||
@@ -78,5 +89,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
textFlow.AddLink(title, LinkAction.OpenBeatmap, beatmap.OnlineID.ToString(), "Open beatmap");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
@@ -35,7 +33,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
});
|
||||
}
|
||||
|
||||
private GetRoomsRequest lastPollRequest;
|
||||
private GetRoomsRequest? lastPollRequest;
|
||||
|
||||
protected override Task Poll()
|
||||
{
|
||||
@@ -53,11 +51,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
|
||||
req.Success += result =>
|
||||
{
|
||||
result = result.Where(r => r.Category.Value != RoomCategory.DailyChallenge).ToList();
|
||||
result = result.Where(r => r.Category != RoomCategory.DailyChallenge).ToList();
|
||||
|
||||
foreach (var existing in RoomManager.Rooms.ToArray())
|
||||
{
|
||||
if (result.All(r => r.RoomID.Value != existing.RoomID.Value))
|
||||
if (result.All(r => r.RoomID != existing.RoomID))
|
||||
RoomManager.RemoveRoom(existing);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
@@ -14,23 +11,22 @@ using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Screens.Select;
|
||||
using osuTK;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class MatchBeatmapDetailArea : BeatmapDetailArea
|
||||
{
|
||||
public Action CreateNewItem;
|
||||
|
||||
public readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||
public Action? CreateNewItem;
|
||||
|
||||
private readonly Room room;
|
||||
private readonly GridContainer playlistArea;
|
||||
private readonly DrawableRoomPlaylist playlist;
|
||||
|
||||
public MatchBeatmapDetailArea()
|
||||
public MatchBeatmapDetailArea(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
|
||||
Add(playlistArea = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@@ -72,10 +68,21 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
playlist.Items.BindTo(Playlist);
|
||||
playlist.SelectedItem.BindTo(SelectedItem);
|
||||
playlist.Items.BindCollectionChanged((_, __) => room.Playlist = playlist.Items.ToArray());
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomPlaylist();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.Playlist))
|
||||
updateRoomPlaylist();
|
||||
}
|
||||
|
||||
private void updateRoomPlaylist()
|
||||
=> playlist.Items.ReplaceRange(0, playlist.Items.Count, room.Playlist);
|
||||
|
||||
protected override void OnTabChanged(BeatmapDetailAreaTabItem tab, bool selectedMods)
|
||||
{
|
||||
base.OnTabChanged(tab, selectedMods);
|
||||
@@ -93,5 +100,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
}
|
||||
|
||||
protected override BeatmapDetailAreaTabItem[] CreateTabItems() => base.CreateTabItems().Prepend(new BeatmapDetailAreaPlaylistTabItem()).ToArray();
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class OnlinePlayBackgroundSprite : OnlinePlayComposite
|
||||
{
|
||||
protected readonly BeatmapSetCoverType BeatmapSetCoverType;
|
||||
private UpdateableBeatmapBackgroundSprite sprite;
|
||||
|
||||
public OnlinePlayBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover)
|
||||
{
|
||||
BeatmapSetCoverType = beatmapSetCoverType;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = sprite = CreateBackgroundSprite();
|
||||
|
||||
CurrentPlaylistItem.BindValueChanged(_ => updateBeatmap());
|
||||
Playlist.CollectionChanged += (_, _) => updateBeatmap();
|
||||
|
||||
updateBeatmap();
|
||||
}
|
||||
|
||||
private void updateBeatmap()
|
||||
{
|
||||
sprite.Beatmap.Value = CurrentPlaylistItem.Value?.Beatmap ?? Playlist.GetCurrentItem()?.Beatmap;
|
||||
}
|
||||
|
||||
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
/// <summary>
|
||||
/// A header used in the multiplayer interface which shows text / details beneath a line.
|
||||
/// </summary>
|
||||
public partial class OverlinedHeader : OnlinePlayComposite
|
||||
public partial class OverlinedHeader : CompositeDrawable
|
||||
{
|
||||
private bool showLine = true;
|
||||
|
||||
|
||||
@@ -1,6 +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.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
@@ -9,19 +10,38 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class OverlinedPlaylistHeader : OverlinedHeader
|
||||
{
|
||||
private readonly Room room;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; } = null!;
|
||||
|
||||
public OverlinedPlaylistHeader()
|
||||
public OverlinedPlaylistHeader(Room room)
|
||||
: base("Playlist")
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Playlist.BindCollectionChanged((_, _) => Details.Value = Playlist.GetTotalDuration(rulesets), true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateDuration();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.Playlist))
|
||||
updateDuration();
|
||||
}
|
||||
|
||||
private void updateDuration()
|
||||
=> Details.Value = room.Playlist.GetTotalDuration(rulesets);
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class ParticipantCountDisplay : OnlinePlayComposite
|
||||
public partial class ParticipantCountDisplay : CompositeDrawable
|
||||
{
|
||||
private const float text_size = 30;
|
||||
private const float transition_duration = 100;
|
||||
|
||||
private OsuSpriteText slash, maxText;
|
||||
private readonly Room room;
|
||||
|
||||
public ParticipantCountDisplay()
|
||||
private OsuSpriteText slash = null!;
|
||||
private OsuSpriteText maxText = null!;
|
||||
private OsuSpriteText count = null!;
|
||||
|
||||
public ParticipantCountDisplay(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
OsuSpriteText count;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
@@ -50,14 +53,33 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
MaxParticipants.BindValueChanged(_ => updateMax(), true);
|
||||
ParticipantCount.BindValueChanged(c => count.Text = c.NewValue.ToString("#,0"), true);
|
||||
}
|
||||
|
||||
private void updateMax()
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
if (MaxParticipants.Value == null)
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomParticipantCount();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.MaxParticipants):
|
||||
updateRoomMaxParticipants();
|
||||
break;
|
||||
|
||||
case nameof(Room.ParticipantCount):
|
||||
updateRoomParticipantCount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomMaxParticipants()
|
||||
{
|
||||
if (room.MaxParticipants == null)
|
||||
{
|
||||
slash.FadeOut(transition_duration);
|
||||
maxText.FadeOut(transition_duration);
|
||||
@@ -65,9 +87,18 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
else
|
||||
{
|
||||
slash.FadeIn(transition_duration);
|
||||
maxText.Text = MaxParticipants.Value.ToString();
|
||||
maxText.Text = room.MaxParticipants.ToString()!;
|
||||
maxText.FadeIn(transition_duration);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomParticipantCount()
|
||||
=> count.Text = room.ParticipantCount.ToString("#,0");
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
// 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 System.ComponentModel;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class ParticipantsDisplay : OnlinePlayComposite
|
||||
public partial class ParticipantsDisplay : CompositeDrawable
|
||||
{
|
||||
public Bindable<string> Details = new Bindable<string>();
|
||||
public readonly Bindable<string> Details = new Bindable<string>();
|
||||
|
||||
public ParticipantsDisplay(Direction direction)
|
||||
private readonly Room room;
|
||||
|
||||
public ParticipantsDisplay(Room room, Direction direction)
|
||||
{
|
||||
this.room = room;
|
||||
OsuScrollContainer scroll;
|
||||
ParticipantsList list;
|
||||
|
||||
AddInternal(scroll = new OsuScrollContainer(direction)
|
||||
{
|
||||
Child = list = new ParticipantsList()
|
||||
Child = list = new ParticipantsList(room)
|
||||
});
|
||||
|
||||
switch (direction)
|
||||
@@ -46,14 +51,32 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
ParticipantCount.BindValueChanged(_ => setParticipantCount());
|
||||
MaxParticipants.BindValueChanged(_ => setParticipantCount(), true);
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomParticipantCount();
|
||||
}
|
||||
|
||||
private void setParticipantCount() =>
|
||||
Details.Value = MaxParticipants.Value != null ? $"{ParticipantCount.Value}/{MaxParticipants.Value}" : ParticipantCount.Value.ToString();
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.MaxParticipants):
|
||||
case nameof(Room.ParticipantCount):
|
||||
updateRoomParticipantCount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomParticipantCount()
|
||||
=> Details.Value = room.MaxParticipants != null ? $"{room.ParticipantCount}/{room.MaxParticipants}" : room.ParticipantCount.ToString();
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class ParticipantsList : OnlinePlayComposite
|
||||
public partial class ParticipantsList : CompositeDrawable
|
||||
{
|
||||
public const float TILE_SIZE = 35;
|
||||
|
||||
@@ -57,15 +56,29 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private readonly Room room;
|
||||
|
||||
public ParticipantsList(Room room)
|
||||
{
|
||||
RecentParticipants.CollectionChanged += (_, _) => updateParticipants();
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateParticipants();
|
||||
}
|
||||
|
||||
private ScheduledDelegate scheduledUpdate;
|
||||
private FillFlowContainer<UserTile> tiles;
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.RecentParticipants))
|
||||
updateParticipants();
|
||||
}
|
||||
|
||||
private ScheduledDelegate? scheduledUpdate;
|
||||
private FillFlowContainer<UserTile>? tiles;
|
||||
|
||||
private void updateParticipants()
|
||||
{
|
||||
@@ -83,8 +96,8 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
Spacing = Vector2.One
|
||||
};
|
||||
|
||||
for (int i = 0; i < RecentParticipants.Count; i++)
|
||||
tiles.Add(new UserTile { User = RecentParticipants[i] });
|
||||
for (int i = 0; i < room.RecentParticipants.Count; i++)
|
||||
tiles.Add(new UserTile { User = room.RecentParticipants[i] });
|
||||
|
||||
AddInternal(tiles);
|
||||
|
||||
@@ -92,9 +105,15 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
|
||||
private partial class UserTile : CompositeDrawable
|
||||
{
|
||||
public APIUser User
|
||||
public APIUser? User
|
||||
{
|
||||
get => avatar.User;
|
||||
set => avatar.User = value;
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class RoomLocalUserInfo : OnlinePlayComposite
|
||||
public partial class RoomLocalUserInfo : CompositeDrawable
|
||||
{
|
||||
private OsuSpriteText attemptDisplay;
|
||||
private readonly Room room;
|
||||
private OsuSpriteText attemptDisplay = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public RoomLocalUserInfo()
|
||||
public RoomLocalUserInfo(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
@@ -45,19 +47,30 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
MaxAttempts.BindValueChanged(_ => updateAttempts());
|
||||
UserScore.BindValueChanged(_ => updateAttempts(), true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateAttempts();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.UserScore):
|
||||
case nameof(Room.MaxAttempts):
|
||||
updateAttempts();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAttempts()
|
||||
{
|
||||
if (MaxAttempts.Value != null)
|
||||
if (room.MaxAttempts != null)
|
||||
{
|
||||
attemptDisplay.Text = $"Maximum attempts: {MaxAttempts.Value:N0}";
|
||||
attemptDisplay.Text = $"Maximum attempts: {room.MaxAttempts:N0}";
|
||||
|
||||
if (UserScore.Value != null)
|
||||
if (room.UserScore != null)
|
||||
{
|
||||
int remaining = MaxAttempts.Value.Value - UserScore.Value.PlaylistItemAttempts.Sum(a => a.Attempts);
|
||||
int remaining = room.MaxAttempts.Value - room.UserScore.PlaylistItemAttempts.Sum(a => a.Attempts);
|
||||
attemptDisplay.Text += $" ({remaining} remaining)";
|
||||
|
||||
if (remaining == 0)
|
||||
@@ -69,5 +82,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
attemptDisplay.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Development;
|
||||
@@ -20,18 +17,17 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class RoomManager : Component, IRoomManager
|
||||
{
|
||||
[CanBeNull]
|
||||
public event Action RoomsUpdated;
|
||||
public event Action? RoomsUpdated;
|
||||
|
||||
private readonly BindableList<Room> rooms = new BindableList<Room>();
|
||||
|
||||
public IBindableList<Room> Rooms => rooms;
|
||||
|
||||
protected IBindable<Room> JoinedRoom => joinedRoom;
|
||||
private readonly Bindable<Room> joinedRoom = new Bindable<Room>();
|
||||
protected IBindable<Room?> JoinedRoom => joinedRoom;
|
||||
private readonly Bindable<Room?> joinedRoom = new Bindable<Room?>();
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
public RoomManager()
|
||||
{
|
||||
@@ -44,9 +40,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
PartRoom();
|
||||
}
|
||||
|
||||
public virtual void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
public virtual void CreateRoom(Room room, Action<Room>? onSuccess = null, Action<string>? onError = null)
|
||||
{
|
||||
room.Host.Value = api.LocalUser.Value;
|
||||
room.Host = api.LocalUser.Value;
|
||||
|
||||
var req = new CreateRoomRequest(room);
|
||||
|
||||
@@ -69,9 +65,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
api.Queue(req);
|
||||
}
|
||||
|
||||
private JoinRoomRequest currentJoinRoomRequest;
|
||||
private JoinRoomRequest? currentJoinRoomRequest;
|
||||
|
||||
public virtual void JoinRoom(Room room, string password = null, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
public virtual void JoinRoom(Room room, string? password = null, Action<Room>? onSuccess = null, Action<string>? onError = null)
|
||||
{
|
||||
currentJoinRoomRequest?.Cancel();
|
||||
currentJoinRoomRequest = new JoinRoomRequest(room, password);
|
||||
@@ -97,7 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
currentJoinRoomRequest?.Cancel();
|
||||
|
||||
if (JoinedRoom.Value == null)
|
||||
if (joinedRoom.Value == null)
|
||||
return;
|
||||
|
||||
if (api.State.Value == APIState.Online)
|
||||
@@ -111,14 +107,14 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
public void AddOrUpdateRoom(Room room)
|
||||
{
|
||||
Debug.Assert(ThreadSafety.IsUpdateThread);
|
||||
Debug.Assert(room.RoomID.Value != null);
|
||||
Debug.Assert(room.RoomID != null);
|
||||
|
||||
if (ignoredRooms.Contains(room.RoomID.Value.Value))
|
||||
if (ignoredRooms.Contains(room.RoomID.Value))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var existing = rooms.FirstOrDefault(e => e.RoomID.Value == room.RoomID.Value);
|
||||
var existing = rooms.FirstOrDefault(e => e.RoomID == room.RoomID);
|
||||
if (existing == null)
|
||||
rooms.Add(room);
|
||||
else
|
||||
@@ -126,9 +122,9 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, $"Failed to update room: {room.Name.Value}.");
|
||||
Logger.Error(ex, $"Failed to update room: {room.Name}.");
|
||||
|
||||
ignoredRooms.Add(room.RoomID.Value.Value);
|
||||
ignoredRooms.Add(room.RoomID.Value);
|
||||
rooms.Remove(room);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
@@ -20,21 +18,21 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
private GetRoomRequest lastPollRequest;
|
||||
private GetRoomRequest? lastPollRequest;
|
||||
|
||||
protected override Task Poll()
|
||||
{
|
||||
if (!API.IsLoggedIn)
|
||||
return base.Poll();
|
||||
|
||||
if (room.RoomID.Value == null)
|
||||
if (room.RoomID == null)
|
||||
return base.Poll();
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
lastPollRequest?.Cancel();
|
||||
|
||||
var req = new GetRoomRequest(room.RoomID.Value.Value);
|
||||
var req = new GetRoomRequest(room.RoomID.Value);
|
||||
|
||||
req.Success += result =>
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -13,22 +12,27 @@ using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osuTK;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class StarRatingRangeDisplay : OnlinePlayComposite
|
||||
public partial class StarRatingRangeDisplay : CompositeDrawable
|
||||
{
|
||||
private readonly Room room;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
private StarRatingDisplay minDisplay;
|
||||
private Drawable minBackground;
|
||||
private StarRatingDisplay maxDisplay;
|
||||
private Drawable maxBackground;
|
||||
private StarRatingDisplay minDisplay = null!;
|
||||
private Drawable minBackground = null!;
|
||||
private StarRatingDisplay maxDisplay = null!;
|
||||
private Drawable maxBackground = null!;
|
||||
|
||||
public StarRatingRangeDisplay()
|
||||
public StarRatingRangeDisplay(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
@@ -76,8 +80,19 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
DifficultyRange.BindValueChanged(_ => updateRange());
|
||||
Playlist.BindCollectionChanged((_, _) => updateRange(), true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRange();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.Playlist):
|
||||
case nameof(Room.DifficultyRange):
|
||||
updateRange();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRange()
|
||||
@@ -85,16 +100,16 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
StarDifficulty minDifficulty;
|
||||
StarDifficulty maxDifficulty;
|
||||
|
||||
if (DifficultyRange.Value != null && Playlist.Count == 0)
|
||||
if (room.DifficultyRange != null && room.Playlist.Count == 0)
|
||||
{
|
||||
// When Playlist is empty (in lounge) we take retrieved range
|
||||
minDifficulty = new StarDifficulty(DifficultyRange.Value.Min, 0);
|
||||
maxDifficulty = new StarDifficulty(DifficultyRange.Value.Max, 0);
|
||||
minDifficulty = new StarDifficulty(room.DifficultyRange.Min, 0);
|
||||
maxDifficulty = new StarDifficulty(room.DifficultyRange.Max, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When Playlist is not empty (in room) we compute actual range
|
||||
var orderedDifficulties = Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray();
|
||||
var orderedDifficulties = room.Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray();
|
||||
|
||||
minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarRating : 0, 0);
|
||||
maxDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[^1].StarRating : 0, 0);
|
||||
@@ -107,5 +122,11 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
minBackground.Colour = colours.ForStarDifficulty(minDifficulty.Stars);
|
||||
maxBackground.Colour = colours.ForStarDifficulty(maxDifficulty.Stars);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,52 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Components
|
||||
{
|
||||
public partial class StatusColouredContainer : Container
|
||||
{
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
private readonly double transitionDuration;
|
||||
private readonly Room room;
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.Status))]
|
||||
private Bindable<RoomStatus> status { get; set; }
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.Category))]
|
||||
private Bindable<RoomCategory> category { get; set; }
|
||||
|
||||
public StatusColouredContainer(double transitionDuration = 100)
|
||||
public StatusColouredContainer(Room room, double transitionDuration = 100)
|
||||
{
|
||||
this.room = room;
|
||||
this.transitionDuration = transitionDuration;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
status.BindValueChanged(s =>
|
||||
{
|
||||
this.FadeColour(colours.ForRoomCategory(category.Value) ?? s.NewValue.GetAppropriateColour(colours), transitionDuration);
|
||||
}, true);
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomStatus();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.Status))
|
||||
updateRoomStatus();
|
||||
}
|
||||
|
||||
private void updateRoomStatus()
|
||||
{
|
||||
this.FadeColour(colours.ForRoomCategory(room.Category) ?? room.Status.GetAppropriateColour(colours), transitionDuration);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,14 +119,6 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING };
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
return new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent))
|
||||
{
|
||||
Model = { Value = room }
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
@@ -228,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DailyChallengeTimeRemainingRing(),
|
||||
new DailyChallengeTimeRemainingRing(room),
|
||||
breakdown = new DailyChallengeScoreBreakdown(),
|
||||
totals = new DailyChallengeTotalsDisplay(),
|
||||
}
|
||||
@@ -301,7 +293,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
Spacing = new Vector2(10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new PlaylistsReadyButton
|
||||
new PlaylistsReadyButton(room)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -353,12 +345,12 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
private void presentScore(long id)
|
||||
{
|
||||
if (this.IsCurrentScreen())
|
||||
this.Push(new PlaylistItemScoreResultsScreen(room.RoomID.Value!.Value, playlistItem, id));
|
||||
this.Push(new PlaylistItemScoreResultsScreen(room.RoomID!.Value, playlistItem, id));
|
||||
}
|
||||
|
||||
private void onRoomScoreSet(MultiplayerRoomScoreSetEvent e)
|
||||
{
|
||||
if (e.RoomID != room.RoomID.Value || e.PlaylistItemID != playlistItem.ID)
|
||||
if (e.RoomID != room.RoomID || e.PlaylistItemID != playlistItem.ID)
|
||||
return;
|
||||
|
||||
userLookupCache.GetUserAsync(e.UserID).ContinueWith(t =>
|
||||
@@ -410,7 +402,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
|
||||
private void dailyChallengeChanged(ValueChangedEvent<DailyChallengeInfo?> change)
|
||||
{
|
||||
if (change.OldValue?.RoomID == room.RoomID.Value && change.NewValue == null && metadataClient.IsConnected.Value)
|
||||
if (change.OldValue?.RoomID == room.RoomID && change.NewValue == null && metadataClient.IsConnected.Value)
|
||||
{
|
||||
notificationOverlay?.Post(new SimpleNotification { Text = DailyChallengeStrings.ChallengeEndedNotification });
|
||||
}
|
||||
@@ -437,7 +429,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
roomManager.JoinRoom(room);
|
||||
startLoopingTrack(this, musicController);
|
||||
|
||||
metadataClient.BeginWatchingMultiplayerRoom(room.RoomID.Value!.Value).ContinueWith(t =>
|
||||
metadataClient.BeginWatchingMultiplayerRoom(room.RoomID!.Value).ContinueWith(t =>
|
||||
{
|
||||
if (t.Exception != null)
|
||||
{
|
||||
@@ -489,7 +481,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
|
||||
|
||||
roomManager.PartRoom();
|
||||
metadataClient.EndWatchingMultiplayerRoom(room.RoomID.Value!.Value).FireAndForget();
|
||||
metadataClient.EndWatchingMultiplayerRoom(room.RoomID!.Value).FireAndForget();
|
||||
|
||||
return base.OnExiting(e);
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = room.Name.Value.Split(':', StringSplitOptions.TrimEntries).Last(),
|
||||
Text = room.Name.Split(':', StringSplitOptions.TrimEntries).Last(),
|
||||
Margin = new MarginPadding { Horizontal = 10f, Vertical = 5f },
|
||||
Shear = new Vector2(-OsuGame.SHEAR, 0f),
|
||||
Font = OsuFont.GetFont(size: 32, weight: FontWeight.Light, typeface: Typeface.TorusAlternate),
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
if (request?.CompletionState == APIRequestCompletionState.Waiting)
|
||||
return;
|
||||
|
||||
request = new IndexPlaylistScoresRequest(room.RoomID.Value!.Value, playlistItem.ID);
|
||||
request = new IndexPlaylistScoresRequest(room.RoomID!.Value, playlistItem.ID);
|
||||
|
||||
request.Success += req => Schedule(() =>
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -10,15 +11,15 @@ using osu.Framework.Threading;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
{
|
||||
public partial class DailyChallengeTimeRemainingRing : OnlinePlayComposite
|
||||
public partial class DailyChallengeTimeRemainingRing : CompositeDrawable
|
||||
{
|
||||
private CircularProgress progress = null!;
|
||||
private OsuSpriteText timeText = null!;
|
||||
private readonly Room room;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
@@ -26,6 +27,14 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
private CircularProgress progress = null!;
|
||||
private OsuSpriteText timeText = null!;
|
||||
|
||||
public DailyChallengeTimeRemainingRing(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@@ -90,12 +99,23 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
StartDate.BindValueChanged(_ => Scheduler.AddOnce(updateState));
|
||||
EndDate.BindValueChanged(_ => Scheduler.AddOnce(updateState));
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateState();
|
||||
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.StartDate):
|
||||
case nameof(Room.EndDate):
|
||||
Scheduler.AddOnce(updateState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private ScheduledDelegate? scheduledUpdate;
|
||||
|
||||
private void updateState()
|
||||
@@ -105,7 +125,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
|
||||
const float transition_duration = 300;
|
||||
|
||||
if (StartDate.Value == null || EndDate.Value == null || EndDate.Value < DateTimeOffset.Now)
|
||||
if (room.StartDate == null || room.EndDate == null || room.EndDate < DateTimeOffset.Now)
|
||||
{
|
||||
timeText.Text = TimeSpan.Zero.ToString(@"hh\:mm\:ss");
|
||||
progress.Progress = 0;
|
||||
@@ -114,8 +134,8 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
return;
|
||||
}
|
||||
|
||||
var roomDuration = EndDate.Value.Value - StartDate.Value.Value;
|
||||
var remaining = EndDate.Value.Value - DateTimeOffset.Now;
|
||||
var roomDuration = room.EndDate.Value - room.StartDate.Value;
|
||||
var remaining = room.EndDate.Value - DateTimeOffset.Now;
|
||||
|
||||
timeText.Text = remaining.ToString(@"hh\:mm\:ss");
|
||||
progress.Progress = remaining.TotalSeconds / roomDuration.TotalSeconds;
|
||||
@@ -138,5 +158,11 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
|
||||
scheduledUpdate = Scheduler.AddDelayed(updateState, 1000);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@@ -16,6 +16,7 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@@ -26,30 +27,33 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public partial class DrawableRoom : CompositeDrawable
|
||||
public abstract partial class DrawableRoom : CompositeDrawable
|
||||
{
|
||||
protected const float CORNER_RADIUS = 10;
|
||||
private const float height = 100;
|
||||
|
||||
public readonly Room Room;
|
||||
|
||||
protected Container ButtonsContainer { get; private set; }
|
||||
protected readonly Bindable<PlaylistItem?> SelectedItem = new Bindable<PlaylistItem?>();
|
||||
protected Container ButtonsContainer { get; private set; } = null!;
|
||||
|
||||
private readonly Bindable<MatchType> roomType = new Bindable<MatchType>();
|
||||
private readonly Bindable<RoomCategory> roomCategory = new Bindable<RoomCategory>();
|
||||
private readonly Bindable<bool> hasPassword = new Bindable<bool>();
|
||||
|
||||
private DrawableRoomParticipantsList drawableRoomParticipantsList;
|
||||
private RoomSpecialCategoryPill specialCategoryPill;
|
||||
private PasswordProtectedIcon passwordIcon;
|
||||
private EndDateInfo endDateInfo;
|
||||
private DrawableRoomParticipantsList? drawableRoomParticipantsList;
|
||||
private RoomSpecialCategoryPill? specialCategoryPill;
|
||||
private PasswordProtectedIcon? passwordIcon;
|
||||
private EndDateInfo? endDateInfo;
|
||||
private SpriteText? roomName;
|
||||
private UpdateableBeatmapBackgroundSprite background = null!;
|
||||
private DelayedLoadWrapper wrapper = null!;
|
||||
|
||||
private DelayedLoadWrapper wrapper;
|
||||
|
||||
public DrawableRoom(Room room)
|
||||
protected DrawableRoom(Room room)
|
||||
{
|
||||
Room = room;
|
||||
|
||||
@@ -77,7 +81,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
AutoSizeAxes = Axes.X
|
||||
};
|
||||
|
||||
InternalChildren = new[]
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
// This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites.
|
||||
new Box
|
||||
@@ -85,7 +89,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.Background5,
|
||||
},
|
||||
CreateBackground().With(d =>
|
||||
background = CreateBackground().With(d =>
|
||||
{
|
||||
d.RelativeSizeAxes = Axes.Both;
|
||||
}),
|
||||
@@ -155,17 +159,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new RoomStatusPill
|
||||
new RoomStatusPill(Room)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
},
|
||||
specialCategoryPill = new RoomSpecialCategoryPill
|
||||
specialCategoryPill = new RoomSpecialCategoryPill(Room)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
},
|
||||
endDateInfo = new EndDateInfo
|
||||
endDateInfo = new EndDateInfo(Room)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
@@ -180,13 +184,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TruncatingSpriteText
|
||||
roomName = new TruncatingSpriteText
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Font = OsuFont.GetFont(size: 28),
|
||||
Current = { BindTarget = Room.Name }
|
||||
Font = OsuFont.GetFont(size: 28)
|
||||
},
|
||||
new RoomStatusText()
|
||||
new RoomStatusText(Room)
|
||||
{
|
||||
SelectedItem = { BindTarget = SelectedItem }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -218,7 +224,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
Children = new Drawable[]
|
||||
{
|
||||
ButtonsContainer,
|
||||
drawableRoomParticipantsList = new DrawableRoomParticipantsList
|
||||
drawableRoomParticipantsList = new DrawableRoomParticipantsList(Room)
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
@@ -243,36 +249,71 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Room.PropertyChanged += onRoomPropertyChanged;
|
||||
|
||||
wrapper.DelayedLoadComplete += _ =>
|
||||
{
|
||||
Debug.Assert(specialCategoryPill != null);
|
||||
Debug.Assert(endDateInfo != null);
|
||||
Debug.Assert(passwordIcon != null);
|
||||
|
||||
wrapper.FadeInFromZero(200);
|
||||
|
||||
roomCategory.BindTo(Room.Category);
|
||||
roomCategory.BindValueChanged(c =>
|
||||
{
|
||||
if (c.NewValue > RoomCategory.Normal)
|
||||
specialCategoryPill.Show();
|
||||
else
|
||||
specialCategoryPill.Hide();
|
||||
}, true);
|
||||
|
||||
roomType.BindTo(Room.Type);
|
||||
roomType.BindValueChanged(t =>
|
||||
{
|
||||
endDateInfo.Alpha = t.NewValue == MatchType.Playlists ? 1 : 0;
|
||||
}, true);
|
||||
|
||||
hasPassword.BindTo(Room.HasPassword);
|
||||
hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true);
|
||||
updateRoomName();
|
||||
updateRoomCategory();
|
||||
updateRoomType();
|
||||
updateRoomHasPassword();
|
||||
};
|
||||
|
||||
SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true);
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
return new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent))
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
Model = { Value = Room }
|
||||
};
|
||||
case nameof(Room.Name):
|
||||
updateRoomName();
|
||||
break;
|
||||
|
||||
case nameof(Room.Category):
|
||||
updateRoomCategory();
|
||||
break;
|
||||
|
||||
case nameof(Room.Type):
|
||||
updateRoomType();
|
||||
break;
|
||||
|
||||
case nameof(Room.HasPassword):
|
||||
updateRoomHasPassword();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomName()
|
||||
{
|
||||
if (roomName != null)
|
||||
roomName.Text = Room.Name;
|
||||
}
|
||||
|
||||
private void updateRoomCategory()
|
||||
{
|
||||
if (Room.Category > RoomCategory.Normal)
|
||||
specialCategoryPill?.Show();
|
||||
else
|
||||
specialCategoryPill?.Hide();
|
||||
}
|
||||
|
||||
private void updateRoomType()
|
||||
{
|
||||
if (endDateInfo != null)
|
||||
endDateInfo.Alpha = Room.Type == MatchType.Playlists ? 1 : 0;
|
||||
}
|
||||
|
||||
private void updateRoomHasPassword()
|
||||
{
|
||||
if (passwordIcon != null)
|
||||
passwordIcon.Alpha = Room.HasPassword ? 1 : 0;
|
||||
}
|
||||
|
||||
private int numberOfAvatars = 7;
|
||||
@@ -289,29 +330,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Drawable CreateBackground() => new OnlinePlayBackgroundSprite();
|
||||
protected virtual UpdateableBeatmapBackgroundSprite CreateBackground() => new UpdateableBeatmapBackgroundSprite();
|
||||
|
||||
protected virtual IEnumerable<Drawable> CreateBottomDetails()
|
||||
{
|
||||
var pills = new List<Drawable>();
|
||||
|
||||
if (Room.Type.Value != MatchType.Playlists)
|
||||
if (Room.Type != MatchType.Playlists)
|
||||
{
|
||||
pills.AddRange(new OnlinePlayComposite[]
|
||||
pills.AddRange(new Drawable[]
|
||||
{
|
||||
new MatchTypePill(),
|
||||
new QueueModePill(),
|
||||
new MatchTypePill(Room),
|
||||
new QueueModePill(Room),
|
||||
});
|
||||
}
|
||||
|
||||
pills.AddRange(new Drawable[]
|
||||
{
|
||||
new PlaylistCountPill
|
||||
new PlaylistCountPill(Room)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
new StarRatingRangeDisplay
|
||||
new StarRatingRangeDisplay(Room)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
@@ -322,19 +363,29 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
return pills;
|
||||
}
|
||||
|
||||
private partial class RoomStatusText : OnlinePlayComposite
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
base.Dispose(isDisposing);
|
||||
Room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
|
||||
private partial class RoomStatusText : CompositeDrawable
|
||||
{
|
||||
public readonly IBindable<PlaylistItem?> SelectedItem = new Bindable<PlaylistItem?>();
|
||||
|
||||
[Resolved]
|
||||
private BeatmapLookupCache beatmapLookupCache { get; set; }
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
private SpriteText statusText;
|
||||
private LinkFlowContainer beatmapText;
|
||||
[Resolved]
|
||||
private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
|
||||
|
||||
public RoomStatusText()
|
||||
private readonly Room room;
|
||||
private SpriteText statusText = null!;
|
||||
private LinkFlowContainer beatmapText = null!;
|
||||
|
||||
public RoomStatusText(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
}
|
||||
@@ -383,17 +434,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
CurrentPlaylistItem.BindValueChanged(onSelectedItemChanged, true);
|
||||
SelectedItem.BindValueChanged(onSelectedItemChanged, true);
|
||||
}
|
||||
|
||||
private CancellationTokenSource beatmapLookupCancellation;
|
||||
private CancellationTokenSource? beatmapLookupCancellation;
|
||||
|
||||
private void onSelectedItemChanged(ValueChangedEvent<PlaylistItem> item)
|
||||
private void onSelectedItemChanged(ValueChangedEvent<PlaylistItem?> item)
|
||||
{
|
||||
beatmapLookupCancellation?.Cancel();
|
||||
beatmapText.Clear();
|
||||
|
||||
if (Type.Value == MatchType.Playlists)
|
||||
if (room.Type == MatchType.Playlists)
|
||||
{
|
||||
statusText.Text = "Ready to play";
|
||||
return;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@@ -16,31 +13,33 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public partial class DrawableRoomParticipantsList : OnlinePlayComposite
|
||||
public partial class DrawableRoomParticipantsList : CompositeDrawable
|
||||
{
|
||||
public const float SHEAR_WIDTH = 12f;
|
||||
|
||||
private const float avatar_size = 36;
|
||||
|
||||
private const float height = 60f;
|
||||
|
||||
private static readonly Vector2 shear = new Vector2(SHEAR_WIDTH / height, 0);
|
||||
|
||||
private FillFlowContainer<CircularAvatar> avatarFlow;
|
||||
private readonly Room room;
|
||||
|
||||
private CircularAvatar hostAvatar;
|
||||
private LinkFlowContainer hostText;
|
||||
private HiddenUserCount hiddenUsers;
|
||||
private OsuSpriteText totalCount;
|
||||
private FillFlowContainer<CircularAvatar> avatarFlow = null!;
|
||||
private CircularAvatar hostAvatar = null!;
|
||||
private LinkFlowContainer hostText = null!;
|
||||
private HiddenUserCount hiddenUsers = null!;
|
||||
private OsuSpriteText totalCount = null!;
|
||||
|
||||
public DrawableRoomParticipantsList()
|
||||
public DrawableRoomParticipantsList(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
|
||||
AutoSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
}
|
||||
@@ -165,14 +164,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
RecentParticipants.BindCollectionChanged(onParticipantsChanged, true);
|
||||
ParticipantCount.BindValueChanged(_ =>
|
||||
{
|
||||
updateHiddenUsers();
|
||||
totalCount.Text = ParticipantCount.Value.ToString();
|
||||
}, true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
|
||||
Host.BindValueChanged(onHostChanged, true);
|
||||
updateRoomHost();
|
||||
updateRoomParticipantCount();
|
||||
updateRoomParticipants();
|
||||
}
|
||||
|
||||
private int numberOfCircles = 4;
|
||||
@@ -192,43 +188,38 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
// Reinitialising the list looks janky, but this is unlikely to be used in a setting where it's visible.
|
||||
clearUsers();
|
||||
foreach (var u in RecentParticipants)
|
||||
foreach (var u in room.RecentParticipants)
|
||||
addUser(u);
|
||||
|
||||
updateHiddenUsers();
|
||||
}
|
||||
}
|
||||
|
||||
private void onParticipantsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
private void updateRoomParticipants()
|
||||
{
|
||||
switch (e.Action)
|
||||
HashSet<APIUser> newUsers = room.RecentParticipants.ToHashSet();
|
||||
|
||||
avatarFlow.RemoveAll(a =>
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
Debug.Assert(e.NewItems != null);
|
||||
// Avatar with no user. Really shouldn't ever be the case but asserting it correctly is difficult.
|
||||
if (a.User == null)
|
||||
return false;
|
||||
|
||||
foreach (var added in e.NewItems.OfType<APIUser>())
|
||||
addUser(added);
|
||||
break;
|
||||
// User was previously and still is a participant. Keep them around but remove them from the new set.
|
||||
// This will be useful when we add all remaining users (now just the new participants) to the flow.
|
||||
if (newUsers.Contains(a.User))
|
||||
{
|
||||
newUsers.Remove(a.User);
|
||||
return false;
|
||||
}
|
||||
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
Debug.Assert(e.OldItems != null);
|
||||
// User is no longer a participant. Remove them from the flow.
|
||||
return true;
|
||||
}, true);
|
||||
|
||||
foreach (var removed in e.OldItems.OfType<APIUser>())
|
||||
removeUser(removed);
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
clearUsers();
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
// Easiest is to just reinitialise the whole list. These are unlikely to ever be use cases.
|
||||
clearUsers();
|
||||
foreach (var u in RecentParticipants)
|
||||
addUser(u);
|
||||
break;
|
||||
}
|
||||
// Add all remaining users to the flow.
|
||||
foreach (var u in newUsers)
|
||||
addUser(u);
|
||||
|
||||
updateHiddenUsers();
|
||||
}
|
||||
@@ -241,11 +232,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
avatarFlow.Add(new CircularAvatar { User = user });
|
||||
}
|
||||
|
||||
private void removeUser(APIUser user)
|
||||
{
|
||||
avatarFlow.RemoveAll(a => a.User == user, true);
|
||||
}
|
||||
|
||||
private void clearUsers()
|
||||
{
|
||||
avatarFlow.Clear();
|
||||
@@ -255,8 +241,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
private void updateHiddenUsers()
|
||||
{
|
||||
int hiddenCount = 0;
|
||||
if (RecentParticipants.Count > NumberOfCircles)
|
||||
hiddenCount = ParticipantCount.Value - NumberOfCircles + 1;
|
||||
if (room.RecentParticipants.Count > NumberOfCircles)
|
||||
hiddenCount = room.ParticipantCount - NumberOfCircles + 1;
|
||||
|
||||
hiddenUsers.Count = hiddenCount;
|
||||
|
||||
@@ -264,26 +250,56 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
avatarFlow.Remove(avatarFlow.Last(), true);
|
||||
else if (displayedCircles < NumberOfCircles)
|
||||
{
|
||||
var nextUser = RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u));
|
||||
var nextUser = room.RecentParticipants.FirstOrDefault(u => avatarFlow.All(a => a.User != u));
|
||||
if (nextUser != null) addUser(nextUser);
|
||||
}
|
||||
}
|
||||
|
||||
private void onHostChanged(ValueChangedEvent<APIUser> host)
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
hostAvatar.User = host.NewValue;
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.Host):
|
||||
updateRoomHost();
|
||||
break;
|
||||
|
||||
case nameof(Room.ParticipantCount):
|
||||
updateRoomParticipantCount();
|
||||
break;
|
||||
|
||||
case nameof(Room.RecentParticipants):
|
||||
updateRoomParticipants();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomHost()
|
||||
{
|
||||
hostAvatar.User = room.Host;
|
||||
hostText.Clear();
|
||||
|
||||
if (host.NewValue != null)
|
||||
if (room.Host != null)
|
||||
{
|
||||
hostText.AddText("hosted by ");
|
||||
hostText.AddUserLink(host.NewValue);
|
||||
hostText.AddUserLink(room.Host);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomParticipantCount()
|
||||
{
|
||||
updateHiddenUsers();
|
||||
totalCount.Text = room.ParticipantCount.ToString();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
|
||||
private partial class CircularAvatar : CompositeDrawable
|
||||
{
|
||||
public APIUser User
|
||||
public APIUser? User
|
||||
{
|
||||
get => avatar.User;
|
||||
set => avatar.User = value;
|
||||
|
||||
@@ -2,49 +2,69 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public partial class EndDateInfo : OnlinePlayComposite
|
||||
public partial class EndDateInfo : CompositeDrawable
|
||||
{
|
||||
public EndDateInfo()
|
||||
private readonly Room room;
|
||||
|
||||
public EndDateInfo(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = new EndDatePart
|
||||
InternalChild = new EndDatePart(room)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12),
|
||||
EndDate = { BindTarget = EndDate }
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12)
|
||||
};
|
||||
}
|
||||
|
||||
private partial class EndDatePart : DrawableDate
|
||||
{
|
||||
public readonly IBindable<DateTimeOffset?> EndDate = new Bindable<DateTimeOffset?>();
|
||||
private readonly Room room;
|
||||
|
||||
public EndDatePart()
|
||||
public EndDatePart(Room room)
|
||||
: base(DateTimeOffset.UtcNow)
|
||||
{
|
||||
EndDate.BindValueChanged(date =>
|
||||
{
|
||||
// If null, set a very large future date to prevent unnecessary schedules.
|
||||
Date = date.NewValue ?? DateTimeOffset.Now.AddYears(1);
|
||||
}, true);
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateEndDate();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.EndDate))
|
||||
updateEndDate();
|
||||
}
|
||||
|
||||
private void updateEndDate()
|
||||
{
|
||||
// If null, set a very large future date to prevent unnecessary schedules.
|
||||
Date = room.EndDate ?? DateTimeOffset.Now.AddYears(1);
|
||||
}
|
||||
|
||||
protected override string Format()
|
||||
{
|
||||
if (EndDate.Value == null)
|
||||
if (room.EndDate == null)
|
||||
return string.Empty;
|
||||
|
||||
var diffToNow = Date.Subtract(DateTimeOffset.Now);
|
||||
@@ -60,6 +80,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
return $"Closing {base.Format()}";
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +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 osu.Framework.Bindables;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
@@ -9,16 +9,36 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public partial class MatchTypePill : OnlinePlayPill
|
||||
{
|
||||
private readonly Room room;
|
||||
|
||||
public MatchTypePill(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Type.BindValueChanged(onMatchTypeChanged, true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomType();
|
||||
}
|
||||
|
||||
private void onMatchTypeChanged(ValueChangedEvent<MatchType> type)
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
TextFlow.Text = type.NewValue.GetLocalisableDescription();
|
||||
if (e.PropertyName == nameof(Room.Type))
|
||||
updateRoomType();
|
||||
}
|
||||
|
||||
private void updateRoomType()
|
||||
{
|
||||
TextFlow.Text = room.Type.GetLocalisableDescription();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public abstract partial class OnlinePlayPill : OnlinePlayComposite
|
||||
public abstract partial class OnlinePlayPill : CompositeDrawable
|
||||
{
|
||||
protected PillContainer Pill { get; private set; } = null!;
|
||||
protected OsuTextFlowContainer TextFlow { get; private set; } = null!;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
@@ -13,26 +15,50 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
/// </summary>
|
||||
public partial class PlaylistCountPill : OnlinePlayPill
|
||||
{
|
||||
private readonly Room room;
|
||||
|
||||
public PlaylistCountPill(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
PlaylistItemStats.BindValueChanged(_ => updateCount());
|
||||
Playlist.BindCollectionChanged((_, _) => updateCount(), true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateCount();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.Playlist):
|
||||
case nameof(Room.PlaylistItemStats):
|
||||
updateCount();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCount()
|
||||
{
|
||||
int activeItems = Playlist.Count > 0 || PlaylistItemStats.Value == null
|
||||
int activeItems = room.Playlist.Count > 0 || room.PlaylistItemStats == null
|
||||
// For now, use the playlist as the source of truth if it has any items.
|
||||
// This allows the count to display correctly on the room screen (after joining a room).
|
||||
? Playlist.Count(i => !i.Expired)
|
||||
: PlaylistItemStats.Value.CountActive;
|
||||
? room.Playlist.Count(i => !i.Expired)
|
||||
: room.PlaylistItemStats.CountActive;
|
||||
|
||||
TextFlow.Clear();
|
||||
TextFlow.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold));
|
||||
TextFlow.AddText(" ");
|
||||
TextFlow.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,42 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public partial class QueueModePill : OnlinePlayPill
|
||||
{
|
||||
private readonly Room room;
|
||||
|
||||
public QueueModePill(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
QueueMode.BindValueChanged(onQueueModeChanged, true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomQueueMode();
|
||||
}
|
||||
|
||||
private void onQueueModeChanged(ValueChangedEvent<QueueMode> mode)
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
TextFlow.Text = mode.NewValue.GetLocalisableDescription();
|
||||
if (e.PropertyName == nameof(Room.QueueMode))
|
||||
updateRoomQueueMode();
|
||||
}
|
||||
|
||||
private void updateRoomQueueMode()
|
||||
=> TextFlow.Text = room.QueueMode.GetLocalisableDescription();
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
// 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.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public partial class RoomSpecialCategoryPill : OnlinePlayPill
|
||||
{
|
||||
private readonly Room room;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
protected override FontUsage Font => base.Font.With(weight: FontWeight.SemiBold);
|
||||
|
||||
public RoomSpecialCategoryPill(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@@ -23,11 +32,26 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
Pill.Background.Alpha = 1;
|
||||
TextFlow.Colour = Color4.Black;
|
||||
|
||||
Category.BindValueChanged(c =>
|
||||
{
|
||||
TextFlow.Text = c.NewValue.GetLocalisableDescription();
|
||||
Pill.Background.Colour = colours.ForRoomCategory(c.NewValue) ?? colours.Pink;
|
||||
}, true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomCategory();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.Category))
|
||||
updateRoomCategory();
|
||||
}
|
||||
|
||||
private void updateRoomCategory()
|
||||
{
|
||||
TextFlow.Text = room.Category.GetLocalisableDescription();
|
||||
Pill.Background.Colour = colours.ForRoomCategory(room.Category) ?? colours.Pink;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +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.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@@ -19,25 +20,47 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
protected override FontUsage Font => base.Font.With(weight: FontWeight.SemiBold);
|
||||
|
||||
private readonly Room room;
|
||||
|
||||
public RoomStatusPill(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
EndDate.BindValueChanged(_ => updateDisplay());
|
||||
Status.BindValueChanged(_ => updateDisplay(), true);
|
||||
|
||||
FinishTransforms(true);
|
||||
|
||||
TextFlow.Colour = Colour4.Black;
|
||||
Pill.Background.Alpha = 1;
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateDisplay();
|
||||
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.Status):
|
||||
case nameof(Room.EndDate):
|
||||
updateDisplay();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
RoomStatus status = Status.Value;
|
||||
Pill.Background.FadeColour(room.Status.GetAppropriateColour(colours), 100);
|
||||
TextFlow.Text = room.Status.Message;
|
||||
}
|
||||
|
||||
Pill.Background.FadeColour(status.GetAppropriateColour(colours), 100);
|
||||
TextFlow.Text = status.Message;
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
@@ -11,6 +9,7 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
@@ -24,8 +23,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
public partial class RoomsContainer : CompositeDrawable, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
public readonly Bindable<Room> SelectedRoom = new Bindable<Room>();
|
||||
public readonly Bindable<FilterCriteria> Filter = new Bindable<FilterCriteria>();
|
||||
public readonly Bindable<Room?> SelectedRoom = new Bindable<Room?>();
|
||||
public readonly Bindable<FilterCriteria?> Filter = new Bindable<FilterCriteria?>();
|
||||
|
||||
public IReadOnlyList<DrawableRoom> Rooms => roomFlow.FlowingChildren.Cast<DrawableRoom>().ToArray();
|
||||
|
||||
@@ -33,7 +32,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
private readonly FillFlowContainer<DrawableLoungeRoom> roomFlow;
|
||||
|
||||
[Resolved]
|
||||
private IRoomManager roomManager { get; set; }
|
||||
private IRoomManager roomManager { get; set; } = null!;
|
||||
|
||||
// handle deselection
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
@@ -67,10 +66,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
rooms.BindTo(roomManager.Rooms);
|
||||
|
||||
Filter?.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
|
||||
Filter.BindValueChanged(criteria => applyFilterCriteria(criteria.NewValue), true);
|
||||
}
|
||||
|
||||
private void applyFilterCriteria(FilterCriteria criteria)
|
||||
private void applyFilterCriteria(FilterCriteria? criteria)
|
||||
{
|
||||
roomFlow.Children.ForEach(r =>
|
||||
{
|
||||
@@ -80,7 +79,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
bool matchingFilter = true;
|
||||
|
||||
matchingFilter &= criteria.Ruleset == null || r.Room.PlaylistItemStats.Value?.RulesetIDs.Any(id => id == criteria.Ruleset.OnlineID) != false;
|
||||
matchingFilter &= criteria.Ruleset == null || r.Room.PlaylistItemStats?.RulesetIDs.Any(id => id == criteria.Ruleset.OnlineID) != false;
|
||||
|
||||
if (!string.IsNullOrEmpty(criteria.SearchString))
|
||||
{
|
||||
@@ -102,10 +101,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
return true;
|
||||
|
||||
case RoomPermissionsFilter.Public:
|
||||
return !room.Room.HasPassword.Value;
|
||||
return !room.Room.HasPassword;
|
||||
|
||||
case RoomPermissionsFilter.Private:
|
||||
return room.Room.HasPassword.Value;
|
||||
return room.Room.HasPassword;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(accessType), accessType, $"Unsupported {nameof(RoomPermissionsFilter)} in filter");
|
||||
@@ -113,7 +112,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void roomsChanged(object sender, NotifyCollectionChangedEventArgs args)
|
||||
private void roomsChanged(object? sender, NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
switch (args.Action)
|
||||
{
|
||||
@@ -140,9 +139,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
private void addRooms(IEnumerable<Room> rooms)
|
||||
{
|
||||
foreach (var room in rooms)
|
||||
roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = { BindTarget = SelectedRoom } });
|
||||
roomFlow.Add(new DrawableLoungeRoom(room) { SelectedRoom = SelectedRoom });
|
||||
|
||||
applyFilterCriteria(Filter?.Value);
|
||||
applyFilterCriteria(Filter.Value);
|
||||
}
|
||||
|
||||
private void removeRooms(IEnumerable<Room> rooms)
|
||||
@@ -170,10 +169,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
foreach (var room in roomFlow)
|
||||
{
|
||||
roomFlow.SetLayoutPosition(room, room.Room.Category.Value > RoomCategory.Normal
|
||||
roomFlow.SetLayoutPosition(room, room.Room.Category > RoomCategory.Normal
|
||||
// Always show spotlight playlists at the top of the listing.
|
||||
? float.MinValue
|
||||
: -(room.Room.RoomID.Value ?? 0));
|
||||
: -(room.Room.RoomID ?? 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +212,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent);
|
||||
|
||||
Room room;
|
||||
Room? room;
|
||||
|
||||
if (SelectedRoom.Value == null)
|
||||
room = visibleRooms.FirstOrDefault()?.Room;
|
||||
@@ -236,7 +235,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (roomManager != null)
|
||||
if (roomManager.IsNotNull())
|
||||
roomManager.RoomsUpdated -= updateSorting;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
@@ -28,6 +27,7 @@ using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
{
|
||||
@@ -39,14 +39,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
private const float transition_duration = 60;
|
||||
private const float selection_border_width = 4;
|
||||
|
||||
public readonly Bindable<Room> SelectedRoom = new Bindable<Room>();
|
||||
public required Bindable<Room?> SelectedRoom
|
||||
{
|
||||
get => selectedRoom;
|
||||
set => selectedRoom.Current = value;
|
||||
}
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private LoungeSubScreen lounge { get; set; }
|
||||
private LoungeSubScreen? lounge { get; set; }
|
||||
|
||||
private Sample sampleSelect;
|
||||
private Sample sampleJoin;
|
||||
private Drawable selectionBox;
|
||||
private readonly BindableWithCurrent<Room?> selectedRoom = new BindableWithCurrent<Room?>();
|
||||
private Sample? sampleSelect;
|
||||
private Sample? sampleJoin;
|
||||
private Drawable selectionBox = null!;
|
||||
|
||||
public DrawableLoungeRoom(Room room)
|
||||
: base(room)
|
||||
@@ -61,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
new StatusColouredContainer(transition_duration)
|
||||
new StatusColouredContainer(Room, transition_duration)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = selectionBox = new Container
|
||||
@@ -89,12 +94,24 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
base.LoadComplete();
|
||||
|
||||
Alpha = matchingFilter ? 1 : 0;
|
||||
selectionBox.Alpha = SelectedRoom.Value == Room ? 1 : 0;
|
||||
selectionBox.Alpha = selectedRoom.Value == Room ? 1 : 0;
|
||||
|
||||
SelectedRoom.BindValueChanged(updateSelectedRoom);
|
||||
selectedRoom.BindValueChanged(updateSelectedRoom);
|
||||
|
||||
Room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateSelectedItem();
|
||||
}
|
||||
|
||||
private void updateSelectedRoom(ValueChangedEvent<Room> selected)
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.CurrentPlaylistItem))
|
||||
updateSelectedItem();
|
||||
}
|
||||
|
||||
private void updateSelectedItem()
|
||||
=> SelectedItem.Value = Room.CurrentPlaylistItem;
|
||||
|
||||
private void updateSelectedRoom(ValueChangedEvent<Room?> selected)
|
||||
{
|
||||
if (selected.NewValue == Room)
|
||||
selectionBox.FadeIn(transition_duration);
|
||||
@@ -104,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
|
||||
public bool FilteringActive { get; set; }
|
||||
|
||||
public IEnumerable<LocalisableString> FilterTerms => new LocalisableString[] { Room.Name.Value };
|
||||
public IEnumerable<LocalisableString> FilterTerms => new LocalisableString[] { Room.Name };
|
||||
|
||||
private bool matchingFilter = true;
|
||||
|
||||
@@ -140,7 +157,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
||||
if (SelectedRoom.Value != Room)
|
||||
if (selectedRoom.Value != Room)
|
||||
return false;
|
||||
|
||||
switch (e.Action)
|
||||
@@ -157,18 +174,18 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ShouldBeConsideredForInput(Drawable child) => SelectedRoom.Value == Room || child is HoverSounds;
|
||||
protected override bool ShouldBeConsideredForInput(Drawable child) => selectedRoom.Value == Room || child is HoverSounds;
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if (Room != SelectedRoom.Value)
|
||||
if (Room != selectedRoom.Value)
|
||||
{
|
||||
sampleSelect?.Play();
|
||||
SelectedRoom.Value = Room;
|
||||
selectedRoom.Value = Room;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Room.HasPassword.Value)
|
||||
if (Room.HasPassword)
|
||||
{
|
||||
this.ShowPopover();
|
||||
return true;
|
||||
@@ -179,12 +196,18 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
Room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
|
||||
public partial class PasswordEntryPopover : OsuPopover
|
||||
{
|
||||
private readonly Room room;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private LoungeSubScreen lounge { get; set; }
|
||||
private LoungeSubScreen? lounge { get; set; }
|
||||
|
||||
public override bool HandleNonPositionalInput => true;
|
||||
|
||||
@@ -195,10 +218,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
private OsuPasswordTextBox passwordTextBox;
|
||||
private RoundedButton joinButton;
|
||||
private OsuSpriteText errorText;
|
||||
private Sample sampleJoinFail;
|
||||
private OsuPasswordTextBox passwordTextBox = null!;
|
||||
private RoundedButton joinButton = null!;
|
||||
private OsuSpriteText errorText = null!;
|
||||
private Sample? sampleJoinFail;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, AudioManager audio)
|
||||
|
||||
@@ -1,6 +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.ComponentModel;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Online.Rooms;
|
||||
@@ -19,21 +20,44 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
playlist.BindCollectionChanged((_, _) => PlaylistItem = playlist.GetCurrentItem());
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
SelectedRoom.BindValueChanged(onSelectedRoomChanged, true);
|
||||
}
|
||||
|
||||
private void onSelectedRoomChanged(ValueChangedEvent<Room?> room)
|
||||
{
|
||||
if (room.OldValue != null)
|
||||
playlist.UnbindFrom(room.OldValue.Playlist);
|
||||
room.OldValue.PropertyChanged -= onRoomPropertyChanged;
|
||||
|
||||
if (room.NewValue != null)
|
||||
playlist.BindTo(room.NewValue.Playlist);
|
||||
else
|
||||
playlist.Clear();
|
||||
room.NewValue.PropertyChanged += onRoomPropertyChanged;
|
||||
|
||||
updateCurrentItem();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.Playlist))
|
||||
updateCurrentItem();
|
||||
}
|
||||
|
||||
private void updateCurrentItem()
|
||||
=> PlaylistItem = SelectedRoom.Value?.Playlist.GetCurrentItem();
|
||||
|
||||
public override bool OnExiting(ScreenExitEvent e)
|
||||
{
|
||||
// This screen never exits.
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (SelectedRoom.Value != null)
|
||||
SelectedRoom.Value.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
selectionLease.Return();
|
||||
selectionLease = null;
|
||||
|
||||
if (SelectedRoom.Value?.RoomID.Value == null)
|
||||
if (SelectedRoom.Value?.RoomID == null)
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
music?.EnsurePlayingSomething();
|
||||
@@ -326,23 +326,23 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||
/// <param name="room">The room to copy.</param>
|
||||
public void OpenCopy(Room room)
|
||||
{
|
||||
Debug.Assert(room.RoomID.Value != null);
|
||||
Debug.Assert(room.RoomID != null);
|
||||
|
||||
if (joiningRoomOperation != null)
|
||||
return;
|
||||
|
||||
joiningRoomOperation = ongoingOperationTracker?.BeginOperation();
|
||||
|
||||
var req = new GetRoomRequest(room.RoomID.Value.Value);
|
||||
var req = new GetRoomRequest(room.RoomID.Value);
|
||||
|
||||
req.Success += r =>
|
||||
{
|
||||
// ID must be unset as we use this as a marker for whether this is a client-side (not-yet-created) room or not.
|
||||
r.RoomID.Value = null;
|
||||
r.RoomID = null;
|
||||
|
||||
// Null out dates because end date is not supported client-side and the settings overlay will populate a duration.
|
||||
r.EndDate.Value = null;
|
||||
r.Duration.Value = null;
|
||||
r.EndDate = null;
|
||||
r.Duration = null;
|
||||
|
||||
Open(r);
|
||||
|
||||
|
||||
@@ -1,8 +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.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
@@ -10,8 +10,6 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
{
|
||||
public partial class MatchChatDisplay : StandAloneChatDisplay
|
||||
{
|
||||
private readonly IBindable<int> channelId = new Bindable<int>();
|
||||
|
||||
[Resolved]
|
||||
private ChannelManager? channelManager { get; set; }
|
||||
|
||||
@@ -29,23 +27,30 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
// Required for the time being since this component is created prior to the room being joined.
|
||||
channelId.BindTo(room.ChannelId);
|
||||
channelId.BindValueChanged(_ => updateChannel(), true);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateChannel();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.ChannelId))
|
||||
updateChannel();
|
||||
}
|
||||
|
||||
private void updateChannel()
|
||||
{
|
||||
if (room.RoomID.Value == null || channelId.Value == 0)
|
||||
if (room.RoomID == null || room.ChannelId == 0)
|
||||
return;
|
||||
|
||||
Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId.Value, Type = ChannelType.Multiplayer, Name = $"#lazermp_{room.RoomID.Value}" });
|
||||
Channel.Value = channelManager?.JoinChannel(new Channel { Id = room.ChannelId, Type = ChannelType.Multiplayer, Name = $"#lazermp_{room.RoomID.Value}" });
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
|
||||
if (leaveChannelOnDispose)
|
||||
channelManager?.LeaveChannel(Channel.Value);
|
||||
}
|
||||
|
||||
@@ -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.ComponentModel;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
@@ -13,30 +12,44 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
{
|
||||
public partial class MatchLeaderboard : Leaderboard<MatchLeaderboardScope, APIUserScoreAggregate>
|
||||
{
|
||||
[Resolved(typeof(Room), nameof(Room.RoomID))]
|
||||
private Bindable<long?> roomId { get; set; } = null!;
|
||||
private readonly Room room;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
public MatchLeaderboard(Room room)
|
||||
{
|
||||
roomId.BindValueChanged(id =>
|
||||
{
|
||||
if (id.NewValue == null)
|
||||
return;
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
SetScores(null);
|
||||
RefetchScores();
|
||||
}, true);
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
fetchInitialScores();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.RoomID))
|
||||
fetchInitialScores();
|
||||
}
|
||||
|
||||
private void fetchInitialScores()
|
||||
{
|
||||
if (room.RoomID == null)
|
||||
return;
|
||||
|
||||
SetScores(null);
|
||||
RefetchScores();
|
||||
}
|
||||
|
||||
protected override bool IsOnlineScope => true;
|
||||
|
||||
protected override APIRequest? FetchScores(CancellationToken cancellationToken)
|
||||
{
|
||||
if (roomId.Value == null)
|
||||
if (room.RoomID == null)
|
||||
return null;
|
||||
|
||||
var req = new GetRoomLeaderboardRequest(roomId.Value ?? 0);
|
||||
var req = new GetRoomLeaderboardRequest(room.RoomID.Value);
|
||||
|
||||
req.Success += r => Schedule(() =>
|
||||
{
|
||||
@@ -52,6 +65,12 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
protected override LeaderboardScore CreateDrawableScore(APIUserScoreAggregate model, int index) => new MatchLeaderboardScore(model, index);
|
||||
|
||||
protected override LeaderboardScore CreateDrawableTopScore(APIUserScoreAggregate model) => new MatchLeaderboardScore(model, model.Position, false);
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MatchLeaderboardScope
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -23,7 +21,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
protected const float TRANSITION_DURATION = 350;
|
||||
protected const float FIELD_PADDING = 25;
|
||||
|
||||
protected OnlinePlayComposite Settings { get; set; }
|
||||
protected Drawable Settings { get; set; } = null!;
|
||||
|
||||
protected override bool BlockScrollInput => false;
|
||||
|
||||
@@ -50,7 +48,7 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
|
||||
|
||||
protected abstract void SelectBeatmap();
|
||||
|
||||
protected abstract OnlinePlayComposite CreateSettings(Room room);
|
||||
protected abstract Drawable CreateSettings(Room room);
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
@@ -21,26 +18,27 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
{
|
||||
public partial class DrawableMatchRoom : DrawableRoom
|
||||
{
|
||||
public readonly IBindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
|
||||
public Action OnEdit;
|
||||
public Action? OnEdit;
|
||||
|
||||
public new required Bindable<PlaylistItem?> SelectedItem
|
||||
{
|
||||
get => selectedItem;
|
||||
set => selectedItem.Current = value;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private readonly IBindable<APIUser> host = new Bindable<APIUser>();
|
||||
private readonly BindableWithCurrent<PlaylistItem?> selectedItem = new BindableWithCurrent<PlaylistItem?>();
|
||||
private readonly bool allowEdit;
|
||||
|
||||
[CanBeNull]
|
||||
private Drawable editButton;
|
||||
|
||||
private BackgroundSprite background;
|
||||
private Drawable? editButton;
|
||||
|
||||
public DrawableMatchRoom(Room room, bool allowEdit = true)
|
||||
: base(room)
|
||||
{
|
||||
this.allowEdit = allowEdit;
|
||||
|
||||
host.BindTo(room.Host);
|
||||
base.SelectedItem.BindTo(SelectedItem);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -62,17 +60,31 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (editButton != null)
|
||||
host.BindValueChanged(h => editButton.Alpha = h.NewValue?.Equals(api.LocalUser.Value) == true ? 1 : 0, true);
|
||||
|
||||
SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true);
|
||||
Room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomHost();
|
||||
}
|
||||
|
||||
protected override Drawable CreateBackground() => background = new BackgroundSprite();
|
||||
|
||||
private partial class BackgroundSprite : UpdateableBeatmapBackgroundSprite
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
protected override double LoadDelay => 0;
|
||||
if (e.PropertyName == nameof(Room.Host))
|
||||
updateRoomHost();
|
||||
}
|
||||
|
||||
private void updateRoomHost()
|
||||
{
|
||||
if (editButton != null)
|
||||
editButton.Alpha = Room.Host?.Equals(api.LocalUser.Value) == true ? 1 : 0;
|
||||
}
|
||||
|
||||
protected override UpdateableBeatmapBackgroundSprite CreateBackground() => base.CreateBackground().With(d =>
|
||||
{
|
||||
d.BackgroundLoadDelay = 0;
|
||||
});
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
Room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
@@ -29,13 +30,13 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Match
|
||||
{
|
||||
[Cached(typeof(IPreviewTrackOwner))]
|
||||
public abstract partial class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner
|
||||
{
|
||||
[Cached(typeof(IBindable<PlaylistItem>))]
|
||||
public readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
|
||||
|
||||
public override bool? ApplyModTrackAdjustments => true;
|
||||
@@ -60,8 +61,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
/// </summary>
|
||||
protected readonly Bindable<IReadOnlyList<Mod>> UserMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
protected readonly IBindable<long?> RoomId = new Bindable<long?>();
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IOverlayManager overlayManager { get; set; }
|
||||
|
||||
@@ -83,6 +82,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
[Resolved]
|
||||
private PreviewTrackManager previewTrackManager { get; set; } = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
[Cached]
|
||||
private readonly OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker();
|
||||
|
||||
@@ -110,8 +112,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
this.allowEdit = allowEdit;
|
||||
|
||||
Padding = new MarginPadding { Top = Header.HEIGHT };
|
||||
|
||||
RoomId.BindTo(room.RoomID);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -164,7 +164,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
new DrawableMatchRoom(Room, allowEdit)
|
||||
{
|
||||
OnEdit = () => settingsOverlay.Show(),
|
||||
SelectedItem = { BindTarget = SelectedItem }
|
||||
SelectedItem = SelectedItem
|
||||
}
|
||||
},
|
||||
null,
|
||||
@@ -253,22 +253,6 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
RoomId.BindValueChanged(id =>
|
||||
{
|
||||
if (id.NewValue == null)
|
||||
{
|
||||
// A new room is being created.
|
||||
// The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed.
|
||||
mainContent.Hide();
|
||||
settingsOverlay.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
mainContent.Show();
|
||||
settingsOverlay.Hide();
|
||||
}
|
||||
}, true);
|
||||
|
||||
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged));
|
||||
UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods));
|
||||
|
||||
@@ -276,24 +260,38 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
beatmapAvailabilityTracker.Availability.BindValueChanged(_ => updateWorkingBeatmap());
|
||||
|
||||
userModsSelectOverlayRegistration = overlayManager?.RegisterBlockingOverlay(UserModsSelectOverlay);
|
||||
|
||||
Room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateSetupState();
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
private void onRoomPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
return new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent))
|
||||
{
|
||||
Model = { Value = Room }
|
||||
};
|
||||
if (e.PropertyName == nameof(Room.RoomID))
|
||||
updateSetupState();
|
||||
}
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
private void updateSetupState()
|
||||
{
|
||||
if (Room.RoomID == null)
|
||||
{
|
||||
// A new room is being created.
|
||||
// The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed.
|
||||
mainContent.Hide();
|
||||
settingsOverlay.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
mainContent.Show();
|
||||
settingsOverlay.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool IsConnected => api.State.Value == APIState.Online;
|
||||
|
||||
public override bool OnBackButton()
|
||||
{
|
||||
if (Room.RoomID.Value == null)
|
||||
if (Room.RoomID == null)
|
||||
{
|
||||
if (!ensureExitConfirmed())
|
||||
return true;
|
||||
@@ -366,7 +364,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
if (!IsConnected)
|
||||
return true;
|
||||
|
||||
bool hasUnsavedChanges = Room.RoomID.Value == null && Room.Playlist.Count > 0;
|
||||
bool hasUnsavedChanges = Room.RoomID == null && Room.Playlist.Count > 0;
|
||||
|
||||
if (dialogOverlay == null || !hasUnsavedChanges)
|
||||
return true;
|
||||
@@ -539,6 +537,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
userModsSelectOverlayRegistration?.Dispose();
|
||||
Room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
public partial class MatchStartControl : CompositeDrawable
|
||||
{
|
||||
public required Bindable<PlaylistItem?> SelectedItem
|
||||
{
|
||||
get => selectedItem;
|
||||
set => selectedItem.Current = value;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!;
|
||||
|
||||
@@ -32,9 +38,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<PlaylistItem?> currentItem { get; set; } = null!;
|
||||
|
||||
private readonly BindableWithCurrent<PlaylistItem?> selectedItem = new BindableWithCurrent<PlaylistItem?>();
|
||||
private readonly MultiplayerReadyButton readyButton;
|
||||
private readonly MultiplayerCountdownButton countdownButton;
|
||||
|
||||
@@ -94,7 +98,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
currentItem.BindValueChanged(_ => updateState());
|
||||
SelectedItem.BindValueChanged(_ => updateState());
|
||||
client.RoomUpdated += onRoomUpdated;
|
||||
client.LoadRequested += onLoadRequested;
|
||||
updateState();
|
||||
@@ -210,7 +214,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
|
||||
readyButton.Enabled.Value = countdownButton.Enabled.Value =
|
||||
client.Room.State != MultiplayerRoomState.Closed
|
||||
&& currentItem.Value?.ID == client.Room.Settings.PlaylistItemId
|
||||
&& SelectedItem.Value?.ID == client.Room.Settings.PlaylistItemId
|
||||
&& !client.Room.Playlist.Single(i => i.ID == client.Room.Settings.PlaylistItemId).Expired
|
||||
&& !operationInProgress.Value;
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
@@ -13,6 +13,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
private const float ready_button_width = 600;
|
||||
private const float spectate_button_width = 200;
|
||||
|
||||
public required Bindable<PlaylistItem?> SelectedItem
|
||||
{
|
||||
get => selectedItem;
|
||||
set => selectedItem.Current = value;
|
||||
}
|
||||
|
||||
private readonly BindableWithCurrent<PlaylistItem?> selectedItem = new BindableWithCurrent<PlaylistItem?>();
|
||||
|
||||
public MultiplayerMatchFooter()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
@@ -22,17 +30,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
new Drawable?[]
|
||||
{
|
||||
null,
|
||||
new MultiplayerSpectateButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
SelectedItem = selectedItem
|
||||
},
|
||||
null,
|
||||
new MatchStartControl
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
SelectedItem = selectedItem
|
||||
},
|
||||
null
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
@@ -28,14 +29,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
public partial class MultiplayerMatchSettingsOverlay : RoomSettingsOverlay
|
||||
{
|
||||
private MatchSettings settings = null!;
|
||||
public required Bindable<PlaylistItem?> SelectedItem
|
||||
{
|
||||
get => selectedItem;
|
||||
set => selectedItem.Current = value;
|
||||
}
|
||||
|
||||
protected override OsuButton SubmitButton => settings.ApplyButton;
|
||||
protected override bool IsLoading => ongoingOperationTracker.InProgress.Value;
|
||||
|
||||
[Resolved]
|
||||
private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!;
|
||||
|
||||
protected override bool IsLoading => ongoingOperationTracker.InProgress.Value;
|
||||
private readonly BindableWithCurrent<PlaylistItem?> selectedItem = new BindableWithCurrent<PlaylistItem?>();
|
||||
private MatchSettings settings = null!;
|
||||
|
||||
public MultiplayerMatchSettingsOverlay(Room room)
|
||||
: base(room)
|
||||
@@ -44,19 +51,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
|
||||
protected override void SelectBeatmap() => settings.SelectBeatmap();
|
||||
|
||||
protected override OnlinePlayComposite CreateSettings(Room room) => settings = new MatchSettings(room)
|
||||
protected override Drawable CreateSettings(Room room) => settings = new MatchSettings(room)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Y,
|
||||
SettingsApplied = Hide
|
||||
SettingsApplied = Hide,
|
||||
SelectedItem = { BindTarget = SelectedItem }
|
||||
};
|
||||
|
||||
protected partial class MatchSettings : OnlinePlayComposite
|
||||
protected partial class MatchSettings : CompositeDrawable
|
||||
{
|
||||
private const float disabled_alpha = 0.2f;
|
||||
|
||||
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
|
||||
|
||||
public readonly Bindable<PlaylistItem?> SelectedItem = new Bindable<PlaylistItem?>();
|
||||
public Action? SettingsApplied;
|
||||
|
||||
public OsuTextBox NameField = null!;
|
||||
@@ -66,7 +75,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
public OsuTextBox PasswordTextBox = null!;
|
||||
public OsuCheckbox AutoSkipCheckbox = null!;
|
||||
public RoundedButton ApplyButton = null!;
|
||||
|
||||
public OsuSpriteText ErrorText = null!;
|
||||
|
||||
private OsuEnumDropdown<StartMode> startModeDropdown = null!;
|
||||
@@ -270,7 +278,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
drawablePlaylist = new DrawableRoomPlaylist
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = DrawableRoomPlaylistItem.HEIGHT
|
||||
Height = DrawableRoomPlaylistItem.HEIGHT,
|
||||
SelectedItem = { BindTarget = SelectedItem }
|
||||
},
|
||||
selectBeatmapButton = new RoundedButton
|
||||
{
|
||||
@@ -316,7 +325,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
Padding = new MarginPadding { Horizontal = OsuScreen.HORIZONTAL_OVERFLOW_PADDING },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
ApplyButton = new CreateOrUpdateButton
|
||||
ApplyButton = new CreateOrUpdateButton(room)
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
@@ -343,14 +352,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
};
|
||||
|
||||
TypePicker.Current.BindValueChanged(type => typeLabel.Text = type.NewValue.GetLocalisableDescription(), true);
|
||||
RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true);
|
||||
Type.BindValueChanged(type => TypePicker.Current.Value = type.NewValue, true);
|
||||
MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true);
|
||||
RoomID.BindValueChanged(roomId => playlistContainer.Alpha = roomId.NewValue == null ? 1 : 0, true);
|
||||
Password.BindValueChanged(password => PasswordTextBox.Text = password.NewValue ?? string.Empty, true);
|
||||
QueueMode.BindValueChanged(mode => QueueModeDropdown.Current.Value = mode.NewValue, true);
|
||||
AutoStartDuration.BindValueChanged(duration => startModeDropdown.Current.Value = (StartMode)(int)duration.NewValue.TotalSeconds, true);
|
||||
AutoSkip.BindValueChanged(autoSkip => AutoSkipCheckbox.Current.Value = autoSkip.NewValue, true);
|
||||
|
||||
operationInProgress.BindTo(ongoingOperationTracker.InProgress);
|
||||
operationInProgress.BindValueChanged(v =>
|
||||
@@ -366,15 +367,88 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
drawablePlaylist.Items.BindTo(Playlist);
|
||||
drawablePlaylist.SelectedItem.BindTo(CurrentPlaylistItem);
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
|
||||
updateRoomName();
|
||||
updateRoomType();
|
||||
updateRoomQueueMode();
|
||||
updateRoomPassword();
|
||||
updateRoomAutoSkip();
|
||||
updateRoomMaxParticipants();
|
||||
updateRoomAutoStartDuration();
|
||||
updateRoomPlaylist();
|
||||
|
||||
drawablePlaylist.Items.BindCollectionChanged((_, __) => room.Playlist = drawablePlaylist.Items.ToArray());
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.Name):
|
||||
updateRoomName();
|
||||
break;
|
||||
|
||||
case nameof(Room.Type):
|
||||
updateRoomName();
|
||||
break;
|
||||
|
||||
case nameof(Room.QueueMode):
|
||||
updateRoomQueueMode();
|
||||
break;
|
||||
|
||||
case nameof(Room.Password):
|
||||
updateRoomPassword();
|
||||
break;
|
||||
|
||||
case nameof(Room.AutoSkip):
|
||||
updateRoomAutoSkip();
|
||||
break;
|
||||
|
||||
case nameof(Room.MaxParticipants):
|
||||
updateRoomMaxParticipants();
|
||||
break;
|
||||
|
||||
case nameof(Room.AutoStartDuration):
|
||||
updateRoomAutoStartDuration();
|
||||
break;
|
||||
|
||||
case nameof(Room.Playlist):
|
||||
updateRoomPlaylist();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomName()
|
||||
=> NameField.Text = room.Name;
|
||||
|
||||
private void updateRoomType()
|
||||
=> TypePicker.Current.Value = room.Type;
|
||||
|
||||
private void updateRoomQueueMode()
|
||||
=> QueueModeDropdown.Current.Value = room.QueueMode;
|
||||
|
||||
private void updateRoomPassword()
|
||||
=> PasswordTextBox.Text = room.Password ?? string.Empty;
|
||||
|
||||
private void updateRoomAutoSkip()
|
||||
=> AutoSkipCheckbox.Current.Value = room.AutoSkip;
|
||||
|
||||
private void updateRoomMaxParticipants()
|
||||
=> MaxParticipantsField.Text = room.MaxParticipants?.ToString();
|
||||
|
||||
private void updateRoomAutoStartDuration()
|
||||
=> typeLabel.Text = room.AutoStartDuration.GetLocalisableDescription();
|
||||
|
||||
private void updateRoomPlaylist()
|
||||
=> drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, room.Playlist);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
ApplyButton.Enabled.Value = Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value;
|
||||
ApplyButton.Enabled.Value = room.Playlist.Count > 0 && NameField.Text.Length > 0 && !operationInProgress.Value;
|
||||
playlistContainer.Alpha = room.RoomID == null ? 1 : 0;
|
||||
}
|
||||
|
||||
private void apply()
|
||||
@@ -387,8 +461,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
Debug.Assert(applyingSettingsOperation == null);
|
||||
applyingSettingsOperation = ongoingOperationTracker.BeginOperation();
|
||||
|
||||
TimeSpan autoStartDuration = TimeSpan.FromSeconds((int)startModeDropdown.Current.Value);
|
||||
|
||||
// If the client is already in a room, update via the client.
|
||||
// Otherwise, update the room directly in preparation for it to be submitted to the API on match creation.
|
||||
if (client.Room != null)
|
||||
@@ -398,7 +470,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
password: PasswordTextBox.Text,
|
||||
matchType: TypePicker.Current.Value,
|
||||
queueMode: QueueModeDropdown.Current.Value,
|
||||
autoStartDuration: autoStartDuration,
|
||||
autoStartDuration: TimeSpan.FromSeconds((int)startModeDropdown.Current.Value),
|
||||
autoSkip: AutoSkipCheckbox.Current.Value)
|
||||
.ContinueWith(t => Schedule(() =>
|
||||
{
|
||||
@@ -410,17 +482,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
}
|
||||
else
|
||||
{
|
||||
room.Name.Value = NameField.Text;
|
||||
room.Type.Value = TypePicker.Current.Value;
|
||||
room.Password.Value = PasswordTextBox.Current.Value;
|
||||
room.QueueMode.Value = QueueModeDropdown.Current.Value;
|
||||
room.AutoStartDuration.Value = autoStartDuration;
|
||||
room.AutoSkip.Value = AutoSkipCheckbox.Current.Value;
|
||||
room.Name = NameField.Text;
|
||||
room.Type = TypePicker.Current.Value;
|
||||
room.Password = PasswordTextBox.Current.Value;
|
||||
room.QueueMode = QueueModeDropdown.Current.Value;
|
||||
room.AutoStartDuration = TimeSpan.FromSeconds((int)startModeDropdown.Current.Value);
|
||||
room.AutoSkip = AutoSkipCheckbox.Current.Value;
|
||||
|
||||
if (int.TryParse(MaxParticipantsField.Text, out int max))
|
||||
room.MaxParticipants.Value = max;
|
||||
room.MaxParticipants = max;
|
||||
else
|
||||
room.MaxParticipants.Value = null;
|
||||
room.MaxParticipants = null;
|
||||
|
||||
manager.CreateRoom(room, onSuccess, onError);
|
||||
}
|
||||
@@ -448,7 +520,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
if (text.StartsWith(not_found_prefix, StringComparison.Ordinal))
|
||||
{
|
||||
ErrorText.Text = "The selected beatmap is not available online.";
|
||||
CurrentPlaylistItem.Value.MarkInvalid();
|
||||
SelectedItem.Value?.MarkInvalid();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -460,17 +532,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
applyingSettingsOperation.Dispose();
|
||||
applyingSettingsOperation = null;
|
||||
});
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CreateOrUpdateButton : RoundedButton
|
||||
{
|
||||
[Resolved(typeof(Room), nameof(Room.RoomID))]
|
||||
private Bindable<long?> roomId { get; set; } = null!;
|
||||
private readonly Room room;
|
||||
|
||||
protected override void LoadComplete()
|
||||
public CreateOrUpdateButton(Room room)
|
||||
{
|
||||
base.LoadComplete();
|
||||
roomId.BindValueChanged(id => Text = id.NewValue == null ? "Create" : "Update", true);
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -478,6 +554,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
BackgroundColour = colours.YellowDark;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Text = room.RoomID == null ? "Create" : "Update";
|
||||
}
|
||||
}
|
||||
|
||||
private enum StartMode
|
||||
|
||||
@@ -21,6 +21,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
public partial class MultiplayerSpectateButton : CompositeDrawable
|
||||
{
|
||||
public required Bindable<PlaylistItem?> SelectedItem
|
||||
{
|
||||
get => selectedItem;
|
||||
set => selectedItem.Current = value;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private OngoingOperationTracker ongoingOperationTracker { get; set; } = null!;
|
||||
|
||||
@@ -30,13 +36,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<PlaylistItem?> currentItem { get; set; } = null!;
|
||||
private readonly BindableWithCurrent<PlaylistItem?> selectedItem = new BindableWithCurrent<PlaylistItem?>();
|
||||
private readonly RoundedButton button;
|
||||
|
||||
private IBindable<bool> operationInProgress = null!;
|
||||
|
||||
private readonly RoundedButton button;
|
||||
|
||||
public MultiplayerSpectateButton()
|
||||
{
|
||||
InternalChild = button = new RoundedButton
|
||||
@@ -71,7 +75,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
currentItem.BindValueChanged(_ => Scheduler.AddOnce(checkForAutomaticDownload), true);
|
||||
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(checkForAutomaticDownload), true);
|
||||
client.RoomUpdated += onRoomUpdated;
|
||||
updateState();
|
||||
}
|
||||
@@ -117,7 +121,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
|
||||
private void checkForAutomaticDownload()
|
||||
{
|
||||
PlaylistItem? item = currentItem.Value;
|
||||
PlaylistItem? item = SelectedItem.Value;
|
||||
|
||||
downloadCheckCancellation?.Cancel();
|
||||
|
||||
|
||||
@@ -19,6 +19,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
{
|
||||
public readonly Bindable<MultiplayerPlaylistDisplayMode> DisplayMode = new Bindable<MultiplayerPlaylistDisplayMode>();
|
||||
|
||||
public required Bindable<PlaylistItem?> SelectedItem
|
||||
{
|
||||
get => selectedItem;
|
||||
set => selectedItem.Current = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when an item requests to be edited.
|
||||
/// </summary>
|
||||
@@ -27,14 +33,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<PlaylistItem?> currentItem { get; set; } = null!;
|
||||
|
||||
private readonly Room room;
|
||||
private readonly BindableWithCurrent<PlaylistItem?> selectedItem = new BindableWithCurrent<PlaylistItem?>();
|
||||
private MultiplayerPlaylistTabControl playlistTabControl = null!;
|
||||
private MultiplayerQueueList queueList = null!;
|
||||
private MultiplayerHistoryList historyList = null!;
|
||||
private bool firstPopulation = true;
|
||||
|
||||
public MultiplayerPlaylist(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@@ -55,17 +65,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
queueList = new MultiplayerQueueList
|
||||
queueList = new MultiplayerQueueList(room)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
SelectedItem = { BindTarget = currentItem },
|
||||
SelectedItem = { BindTarget = selectedItem },
|
||||
RequestEdit = item => RequestEdit?.Invoke(item)
|
||||
},
|
||||
historyList = new MultiplayerHistoryList
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
SelectedItem = { BindTarget = currentItem }
|
||||
SelectedItem = { BindTarget = selectedItem }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
@@ -21,28 +20,49 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
/// </summary>
|
||||
public partial class MultiplayerQueueList : DrawableRoomPlaylist
|
||||
{
|
||||
public MultiplayerQueueList()
|
||||
private readonly Room room;
|
||||
|
||||
private QueueFillFlowContainer flow = null!;
|
||||
|
||||
public MultiplayerQueueList(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
ShowItemOwners = true;
|
||||
}
|
||||
|
||||
protected override FillFlowContainer<RearrangeableListItem<PlaylistItem>> CreateListFillFlowContainer() => new QueueFillFlowContainer
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomPlaylist();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.Playlist))
|
||||
updateRoomPlaylist();
|
||||
}
|
||||
|
||||
private void updateRoomPlaylist()
|
||||
=> flow.InvalidateLayout();
|
||||
|
||||
protected override FillFlowContainer<RearrangeableListItem<PlaylistItem>> CreateListFillFlowContainer() => flow = new QueueFillFlowContainer
|
||||
{
|
||||
Spacing = new Vector2(0, 2)
|
||||
};
|
||||
|
||||
protected override DrawableRoomPlaylistItem CreateDrawablePlaylistItem(PlaylistItem item) => new QueuePlaylistItem(item);
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
|
||||
private partial class QueueFillFlowContainer : FillFlowContainer<RearrangeableListItem<PlaylistItem>>
|
||||
{
|
||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||
private BindableList<PlaylistItem> roomPlaylist { get; set; }
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
roomPlaylist.BindCollectionChanged((_, _) => InvalidateLayout());
|
||||
}
|
||||
public new void InvalidateLayout() => base.InvalidateLayout();
|
||||
|
||||
public override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.OfType<RearrangeableListItem<PlaylistItem>>().OrderBy(item => item.Model.PlaylistOrder);
|
||||
}
|
||||
@@ -50,10 +70,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
private partial class QueuePlaylistItem : DrawableRoomPlaylistItem
|
||||
{
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private MultiplayerClient multiplayerClient { get; set; }
|
||||
private MultiplayerClient multiplayerClient { get; set; } = null!;
|
||||
|
||||
public QueuePlaylistItem(PlaylistItem item)
|
||||
: base(item)
|
||||
@@ -91,7 +111,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (multiplayerClient != null)
|
||||
if (multiplayerClient.IsNotNull())
|
||||
multiplayerClient.RoomUpdated -= onRoomUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
protected override Room CreateNewRoom() => new Room
|
||||
{
|
||||
Name = { Value = $"{api.LocalUser}'s awesome room" },
|
||||
Type = { Value = MatchType.HeadToHead },
|
||||
Name = $"{api.LocalUser}'s awesome room",
|
||||
Type = MatchType.HeadToHead,
|
||||
};
|
||||
|
||||
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new MultiplayerMatchSubScreen(room);
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
[Resolved]
|
||||
private OngoingOperationTracker operationTracker { get; set; } = null!;
|
||||
|
||||
private readonly Room room;
|
||||
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
|
||||
private readonly PlaylistItem? itemToEdit;
|
||||
|
||||
@@ -38,6 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
public MultiplayerMatchSongSelect(Room room, PlaylistItem? itemToEdit = null)
|
||||
: base(room, itemToEdit)
|
||||
{
|
||||
this.room = room;
|
||||
this.itemToEdit = itemToEdit;
|
||||
}
|
||||
|
||||
@@ -111,8 +113,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
}
|
||||
else
|
||||
{
|
||||
Playlist.Clear();
|
||||
Playlist.Add(item);
|
||||
room.Playlist = [item];
|
||||
this.Exit();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
@@ -45,17 +44,17 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
public override string ShortTitle => "room";
|
||||
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; }
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuGame game { get; set; }
|
||||
private OsuGame? game { get; set; }
|
||||
|
||||
private AddItemButton addItemButton;
|
||||
private AddItemButton addItemButton = null!;
|
||||
|
||||
public MultiplayerMatchSubScreen(Room room)
|
||||
: base(room)
|
||||
{
|
||||
Title = room.RoomID.Value == null ? "New room" : room.Name.Value;
|
||||
Title = room.RoomID == null ? "New room" : room.Name;
|
||||
Activity.Value = new UserActivity.InLobby(room);
|
||||
}
|
||||
|
||||
@@ -95,7 +94,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
new Drawable?[]
|
||||
{
|
||||
// Participants column
|
||||
new GridContainer
|
||||
@@ -139,10 +138,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
null,
|
||||
new Drawable[]
|
||||
{
|
||||
new MultiplayerPlaylist
|
||||
new MultiplayerPlaylist(Room)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RequestEdit = OpenSongSelection
|
||||
RequestEdit = OpenSongSelection,
|
||||
SelectedItem = SelectedItem
|
||||
}
|
||||
},
|
||||
new[]
|
||||
@@ -220,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
/// Opens the song selection screen to add or edit an item.
|
||||
/// </summary>
|
||||
/// <param name="itemToEdit">An optional playlist item to edit. If null, a new item will be added instead.</param>
|
||||
internal void OpenSongSelection(PlaylistItem itemToEdit = null)
|
||||
internal void OpenSongSelection(PlaylistItem? itemToEdit = null)
|
||||
{
|
||||
if (!this.IsCurrentScreen())
|
||||
return;
|
||||
@@ -228,9 +228,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
this.Push(new MultiplayerMatchSongSelect(Room, itemToEdit));
|
||||
}
|
||||
|
||||
protected override Drawable CreateFooter() => new MultiplayerMatchFooter();
|
||||
protected override Drawable CreateFooter() => new MultiplayerMatchFooter
|
||||
{
|
||||
SelectedItem = SelectedItem
|
||||
};
|
||||
|
||||
protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room);
|
||||
protected override RoomSettingsOverlay CreateRoomSettingsOverlay(Room room) => new MultiplayerMatchSettingsOverlay(room)
|
||||
{
|
||||
SelectedItem = SelectedItem
|
||||
};
|
||||
|
||||
protected override void UpdateMods()
|
||||
{
|
||||
@@ -245,7 +251,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
}
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
|
||||
private bool exitConfirmed;
|
||||
|
||||
@@ -275,8 +281,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
return base.OnExiting(e);
|
||||
}
|
||||
|
||||
private ModSettingChangeTracker modSettingChangeTracker;
|
||||
private ScheduledDelegate debouncedModSettingsUpdate;
|
||||
private ModSettingChangeTracker? modSettingChangeTracker;
|
||||
private ScheduledDelegate? debouncedModSettingsUpdate;
|
||||
|
||||
private void onUserModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||
{
|
||||
@@ -352,7 +358,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
Activity.Value = new UserActivity.InLobby(Room);
|
||||
}
|
||||
|
||||
private bool localUserCanAddItem => client.IsHost || Room.QueueMode.Value != QueueMode.HostOnly;
|
||||
private bool localUserCanAddItem => client.IsHost || Room.QueueMode != QueueMode.HostOnly;
|
||||
|
||||
private void updateCurrentItem()
|
||||
{
|
||||
@@ -422,7 +428,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
return;
|
||||
|
||||
// If there's only one playlist item and we are the host, assume we want to change it. Else add a new one.
|
||||
PlaylistItem itemToEdit = client.IsHost && Room.Playlist.Count == 1 ? Room.Playlist.Single() : null;
|
||||
PlaylistItem? itemToEdit = client.IsHost && Room.Playlist.Count == 1 ? Room.Playlist.Single() : null;
|
||||
|
||||
OpenSongSelection(itemToEdit);
|
||||
|
||||
@@ -434,7 +440,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (client != null)
|
||||
if (client.IsNotNull())
|
||||
{
|
||||
client.RoomUpdated -= onRoomUpdated;
|
||||
client.LoadRequested -= onLoadRequested;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@@ -29,17 +28,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
|
||||
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; }
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
private IBindable<bool> isConnected;
|
||||
private IBindable<bool> isConnected = null!;
|
||||
|
||||
private readonly TaskCompletionSource<bool> resultsReady = new TaskCompletionSource<bool>();
|
||||
|
||||
private readonly MultiplayerRoomUser[] users;
|
||||
|
||||
private LoadingLayer loadingDisplay;
|
||||
|
||||
private MultiplayerGameplayLeaderboard multiplayerLeaderboard;
|
||||
private LoadingLayer loadingDisplay = null!;
|
||||
private MultiplayerGameplayLeaderboard multiplayerLeaderboard = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a multiplayer player.
|
||||
@@ -53,8 +50,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
AllowPause = false,
|
||||
AllowRestart = false,
|
||||
AllowFailAnimation = false,
|
||||
AllowSkipping = room.AutoSkip.Value,
|
||||
AutomaticallySkipIntro = room.AutoSkip.Value,
|
||||
AllowSkipping = room.AutoSkip,
|
||||
AutomaticallySkipIntro = room.AutoSkip,
|
||||
AlwaysShowLeaderboard = true,
|
||||
})
|
||||
{
|
||||
@@ -153,7 +150,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
GameplayClockContainer.Reset();
|
||||
}
|
||||
|
||||
private void failAndBail(string message = null)
|
||||
private void failAndBail(string? message = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important);
|
||||
@@ -196,14 +193,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
protected override ResultsScreen CreateResults(ScoreInfo score)
|
||||
{
|
||||
Debug.Assert(Room.RoomID.Value != null);
|
||||
Debug.Assert(Room.RoomID != null);
|
||||
|
||||
return multiplayerLeaderboard.TeamScores.Count == 2
|
||||
? new MultiplayerTeamResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem, multiplayerLeaderboard.TeamScores)
|
||||
? new MultiplayerTeamResultsScreen(score, Room.RoomID.Value, PlaylistItem, multiplayerLeaderboard.TeamScores)
|
||||
{
|
||||
ShowUserStatistics = true,
|
||||
}
|
||||
: new MultiplayerResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem)
|
||||
: new MultiplayerResultsScreen(score, Room.RoomID.Value, PlaylistItem)
|
||||
{
|
||||
ShowUserStatistics = true
|
||||
};
|
||||
@@ -213,7 +210,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (client != null)
|
||||
if (client.IsNotNull())
|
||||
{
|
||||
client.GameplayStarted -= onGameplayStarted;
|
||||
client.ResultsReady -= onResultsReady;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
@@ -18,12 +16,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
public partial class MultiplayerRoomManager : RoomManager
|
||||
{
|
||||
[Resolved]
|
||||
private MultiplayerClient multiplayerClient { get; set; }
|
||||
private MultiplayerClient multiplayerClient { get; set; } = null!;
|
||||
|
||||
public override void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
=> base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password.Value, onSuccess, onError), onError);
|
||||
public override void CreateRoom(Room room, Action<Room>? onSuccess = null, Action<string>? onError = null)
|
||||
=> base.CreateRoom(room, r => joinMultiplayerRoom(r, r.Password, onSuccess, onError), onError);
|
||||
|
||||
public override void JoinRoom(Room room, string password = null, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
public override void JoinRoom(Room room, string? password = null, Action<Room>? onSuccess = null, Action<string>? onError = null)
|
||||
{
|
||||
if (!multiplayerClient.IsConnected.Value)
|
||||
{
|
||||
@@ -33,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
// this is done here as a pre-check to avoid clicking on already closed rooms in the lounge from triggering a server join.
|
||||
// should probably be done at a higher level, but due to the current structure of things this is the easiest place for now.
|
||||
if (room.Status.Value is RoomStatusEnded)
|
||||
if (room.Status is RoomStatusEnded)
|
||||
{
|
||||
onError?.Invoke("Cannot join an ended room.");
|
||||
return;
|
||||
@@ -51,9 +49,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
multiplayerClient.LeaveRoom();
|
||||
}
|
||||
|
||||
private void joinMultiplayerRoom(Room room, string password, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
private void joinMultiplayerRoom(Room room, string? password, Action<Room>? onSuccess = null, Action<string>? onError = null)
|
||||
{
|
||||
Debug.Assert(room.RoomID.Value != null);
|
||||
Debug.Assert(room.RoomID != null);
|
||||
|
||||
multiplayerClient.JoinRoom(room, password).ContinueWith(t =>
|
||||
{
|
||||
|
||||
@@ -1,115 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Match;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="CompositeDrawable"/> that exposes bindables for <see cref="Room"/> properties.
|
||||
/// </summary>
|
||||
public partial class OnlinePlayComposite : CompositeDrawable
|
||||
{
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<long?> RoomID { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.Name))]
|
||||
protected Bindable<string> RoomName { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<APIUser> Host { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<RoomStatus> Status { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<MatchType> Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected item in the <see cref="RoomSubScreen"/>, or the current item from <see cref="Playlist"/>
|
||||
/// if this <see cref="OnlinePlayComposite"/> is not within a <see cref="RoomSubScreen"/>.
|
||||
/// </summary>
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<PlaylistItem> CurrentPlaylistItem { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<Room.RoomPlaylistItemStats> PlaylistItemStats { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<Room.RoomDifficultyRange> DifficultyRange { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<RoomCategory> Category { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected BindableList<APIUser> RecentParticipants { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<int> ParticipantCount { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<int?> MaxParticipants { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<int?> MaxAttempts { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
public Bindable<PlaylistAggregateScore> UserScore { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<DateTimeOffset?> StartDate { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<DateTimeOffset?> EndDate { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<RoomAvailability> Availability { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
public Bindable<string> Password { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<TimeSpan?> Duration { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<QueueMode> QueueMode { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<TimeSpan> AutoStartDuration { get; private set; }
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<bool> AutoSkip { get; private set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IBindable<PlaylistItem> subScreenSelectedItem { get; set; }
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
subScreenSelectedItem?.BindValueChanged(_ => UpdateSelectedItem());
|
||||
Playlist.BindCollectionChanged((_, _) => UpdateSelectedItem(), true);
|
||||
}
|
||||
|
||||
protected void UpdateSelectedItem()
|
||||
{
|
||||
// null room ID means this is a room in the process of being created.
|
||||
if (RoomID.Value == null)
|
||||
CurrentPlaylistItem.Value = Playlist.GetCurrentItem();
|
||||
else if (subScreenSelectedItem != null)
|
||||
CurrentPlaylistItem.Value = subScreenSelectedItem.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,6 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
|
||||
public override bool AllowEditing => false;
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; } = null!;
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -22,9 +20,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
public partial class PlaylistsLoungeSubScreen : LoungeSubScreen
|
||||
{
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private Dropdown<PlaylistsCategory> categoryDropdown;
|
||||
private Dropdown<PlaylistsCategory> categoryDropdown = null!;
|
||||
|
||||
protected override IEnumerable<Drawable> CreateFilterControls()
|
||||
{
|
||||
@@ -67,8 +65,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
return new Room
|
||||
{
|
||||
Name = { Value = $"{api.LocalUser}'s awesome playlist" },
|
||||
Type = { Value = MatchType.Playlists }
|
||||
Name = $"{api.LocalUser}'s awesome playlist",
|
||||
Type = MatchType.Playlists
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -21,11 +19,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
public partial class PlaylistsPlayer : RoomSubmittingPlayer
|
||||
{
|
||||
public Action Exited;
|
||||
public Action? Exited;
|
||||
|
||||
protected override UserActivity InitialActivity => new UserActivity.InPlaylistGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
|
||||
|
||||
public PlaylistsPlayer(Room room, PlaylistItem playlistItem, PlayerConfiguration configuration = null)
|
||||
public PlaylistsPlayer(Room room, PlaylistItem playlistItem, PlayerConfiguration? configuration = null)
|
||||
: base(room, playlistItem, configuration)
|
||||
{
|
||||
}
|
||||
@@ -57,8 +55,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
|
||||
protected override ResultsScreen CreateResults(ScoreInfo score)
|
||||
{
|
||||
Debug.Assert(Room.RoomID.Value != null);
|
||||
return new PlaylistItemUserResultsScreen(score, Room.RoomID.Value.Value, PlaylistItem)
|
||||
Debug.Assert(Room.RoomID != null);
|
||||
return new PlaylistItemUserResultsScreen(score, Room.RoomID.Value, PlaylistItem)
|
||||
{
|
||||
AllowRetry = true,
|
||||
ShowUserStatistics = true,
|
||||
|
||||
@@ -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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@@ -17,20 +16,14 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
public partial class PlaylistsReadyButton : ReadyButton
|
||||
{
|
||||
[Resolved(typeof(Room), nameof(Room.EndDate))]
|
||||
private Bindable<DateTimeOffset?> endDate { get; set; }
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.MaxAttempts))]
|
||||
private Bindable<int?> maxAttempts { get; set; }
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.UserScore))]
|
||||
private Bindable<PlaylistAggregateScore> userScore { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<WorkingBeatmap> gameBeatmap { get; set; }
|
||||
private IBindable<WorkingBeatmap> gameBeatmap { get; set; } = null!;
|
||||
|
||||
public PlaylistsReadyButton()
|
||||
private readonly Room room;
|
||||
|
||||
public PlaylistsReadyButton(Room room)
|
||||
{
|
||||
this.room = room;
|
||||
Text = "Start";
|
||||
}
|
||||
|
||||
@@ -46,15 +39,24 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
userScore.BindValueChanged(aggregate =>
|
||||
{
|
||||
if (maxAttempts.Value == null)
|
||||
return;
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateRoomUserScore();
|
||||
}
|
||||
|
||||
int remaining = maxAttempts.Value.Value - aggregate.NewValue.PlaylistItemAttempts.Sum(a => a.Attempts);
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Room.UserScore))
|
||||
updateRoomUserScore();
|
||||
}
|
||||
|
||||
hasRemainingAttempts = remaining > 0;
|
||||
});
|
||||
private void updateRoomUserScore()
|
||||
{
|
||||
if (room.MaxAttempts == null || room.UserScore == null)
|
||||
return;
|
||||
|
||||
int remaining = room.MaxAttempts.Value - room.UserScore.PlaylistItemAttempts.Sum(a => a.Attempts);
|
||||
|
||||
hasRemainingAttempts = remaining > 0;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@@ -80,6 +82,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
|
||||
private bool enoughTimeLeft =>
|
||||
// This should probably consider the length of the currently selected item, rather than a constant 30 seconds.
|
||||
endDate.Value != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate.Value;
|
||||
room.EndDate != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < room.EndDate;
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
public partial class PlaylistsRoomFooter : CompositeDrawable
|
||||
{
|
||||
public Action OnStart;
|
||||
public Action? OnStart;
|
||||
|
||||
public PlaylistsRoomFooter()
|
||||
public PlaylistsRoomFooter(Room room)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new PlaylistsReadyButton
|
||||
new PlaylistsReadyButton(room)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using Humanizer.Localisation;
|
||||
@@ -25,6 +26,7 @@ using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osuTK;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Rulesets;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
@@ -45,14 +47,14 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
|
||||
protected override void SelectBeatmap() => settings.SelectBeatmap();
|
||||
|
||||
protected override OnlinePlayComposite CreateSettings(Room room) => settings = new MatchSettings(room)
|
||||
protected override Drawable CreateSettings(Room room) => settings = new MatchSettings(room)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Y,
|
||||
EditPlaylist = () => EditPlaylist?.Invoke()
|
||||
};
|
||||
|
||||
protected partial class MatchSettings : OnlinePlayComposite
|
||||
protected partial class MatchSettings : CompositeDrawable
|
||||
{
|
||||
private const float disabled_alpha = 0.2f;
|
||||
|
||||
@@ -142,7 +144,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
TabbableContentContainer = this,
|
||||
LengthLimit = 100
|
||||
LengthLimit = 100,
|
||||
Text = room.Name
|
||||
},
|
||||
},
|
||||
new Section("Duration")
|
||||
@@ -313,12 +316,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
loadingLayer = new LoadingLayer(true)
|
||||
};
|
||||
|
||||
RoomName.BindValueChanged(name => NameField.Text = name.NewValue, true);
|
||||
Availability.BindValueChanged(availability => AvailabilityPicker.Current.Value = availability.NewValue, true);
|
||||
MaxParticipants.BindValueChanged(count => MaxParticipantsField.Text = count.NewValue?.ToString(), true);
|
||||
MaxAttempts.BindValueChanged(count => MaxAttemptsField.Text = count.NewValue?.ToString(), true);
|
||||
Duration.BindValueChanged(duration => DurationField.Current.Value = duration.NewValue ?? TimeSpan.FromMinutes(30), true);
|
||||
|
||||
DurationField.Current.BindValueChanged(duration =>
|
||||
{
|
||||
if (hasValidDuration)
|
||||
@@ -332,11 +329,72 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
|
||||
localUser = api.LocalUser.GetBoundCopy();
|
||||
localUser.BindValueChanged(populateDurations, true);
|
||||
|
||||
playlist.Items.BindTo(Playlist);
|
||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
room.PropertyChanged += onRoomPropertyChanged;
|
||||
|
||||
updateRoomName();
|
||||
updateRoomAvailability();
|
||||
updateRoomMaxParticipants();
|
||||
updateRoomDuration();
|
||||
updateRoomMaxAttempts();
|
||||
updateRoomPlaylist();
|
||||
|
||||
playlist.Items.BindCollectionChanged((_, __) => room.Playlist = playlist.Items.ToArray());
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.Name):
|
||||
updateRoomName();
|
||||
break;
|
||||
|
||||
case nameof(Room.Availability):
|
||||
updateRoomAvailability();
|
||||
break;
|
||||
|
||||
case nameof(Room.MaxParticipants):
|
||||
updateRoomMaxParticipants();
|
||||
break;
|
||||
|
||||
case nameof(Room.Duration):
|
||||
updateRoomDuration();
|
||||
break;
|
||||
|
||||
case nameof(Room.MaxAttempts):
|
||||
updateRoomMaxAttempts();
|
||||
break;
|
||||
|
||||
case nameof(Room.Playlist):
|
||||
updateRoomPlaylist();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomName()
|
||||
=> NameField.Text = room.Name;
|
||||
|
||||
private void updateRoomAvailability()
|
||||
=> AvailabilityPicker.Current.Value = room.Availability;
|
||||
|
||||
private void updateRoomMaxParticipants()
|
||||
=> MaxParticipantsField.Text = room.MaxParticipants?.ToString();
|
||||
|
||||
private void updateRoomDuration()
|
||||
=> DurationField.Current.Value = room.Duration ?? TimeSpan.FromMinutes(30);
|
||||
|
||||
private void updateRoomMaxAttempts()
|
||||
=> MaxAttemptsField.Text = room.MaxAttempts?.ToString();
|
||||
|
||||
private void updateRoomPlaylist()
|
||||
=> playlist.Items.ReplaceRange(0, playlist.Items.Count, room.Playlist);
|
||||
|
||||
private void populateDurations(ValueChangedEvent<APIUser> user)
|
||||
{
|
||||
// roughly correct (see https://github.com/Humanizr/Humanizer/blob/18167e56c082449cc4fe805b8429e3127a7b7f93/readme.md?plain=1#L427)
|
||||
@@ -370,9 +428,9 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
public void SelectBeatmap() => editPlaylistButton.TriggerClick();
|
||||
|
||||
private void onPlaylistChanged(object? sender, NotifyCollectionChangedEventArgs e) =>
|
||||
playlistLength.Text = $"Length: {Playlist.GetTotalDuration(rulesets)}";
|
||||
playlistLength.Text = $"Length: {room.Playlist.GetTotalDuration(rulesets)}";
|
||||
|
||||
private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0
|
||||
private bool hasValidSettings => room.RoomID == null && NameField.Text.Length > 0 && room.Playlist.Count > 0
|
||||
&& hasValidDuration;
|
||||
|
||||
private bool hasValidDuration => DurationField.Current.Value <= TimeSpan.FromDays(14) || localUser.Value.IsSupporter;
|
||||
@@ -384,20 +442,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
|
||||
hideError();
|
||||
|
||||
RoomName.Value = NameField.Text;
|
||||
Availability.Value = AvailabilityPicker.Current.Value;
|
||||
|
||||
if (int.TryParse(MaxParticipantsField.Text, out int max))
|
||||
MaxParticipants.Value = max;
|
||||
else
|
||||
MaxParticipants.Value = null;
|
||||
|
||||
if (int.TryParse(MaxAttemptsField.Text, out max))
|
||||
MaxAttempts.Value = max;
|
||||
else
|
||||
MaxAttempts.Value = null;
|
||||
|
||||
Duration.Value = DurationField.Current.Value;
|
||||
room.Name = NameField.Text;
|
||||
room.Availability = AvailabilityPicker.Current.Value;
|
||||
room.MaxParticipants = int.TryParse(MaxParticipantsField.Text, out int maxParticipants) ? maxParticipants : null;
|
||||
room.MaxAttempts = int.TryParse(MaxAttemptsField.Text, out int maxAttempts) ? maxAttempts : null;
|
||||
room.Duration = DurationField.Current.Value;
|
||||
|
||||
loadingLayer.Show();
|
||||
manager?.CreateRoom(room, onSuccess, onError);
|
||||
@@ -422,7 +471,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
.Select(int.Parse)
|
||||
.ToArray();
|
||||
|
||||
foreach (var item in Playlist)
|
||||
foreach (var item in room.Playlist)
|
||||
{
|
||||
if (invalidBeatmapIDs.Contains(item.Beatmap.OnlineID))
|
||||
item.MarkInvalid();
|
||||
@@ -436,6 +485,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
ErrorText.FadeIn(50);
|
||||
loadingLayer.Hide();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CreateRoomButton : RoundedButton
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -22,6 +20,7 @@ using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
@@ -33,20 +32,23 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
|
||||
private readonly IBindable<bool> isIdle = new BindableBool();
|
||||
|
||||
private MatchLeaderboard leaderboard;
|
||||
private SelectionPollingComponent selectionPollingComponent;
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IdleTracker? idleTracker { get; set; }
|
||||
|
||||
private FillFlowContainer progressSection;
|
||||
private MatchLeaderboard leaderboard = null!;
|
||||
private SelectionPollingComponent selectionPollingComponent = null!;
|
||||
private FillFlowContainer progressSection = null!;
|
||||
private DrawableRoomPlaylist drawablePlaylist = null!;
|
||||
|
||||
public PlaylistsRoomSubScreen(Room room)
|
||||
: base(room, false) // Editing is temporarily not allowed.
|
||||
{
|
||||
Title = room.RoomID.Value == null ? "New playlist" : room.Name.Value;
|
||||
Title = room.RoomID == null ? "New playlist" : room.Name;
|
||||
Activity.Value = new UserActivity.InLobby(room);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] IdleTracker idleTracker)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
if (idleTracker != null)
|
||||
isIdle.BindTo(idleTracker.IsIdle);
|
||||
@@ -59,19 +61,47 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
base.LoadComplete();
|
||||
|
||||
isIdle.BindValueChanged(_ => updatePollingRate(), true);
|
||||
RoomId.BindValueChanged(id =>
|
||||
{
|
||||
if (id.NewValue != null)
|
||||
{
|
||||
// Set the first playlist item.
|
||||
// This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()).
|
||||
Schedule(() => SelectedItem.Value = Room.Playlist.FirstOrDefault());
|
||||
}
|
||||
}, true);
|
||||
|
||||
Room.MaxAttempts.BindValueChanged(_ => progressSection.Alpha = Room.MaxAttempts.Value != null ? 1 : 0, true);
|
||||
Room.PropertyChanged += onRoomPropertyChanged;
|
||||
updateSetupState();
|
||||
updateRoomMaxAttempts();
|
||||
updateRoomPlaylist();
|
||||
}
|
||||
|
||||
private void onRoomPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(Room.RoomID):
|
||||
updateSetupState();
|
||||
break;
|
||||
|
||||
case nameof(Room.MaxAttempts):
|
||||
updateRoomMaxAttempts();
|
||||
break;
|
||||
|
||||
case nameof(Room.Playlist):
|
||||
updateRoomPlaylist();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSetupState()
|
||||
{
|
||||
if (Room.RoomID != null)
|
||||
{
|
||||
// Set the first playlist item.
|
||||
// This is scheduled since updating the room and playlist may happen in an arbitrary order (via Room.CopyFrom()).
|
||||
Schedule(() => SelectedItem.Value = Room.Playlist.FirstOrDefault());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRoomMaxAttempts()
|
||||
=> progressSection.Alpha = Room.MaxAttempts != null ? 1 : 0;
|
||||
|
||||
private void updateRoomPlaylist()
|
||||
=> drawablePlaylist.Items.ReplaceRange(0, drawablePlaylist.Items.Count, Room.Playlist);
|
||||
|
||||
protected override Drawable CreateMainContent() => new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@@ -92,7 +122,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
new Drawable?[]
|
||||
{
|
||||
// Playlist items column
|
||||
new GridContainer
|
||||
@@ -101,20 +131,19 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
Padding = new MarginPadding { Right = 5 },
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[] { new OverlinedPlaylistHeader(), },
|
||||
new Drawable[] { new OverlinedPlaylistHeader(Room), },
|
||||
new Drawable[]
|
||||
{
|
||||
new DrawableRoomPlaylist
|
||||
drawablePlaylist = new DrawableRoomPlaylist
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Items = { BindTarget = Room.Playlist },
|
||||
SelectedItem = { BindTarget = SelectedItem },
|
||||
AllowSelection = true,
|
||||
AllowShowingResults = true,
|
||||
RequestResults = item =>
|
||||
{
|
||||
Debug.Assert(RoomId.Value != null);
|
||||
ParentScreen?.Push(new PlaylistItemUserResultsScreen(null, RoomId.Value.Value, item));
|
||||
Debug.Assert(Room.RoomID != null);
|
||||
ParentScreen?.Push(new PlaylistItemUserResultsScreen(null, Room.RoomID.Value, item));
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -183,7 +212,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OverlinedHeader("Progress"),
|
||||
new RoomLocalUserInfo(),
|
||||
new RoomLocalUserInfo(Room),
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -191,7 +220,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
new OverlinedHeader("Leaderboard")
|
||||
},
|
||||
new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
|
||||
new Drawable[] { leaderboard = new MatchLeaderboard(Room) { RelativeSizeAxes = Axes.Both }, },
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
@@ -224,7 +253,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
}
|
||||
};
|
||||
|
||||
protected override Drawable CreateFooter() => new PlaylistsRoomFooter
|
||||
protected override Drawable CreateFooter() => new PlaylistsRoomFooter(Room)
|
||||
{
|
||||
OnStart = StartPlay
|
||||
};
|
||||
@@ -248,5 +277,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
Exited = () => leaderboard.RefetchScores()
|
||||
});
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
Room.PropertyChanged -= onRoomPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user