1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-03 13:44:34 +08:00

Merge branch 'master' into multiple_keys

This commit is contained in:
Iamnotcoding
2025-03-31 20:35:09 +09:00
committed by GitHub
Unverified
8 changed files with 88 additions and 115 deletions
@@ -43,7 +43,6 @@ namespace osu.Game.Tests.Visual.DailyChallenge
{
var room = new Room
{
RoomID = 1234,
Name = "Daily Challenge: June 4, 2024",
Playlist =
[
@@ -66,7 +65,6 @@ namespace osu.Game.Tests.Visual.DailyChallenge
{
var room = new Room
{
RoomID = 1234,
Name = "Daily Challenge: June 4, 2024",
Playlist =
[
@@ -99,7 +97,6 @@ namespace osu.Game.Tests.Visual.DailyChallenge
{
var room = new Room
{
RoomID = 1234,
Name = "Daily Challenge: June 4, 2024",
Playlist =
[
@@ -114,7 +111,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
};
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
AddStep("set daily challenge info", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = 1234 });
AddStep("set daily challenge info", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = room.RoomID!.Value });
Screens.OnlinePlay.DailyChallenge.DailyChallenge screen = null!;
AddStep("push screen", () => LoadScreen(screen = new Screens.OnlinePlay.DailyChallenge.DailyChallenge(room)));
@@ -128,7 +125,6 @@ namespace osu.Game.Tests.Visual.DailyChallenge
{
var room = new Room
{
RoomID = 1234,
Name = "Daily Challenge: June 4, 2024",
Playlist =
[
@@ -143,7 +139,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
};
AddStep("add room", () => API.Perform(new CreateRoomRequest(room)));
AddStep("set daily challenge info", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = 1234 });
AddStep("set daily challenge info", () => metadataClient.DailyChallengeInfo.Value = new DailyChallengeInfo { RoomID = room.RoomID!.Value });
Screens.OnlinePlay.DailyChallenge.DailyChallenge screen = null!;
AddStep("push screen", () => LoadScreen(screen = new Screens.OnlinePlay.DailyChallenge.DailyChallenge(room)));
@@ -44,17 +44,17 @@ namespace osu.Game.Tests.Visual.DailyChallenge
[Test]
public void TestDailyChallenge()
{
startChallenge(1234);
startChallenge();
AddStep("push screen", () => LoadScreen(new DailyChallengeIntro(room)));
}
[Test]
public void TestPlayIntroOnceFlag()
{
startChallenge(1234);
startChallenge();
AddStep("set intro played flag", () => Dependencies.Get<SessionStatics>().SetValue(Static.DailyChallengeIntroPlayed, true));
startChallenge(1235);
startChallenge();
AddAssert("intro played flag reset", () => Dependencies.Get<SessionStatics>().Get<bool>(Static.DailyChallengeIntroPlayed), () => Is.False);
@@ -62,13 +62,12 @@ namespace osu.Game.Tests.Visual.DailyChallenge
AddUntilStep("intro played flag set", () => Dependencies.Get<SessionStatics>().Get<bool>(Static.DailyChallengeIntroPlayed), () => Is.True);
}
private void startChallenge(int roomId)
private void startChallenge()
{
AddStep("add room", () =>
{
API.Perform(new CreateRoomRequest(room = new Room
{
RoomID = roomId,
Name = "Daily Challenge: June 4, 2024",
Playlist =
[
@@ -83,7 +82,7 @@ namespace osu.Game.Tests.Visual.DailyChallenge
Category = RoomCategory.DailyChallenge
}));
});
AddStep("signal client", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo { RoomID = roomId }));
AddStep("signal client", () => metadataClient.DailyChallengeUpdated(new DailyChallengeInfo { RoomID = room.RoomID!.Value }));
}
}
}
@@ -6,7 +6,6 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Platform;
@@ -32,7 +31,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
private BeatmapManager beatmaps = null!;
private BeatmapSetInfo importedSet = null!;
private BeatmapInfo importedBeatmap = null!;
private Room room = null!;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
@@ -47,19 +45,17 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
base.SetUpSteps();
AddStep("create room", () => room = CreateDefaultRoom());
AddStep("join room", () => JoinRoom(room));
AddStep("join room", () => JoinRoom(CreateDefaultRoom()));
WaitForJoined();
AddStep("create list", () =>
{
Child = list = new MultiplayerPlaylist(room)
Child = list = new MultiplayerPlaylist
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.4f, 0.8f),
SelectedItem = new Bindable<PlaylistItem?>()
Size = new Vector2(0.4f, 0.8f)
};
});
@@ -158,37 +154,36 @@ namespace osu.Game.Tests.Visual.Multiplayer
assertQueueTabCount(0);
}
[Ignore("Expired items are initially removed from the room.")]
[Test]
public void TestJoinRoomWithMixedItemsAddedInCorrectLists()
{
AddStep("leave room", () => MultiplayerClient.LeaveRoom());
AddUntilStep("wait for room part", () => !RoomJoined);
AddStep("join room with items", () =>
AddStep("join room with expired items", () =>
{
API.Queue(new CreateRoomRequest(new Room
{
Name = "test name",
Playlist =
[
new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
RulesetID = Ruleset.Value.OnlineID
},
new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
RulesetID = Ruleset.Value.OnlineID,
Expired = true
}
]
}));
Room room = CreateDefaultRoom();
room.Playlist =
[
new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
RulesetID = Ruleset.Value.OnlineID
},
new PlaylistItem(new TestBeatmap(Ruleset.Value).BeatmapInfo)
{
RulesetID = Ruleset.Value.OnlineID,
Expired = true
}
];
JoinRoom(room);
});
AddUntilStep("wait for room join", () => RoomJoined);
WaitForJoined();
assertItemInQueueListStep(1, 0);
assertItemInHistoryListStep(2, 0);
// IDs are offset by 1 because we've joined two rooms in this test.
assertItemInQueueListStep(2, 0);
assertItemInHistoryListStep(3, 0);
}
[Test]
@@ -49,13 +49,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("create playlist", () =>
{
Child = playlist = new MultiplayerQueueList(room)
Child = playlist = new MultiplayerQueueList
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 300),
Size = new Vector2(500, 300)
};
playlist.Items.ReplaceRange(0, playlist.Items.Count, MultiplayerClient.ClientAPIRoom!.Playlist);
MultiplayerClient.ClientAPIRoom!.PropertyChanged += (_, e) =>
{
if (e.PropertyName == nameof(Room.Playlist))
@@ -132,6 +134,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
assertDeleteButtonVisibility(1, false);
}
[Test]
public void TestChangeExistingItem()
{
AddStep("change beatmap", () => MultiplayerClient.EditPlaylistItem(new MultiplayerPlaylistItem
{
ID = playlist.Items[0].ID,
BeatmapID = 1337
}).WaitSafely());
AddUntilStep("first playlist item has new beatmap", () => playlist.Items[0].Beatmap.OnlineID, () => Is.EqualTo(1337));
}
private void addPlaylistItem(Func<int> userId)
{
long itemId = -1;
@@ -5,6 +5,7 @@ using System;
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.Multiplayer;
@@ -19,12 +20,6 @@ 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>
@@ -33,18 +28,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
[Resolved]
private MultiplayerClient client { 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()
{
@@ -65,17 +53,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
Masking = true,
Children = new Drawable[]
{
queueList = new MultiplayerQueueList(room)
queueList = new MultiplayerQueueList
{
RelativeSizeAxes = Axes.Both,
SelectedItem = { BindTarget = selectedItem },
RequestEdit = item => RequestEdit?.Invoke(item)
},
historyList = new MultiplayerHistoryList
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
SelectedItem = { BindTarget = selectedItem }
}
}
}
@@ -89,10 +75,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
base.LoadComplete();
DisplayMode.BindValueChanged(onDisplayModeChanged, true);
client.ItemAdded += playlistItemAdded;
client.ItemRemoved += playlistItemRemoved;
client.ItemChanged += playlistItemChanged;
client.RoomUpdated += onRoomUpdated;
updateState();
}
@@ -121,28 +109,28 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
firstPopulation = false;
}
PlaylistItem? currentItem = client.Room == null ? null : new PlaylistItem(client.Room.CurrentPlaylistItem);
queueList.SelectedItem.Value = currentItem;
historyList.SelectedItem.Value = currentItem;
}
private void playlistItemAdded(MultiplayerPlaylistItem item) => Schedule(() => addItemToLists(item));
private void playlistItemAdded(MultiplayerPlaylistItem item) => Scheduler.Add(() => addItemToLists(item));
private void playlistItemRemoved(long item) => Schedule(() => removeItemFromLists(item));
private void playlistItemRemoved(long item) => Scheduler.Add(() => removeItemFromLists(item));
private void playlistItemChanged(MultiplayerPlaylistItem item) => Schedule(() =>
private void playlistItemChanged(MultiplayerPlaylistItem item) => Scheduler.Add(() =>
{
if (client.Room == null)
return;
var newApiItem = new PlaylistItem(item);
var existingApiItemInQueue = queueList.Items.SingleOrDefault(i => i.ID == item.ID);
var existingItem = queueList.Items.SingleOrDefault(i => i.ID == item.ID);
// Test if the only change between the two playlist items is the order.
if (existingApiItemInQueue != null && existingApiItemInQueue.With(playlistOrder: newApiItem.PlaylistOrder).Equals(newApiItem))
if (existingItem != null && existingItem.With(playlistOrder: item.PlaylistOrder).Equals(new PlaylistItem(item)))
{
// Set the new playlist order directly without refreshing the DrawablePlaylistItem.
existingApiItemInQueue.PlaylistOrder = newApiItem.PlaylistOrder;
// The following isn't really required, but is here for safety and explicitness.
// MultiplayerQueueList internally binds to changes in Playlist to invalidate its own layout, which is mutated on every playlist operation.
// Set the new order directly and refresh the flow layout as an optimisation to avoid refreshing the items' visual state.
existingItem.PlaylistOrder = item.PlaylistOrder;
queueList.Invalidate();
}
else
@@ -154,22 +142,35 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
private void addItemToLists(MultiplayerPlaylistItem item)
{
var apiItem = client.Room?.Playlist.SingleOrDefault(i => i.ID == item.ID);
// Item could have been removed from the playlist while the local player was in gameplay.
if (apiItem == null)
if (client.Room == null)
return;
if (item.Expired)
historyList.Items.Add(new PlaylistItem(apiItem));
historyList.Items.Add(new PlaylistItem(item));
else
queueList.Items.Add(new PlaylistItem(apiItem));
queueList.Items.Add(new PlaylistItem(item));
}
private void removeItemFromLists(long item)
private void removeItemFromLists(long itemId)
{
queueList.Items.RemoveAll(i => i.ID == item);
historyList.Items.RemoveAll(i => i.ID == item);
if (client.Room == null)
return;
queueList.Items.RemoveAll(i => i.ID == itemId);
historyList.Items.RemoveAll(i => i.ID == itemId);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client.IsNotNull())
{
client.ItemAdded -= playlistItemAdded;
client.ItemRemoved -= playlistItemRemoved;
client.ItemChanged -= playlistItemChanged;
client.RoomUpdated -= onRoomUpdated;
}
}
}
}
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
@@ -20,50 +19,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
/// </summary>
public partial class MultiplayerQueueList : DrawableRoomPlaylist
{
private readonly Room room;
private QueueFillFlowContainer flow = null!;
public MultiplayerQueueList(Room room)
public MultiplayerQueueList()
{
this.room = room;
ShowItemOwners = true;
}
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
protected override FillFlowContainer<RearrangeableListItem<PlaylistItem>> CreateListFillFlowContainer() => 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>>
{
public new void InvalidateLayout() => base.InvalidateLayout();
public override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.OfType<RearrangeableListItem<PlaylistItem>>().OrderBy(item => item.Model.PlaylistOrder);
}
@@ -145,11 +145,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
null,
new Drawable[]
{
new MultiplayerPlaylist(Room)
new MultiplayerPlaylist
{
RelativeSizeAxes = Axes.Both,
RequestEdit = OpenSongSelection,
SelectedItem = SelectedItem
RequestEdit = OpenSongSelection
}
},
new[]
@@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
/// <param name="host">The room host.</param>
public void AddServerSideRoom(Room room, APIUser host)
{
room.RoomID ??= currentRoomId++;
room.RoomID = currentRoomId++;
room.Host = host;
for (int i = 0; i < room.Playlist.Count; i++)