mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 08:32:57 +08:00
Make CurrentPlaylistItem
not a bindable
This commit is contained in:
parent
9f08b37792
commit
72564b5c98
@ -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;
|
||||
@ -31,8 +30,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[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());
|
||||
|
||||
|
@ -41,6 +41,31 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
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,
|
||||
@ -56,16 +81,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
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)
|
||||
}
|
||||
Playlist = { item1 },
|
||||
CurrentPlaylistItem = item1
|
||||
}),
|
||||
createLoungeRoom(new Room
|
||||
{
|
||||
@ -74,46 +91,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
Playlist = { item1, item2 },
|
||||
CurrentPlaylistItem = item1
|
||||
}),
|
||||
createLoungeRoom(new Room
|
||||
{
|
||||
@ -181,20 +168,29 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
Name = { Value = "A host-only room" },
|
||||
QueueMode = { Value = QueueMode.HostOnly },
|
||||
Type = { Value = MatchType.HeadToHead }
|
||||
}),
|
||||
Type = { Value = 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 }
|
||||
}),
|
||||
})
|
||||
{
|
||||
SelectedItem = new Bindable<PlaylistItem>()
|
||||
},
|
||||
new DrawableMatchRoom(new Room
|
||||
{
|
||||
Name = { Value = "A round-robin room" },
|
||||
QueueMode = { Value = QueueMode.AllPlayersRoundRobin },
|
||||
Type = { Value = MatchType.HeadToHead }
|
||||
}),
|
||||
})
|
||||
{
|
||||
SelectedItem = new Bindable<PlaylistItem>()
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -215,7 +211,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
return new DrawableLoungeRoom(room)
|
||||
{
|
||||
MatchingFilter = true,
|
||||
SelectedRoom = { BindTarget = selectedRoom }
|
||||
SelectedRoom = selectedRoom
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -43,9 +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 } };
|
||||
|
||||
@ -107,31 +104,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 +143,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(250, 50),
|
||||
SelectedItem = new Bindable<PlaylistItem?>(item)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -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?>()
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -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!;
|
||||
@ -56,7 +53,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
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?>()
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 = 500;
|
||||
|
||||
/// <summary>
|
||||
/// Delay before the background is unloaded while off-screen.
|
||||
/// </summary>
|
||||
public double BackgroundUnloadDelay = 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 };
|
||||
|
@ -203,7 +203,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
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.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;
|
||||
@ -847,7 +847,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
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.CurrentPlaylistItem = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId);
|
||||
APIRoom.AutoSkip.Value = Room.Settings.AutoSkip;
|
||||
|
||||
RoomUpdated?.Invoke();
|
||||
|
@ -1,10 +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;
|
||||
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;
|
||||
@ -16,8 +17,25 @@ using osu.Game.Online.Rooms.RoomStatuses;
|
||||
namespace osu.Game.Online.Rooms
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public partial class Room : IDependencyInjectionCandidate
|
||||
public partial class Room : IDependencyInjectionCandidate, INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
[JsonProperty("current_playlist_item")]
|
||||
private PlaylistItem? currentPlaylistItem;
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("id")]
|
||||
public readonly Bindable<long?> RoomID = new Bindable<long?>();
|
||||
@ -28,7 +46,7 @@ namespace osu.Game.Online.Rooms
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("host")]
|
||||
public readonly Bindable<APIUser> Host = new Bindable<APIUser>();
|
||||
public readonly Bindable<APIUser?> Host = new Bindable<APIUser?>();
|
||||
|
||||
[Cached]
|
||||
[JsonProperty("playlist")]
|
||||
@ -38,10 +56,6 @@ namespace osu.Game.Online.Rooms
|
||||
[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>();
|
||||
@ -126,7 +140,7 @@ namespace osu.Game.Online.Rooms
|
||||
|
||||
[Cached(Name = nameof(Password))]
|
||||
[JsonProperty("password")]
|
||||
public readonly Bindable<string> Password = new Bindable<string>();
|
||||
public readonly Bindable<string?> Password = new Bindable<string?>();
|
||||
|
||||
[Cached]
|
||||
public readonly Bindable<TimeSpan?> Duration = new Bindable<TimeSpan?>();
|
||||
@ -203,7 +217,7 @@ namespace osu.Game.Online.Rooms
|
||||
AutoStartDuration.Value = other.AutoStartDuration.Value;
|
||||
DifficultyRange.Value = other.DifficultyRange.Value;
|
||||
PlaylistItemStats.Value = other.PlaylistItemStats.Value;
|
||||
CurrentPlaylistItem.Value = other.CurrentPlaylistItem.Value;
|
||||
CurrentPlaylistItem = other.CurrentPlaylistItem;
|
||||
AutoSkip.Value = other.AutoSkip.Value;
|
||||
|
||||
other.RemoveExpiredPlaylistItems();
|
||||
@ -240,7 +254,7 @@ namespace osu.Game.Online.Rooms
|
||||
public int CountTotal;
|
||||
|
||||
[JsonProperty("ruleset_ids")]
|
||||
public int[] RulesetIDs;
|
||||
public int[] RulesetIDs = [];
|
||||
}
|
||||
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
@ -252,5 +266,18 @@ 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 };
|
||||
}
|
||||
}
|
@ -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.Diagnostics;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -16,6 +15,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;
|
||||
@ -29,27 +29,28 @@ using osuTK.Graphics;
|
||||
|
||||
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 UpdateableBeatmapBackgroundSprite background = null!;
|
||||
private DelayedLoadWrapper wrapper = null!;
|
||||
|
||||
private DelayedLoadWrapper wrapper;
|
||||
|
||||
public DrawableRoom(Room room)
|
||||
protected DrawableRoom(Room room)
|
||||
{
|
||||
Room = room;
|
||||
|
||||
@ -77,7 +78,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 +86,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;
|
||||
}),
|
||||
@ -186,7 +187,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
Font = OsuFont.GetFont(size: 28),
|
||||
Current = { BindTarget = Room.Name }
|
||||
},
|
||||
new RoomStatusText()
|
||||
new RoomStatusText
|
||||
{
|
||||
SelectedItem = { BindTarget = SelectedItem }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -245,6 +249,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
wrapper.DelayedLoadComplete += _ =>
|
||||
{
|
||||
Debug.Assert(specialCategoryPill != null);
|
||||
Debug.Assert(endDateInfo != null);
|
||||
Debug.Assert(passwordIcon != null);
|
||||
|
||||
wrapper.FadeInFromZero(200);
|
||||
|
||||
roomCategory.BindTo(Room.Category);
|
||||
@ -265,6 +273,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
hasPassword.BindTo(Room.HasPassword);
|
||||
hasPassword.BindValueChanged(v => passwordIcon.Alpha = v.NewValue ? 1 : 0, true);
|
||||
};
|
||||
|
||||
SelectedItem.BindValueChanged(item => background.Beatmap.Value = item.NewValue?.Beatmap, true);
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
@ -289,7 +299,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Drawable CreateBackground() => new OnlinePlayBackgroundSprite();
|
||||
protected virtual UpdateableBeatmapBackgroundSprite CreateBackground() => new UpdateableBeatmapBackgroundSprite();
|
||||
|
||||
protected virtual IEnumerable<Drawable> CreateBottomDetails()
|
||||
{
|
||||
@ -324,14 +334,16 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
|
||||
private partial class RoomStatusText : OnlinePlayComposite
|
||||
{
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
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!;
|
||||
|
||||
private SpriteText statusText = null!;
|
||||
private LinkFlowContainer beatmapText = null!;
|
||||
|
||||
public RoomStatusText()
|
||||
{
|
||||
@ -383,12 +395,12 @@ 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();
|
||||
|
@ -140,7 +140,7 @@ 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);
|
||||
}
|
||||
|
@ -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)
|
||||
@ -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);
|
||||
@ -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,14 +174,14 @@ 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;
|
||||
}
|
||||
|
||||
@ -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,10 +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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -21,25 +18,22 @@ 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;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private readonly IBindable<APIUser> host = new Bindable<APIUser>();
|
||||
private readonly BindableWithCurrent<PlaylistItem?> current = new BindableWithCurrent<PlaylistItem?>();
|
||||
private readonly IBindable<APIUser?> host = new Bindable<APIUser?>();
|
||||
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;
|
||||
|
||||
base.SelectedItem.BindTo(SelectedItem);
|
||||
host.BindTo(room.Host);
|
||||
}
|
||||
|
||||
@ -58,21 +52,23 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
}
|
||||
}
|
||||
|
||||
public new required Bindable<PlaylistItem?> SelectedItem
|
||||
{
|
||||
get => current;
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
protected override Drawable CreateBackground() => background = new BackgroundSprite();
|
||||
|
||||
private partial class BackgroundSprite : UpdateableBeatmapBackgroundSprite
|
||||
protected override UpdateableBeatmapBackgroundSprite CreateBackground() => base.CreateBackground().With(d =>
|
||||
{
|
||||
protected override double LoadDelay => 0;
|
||||
}
|
||||
d.BackgroundLoadDelay = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ 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;
|
||||
@ -164,7 +163,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
new DrawableMatchRoom(Room, allowEdit)
|
||||
{
|
||||
OnEdit = () => settingsOverlay.Show(),
|
||||
SelectedItem = { BindTarget = SelectedItem }
|
||||
SelectedItem = SelectedItem
|
||||
}
|
||||
},
|
||||
null,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -28,14 +28,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)
|
||||
@ -48,7 +54,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Y,
|
||||
SettingsApplied = Hide
|
||||
SettingsApplied = Hide,
|
||||
SelectedItem = { BindTarget = SelectedItem }
|
||||
};
|
||||
|
||||
protected partial class MatchSettings : OnlinePlayComposite
|
||||
@ -57,6 +64,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
|
||||
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 +74,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!;
|
||||
@ -367,7 +374,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
||||
base.LoadComplete();
|
||||
|
||||
drawablePlaylist.Items.BindTo(Playlist);
|
||||
drawablePlaylist.SelectedItem.BindTo(CurrentPlaylistItem);
|
||||
drawablePlaylist.SelectedItem.BindTo(SelectedItem);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -448,7 +455,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
|
||||
{
|
||||
|
@ -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,9 +33,7 @@ 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 BindableWithCurrent<PlaylistItem?> selectedItem = new BindableWithCurrent<PlaylistItem?>();
|
||||
private MultiplayerPlaylistTabControl playlistTabControl = null!;
|
||||
private MultiplayerQueueList queueList = null!;
|
||||
private MultiplayerHistoryList historyList = null!;
|
||||
@ -58,14 +62,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
||||
queueList = new MultiplayerQueueList
|
||||
{
|
||||
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,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,12 +44,12 @@ 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)
|
||||
@ -95,7 +94,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
new Drawable?[]
|
||||
{
|
||||
// Participants column
|
||||
new GridContainer
|
||||
@ -142,7 +141,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
new MultiplayerPlaylist
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -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,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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -10,7 +8,6 @@ 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
|
||||
{
|
||||
@ -20,96 +17,69 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
public partial class OnlinePlayComposite : CompositeDrawable
|
||||
{
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<long?> RoomID { get; private set; }
|
||||
protected Bindable<long?> RoomID { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.Name))]
|
||||
protected Bindable<string> RoomName { get; private set; }
|
||||
protected Bindable<string> RoomName { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<APIUser> Host { get; private set; }
|
||||
protected Bindable<APIUser> Host { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<RoomStatus> Status { get; private set; }
|
||||
protected Bindable<RoomStatus> Status { get; private set; } = null!;
|
||||
|
||||
[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; }
|
||||
protected Bindable<MatchType> Type { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<Room.RoomPlaylistItemStats> PlaylistItemStats { get; private set; }
|
||||
protected Bindable<Room.RoomPlaylistItemStats> PlaylistItemStats { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<Room.RoomDifficultyRange> DifficultyRange { get; private set; }
|
||||
protected Bindable<Room.RoomDifficultyRange> DifficultyRange { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<RoomCategory> Category { get; private set; }
|
||||
protected Bindable<RoomCategory> Category { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected BindableList<APIUser> RecentParticipants { get; private set; }
|
||||
protected BindableList<APIUser> RecentParticipants { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<int> ParticipantCount { get; private set; }
|
||||
protected Bindable<int> ParticipantCount { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<int?> MaxParticipants { get; private set; }
|
||||
protected Bindable<int?> MaxParticipants { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<int?> MaxAttempts { get; private set; }
|
||||
protected Bindable<int?> MaxAttempts { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
public Bindable<PlaylistAggregateScore> UserScore { get; private set; }
|
||||
public Bindable<PlaylistAggregateScore> UserScore { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<DateTimeOffset?> StartDate { get; private set; }
|
||||
protected Bindable<DateTimeOffset?> StartDate { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<DateTimeOffset?> EndDate { get; private set; }
|
||||
protected Bindable<DateTimeOffset?> EndDate { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<RoomAvailability> Availability { get; private set; }
|
||||
protected Bindable<RoomAvailability> Availability { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
public Bindable<string> Password { get; private set; }
|
||||
public Bindable<string> Password { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<TimeSpan?> Duration { get; private set; }
|
||||
protected Bindable<TimeSpan?> Duration { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<QueueMode> QueueMode { get; private set; }
|
||||
protected Bindable<QueueMode> QueueMode { get; private set; } = null!;
|
||||
|
||||
[Resolved(typeof(Room))]
|
||||
protected Bindable<TimeSpan> AutoStartDuration { get; private set; }
|
||||
protected Bindable<TimeSpan> AutoStartDuration { get; private set; } = null!;
|
||||
|
||||
[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;
|
||||
}
|
||||
protected Bindable<bool> AutoSkip { get; private set; } = null!;
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
Name = ServerAPIRoom.Name.Value,
|
||||
MatchType = ServerAPIRoom.Type.Value,
|
||||
Password = password,
|
||||
Password = password ?? string.Empty,
|
||||
QueueMode = ServerAPIRoom.QueueMode.Value,
|
||||
AutoStartDuration = ServerAPIRoom.AutoStartDuration.Value
|
||||
},
|
||||
|
@ -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.Diagnostics;
|
||||
@ -296,10 +294,11 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
||||
Debug.Assert(result != null);
|
||||
|
||||
// Playlist item IDs and beatmaps aren't serialised.
|
||||
if (source.CurrentPlaylistItem.Value != null)
|
||||
if (source.CurrentPlaylistItem != null)
|
||||
{
|
||||
result.CurrentPlaylistItem.Value = result.CurrentPlaylistItem.Value.With(new Optional<IBeatmapInfo>(source.CurrentPlaylistItem.Value.Beatmap));
|
||||
result.CurrentPlaylistItem.Value.ID = source.CurrentPlaylistItem.Value.ID;
|
||||
Debug.Assert(result.CurrentPlaylistItem != null);
|
||||
result.CurrentPlaylistItem = result.CurrentPlaylistItem.With(new Optional<IBeatmapInfo>(source.CurrentPlaylistItem.Beatmap));
|
||||
result.CurrentPlaylistItem.ID = source.CurrentPlaylistItem.ID;
|
||||
}
|
||||
|
||||
for (int i = 0; i < source.Playlist.Count; i++)
|
||||
|
Loading…
Reference in New Issue
Block a user