Give playlist items a PlayedAt date

This commit is contained in:
Dan Balasescu
2021-12-03 20:05:25 +09:00
parent 9d6fe558c2
commit d5803e541b
6 changed files with 26 additions and 30 deletions

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -168,7 +169,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
Beatmap = { Value = importedBeatmap }, Beatmap = { Value = importedBeatmap },
BeatmapID = importedBeatmap.OnlineID ?? -1, BeatmapID = importedBeatmap.OnlineID ?? -1,
Expired = expired Expired = expired,
PlayedAt = DateTimeOffset.Now
}))); })));
/// <summary> /// <summary>

View File

@ -742,7 +742,8 @@ namespace osu.Game.Online.Multiplayer
Beatmap = { Value = apiBeatmap }, Beatmap = { Value = apiBeatmap },
Ruleset = { Value = ruleset }, Ruleset = { Value = ruleset },
Expired = item.Expired, Expired = item.Expired,
PlaylistOrder = item.PlaylistOrder PlaylistOrder = item.PlaylistOrder,
PlayedAt = item.PlayedAt
}; };
playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance))); playlistItem.RequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance)));

View File

@ -46,11 +46,10 @@ namespace osu.Game.Online.Rooms
public ushort PlaylistOrder { get; set; } public ushort PlaylistOrder { get; set; }
/// <summary> /// <summary>
/// The date when this <see cref="MultiplayerPlaylistItem"/> was last updated. /// The date when this <see cref="MultiplayerPlaylistItem"/> was played.
/// Not serialised to/from the client.
/// </summary> /// </summary>
[IgnoreMember] [Key(9)]
public DateTimeOffset UpdatedAt { get; set; } public DateTimeOffset? PlayedAt { get; set; }
public MultiplayerPlaylistItem() public MultiplayerPlaylistItem()
{ {
@ -66,6 +65,7 @@ namespace osu.Game.Online.Rooms
AllowedMods = item.AllowedMods.Select(m => new APIMod(m)).ToArray(); AllowedMods = item.AllowedMods.Select(m => new APIMod(m)).ToArray();
Expired = item.Expired; Expired = item.Expired;
PlaylistOrder = item.PlaylistOrder ?? 0; PlaylistOrder = item.PlaylistOrder ?? 0;
PlayedAt = item.PlayedAt;
} }
} }
} }

View File

@ -36,6 +36,9 @@ namespace osu.Game.Online.Rooms
[JsonProperty("playlist_order")] [JsonProperty("playlist_order")]
public ushort? PlaylistOrder { get; set; } public ushort? PlaylistOrder { get; set; }
[JsonProperty("played_at")]
public DateTimeOffset? PlayedAt { get; set; }
[JsonIgnore] [JsonIgnore]
public IBindable<bool> Valid => valid; public IBindable<bool> Valid => valid;

View File

@ -27,7 +27,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
private class HistoryFillFlowContainer : FillFlowContainer<RearrangeableListItem<PlaylistItem>> private class HistoryFillFlowContainer : FillFlowContainer<RearrangeableListItem<PlaylistItem>>
{ {
public override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.OfType<RearrangeableListItem<PlaylistItem>>().OrderByDescending(item => item.Model.PlaylistOrder); public override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.OfType<RearrangeableListItem<PlaylistItem>>().OrderByDescending(item => item.Model.PlayedAt);
} }
} }
} }

View File

@ -400,7 +400,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
// Expire the current playlist item. // Expire the current playlist item.
currentItem.Expired = true; currentItem.Expired = true;
currentItem.UpdatedAt = DateTimeOffset.Now; currentItem.PlayedAt = DateTimeOffset.Now;
await ((IMultiplayerClient)this).PlaylistItemChanged(currentItem).ConfigureAwait(false); await ((IMultiplayerClient)this).PlaylistItemChanged(currentItem).ConfigureAwait(false);
await updatePlaylistOrder(Room).ConfigureAwait(false); await updatePlaylistOrder(Room).ConfigureAwait(false);
@ -430,9 +430,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
Debug.Assert(Room != null); Debug.Assert(Room != null);
// Some tests can add items in already-expired states.
item.UpdatedAt = DateTimeOffset.Now;
// Add the item to the list first in order to compute gameplay order. // Add the item to the list first in order to compute gameplay order.
serverSidePlaylist.Add(item); serverSidePlaylist.Add(item);
await updatePlaylistOrder(Room).ConfigureAwait(false); await updatePlaylistOrder(Room).ConfigureAwait(false);
@ -443,8 +440,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
private async Task updateCurrentItem(MultiplayerRoom room, bool notify = true) private async Task updateCurrentItem(MultiplayerRoom room, bool notify = true)
{ {
// The playlist is already in correct gameplay order, so pick the next non-expired item or default to the last item. MultiplayerPlaylistItem nextItem = serverSidePlaylist
MultiplayerPlaylistItem nextItem = serverSidePlaylist.FirstOrDefault(i => !i.Expired) ?? room.Playlist.Last(); .Where(i => !i.Expired)
.OrderBy(i => i.PlaylistOrder)
.FirstOrDefault()
?? room.Playlist.Last();
currentIndex = serverSidePlaylist.IndexOf(nextItem); currentIndex = serverSidePlaylist.IndexOf(nextItem);
long lastItem = room.Settings.PlaylistItemId; long lastItem = room.Settings.PlaylistItemId;
@ -494,29 +495,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
break; break;
} }
// For expired items, it's important that they're ordered in ascending order such that the last updated item is the last in the list.
// This is so that the updated_at database column doesn't get refreshed as a result of change in ordering.
List<MultiplayerPlaylistItem> orderedExpiredItems = serverSidePlaylist.Where(item => item.Expired).OrderBy(item => item.UpdatedAt).ToList();
for (int i = 0; i < orderedExpiredItems.Count; i++)
await setOrder(orderedExpiredItems[i], (ushort)i).ConfigureAwait(false);
for (int i = 0; i < orderedActiveItems.Count; i++) for (int i = 0; i < orderedActiveItems.Count; i++)
await setOrder(orderedActiveItems[i], (ushort)i).ConfigureAwait(false);
serverSidePlaylist.Clear();
serverSidePlaylist.AddRange(orderedExpiredItems);
serverSidePlaylist.AddRange(orderedActiveItems);
async Task setOrder(MultiplayerPlaylistItem item, ushort order)
{ {
if (item.PlaylistOrder == order) var item = orderedActiveItems[i];
return;
item.PlaylistOrder = order; if (item.PlaylistOrder == i)
continue;
item.PlaylistOrder = (ushort)i;
// Items which have an ID of 0 are not in the database, so avoid propagating database/hub events for them. // Items which have an ID of 0 are not in the database, so avoid propagating database/hub events for them.
if (item.ID <= 0) if (item.ID <= 0)
return; continue;
await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false); await ((IMultiplayerClient)this).PlaylistItemChanged(item).ConfigureAwait(false);
} }