Rewrite rooms to store multiple active countdowns

Update test to the new structure
This commit is contained in:
Dan Balasescu
2022-09-01 18:53:35 +09:00
parent a4dc3fe412
commit 2923c10cd8
13 changed files with 117 additions and 54 deletions

View File

@ -91,8 +91,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
break; break;
case StopCountdownRequest: case StopCountdownRequest:
multiplayerRoom.Countdown = null; clearRoomCountdown();
raiseRoomUpdated();
break; break;
} }
}); });
@ -244,14 +243,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
}); });
AddStep("start countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely()); AddStep("start countdown", () => multiplayerClient.Object.SendMatchRequest(new StartMatchCountdownRequest { Duration = TimeSpan.FromMinutes(1) }).WaitSafely());
AddUntilStep("countdown started", () => multiplayerRoom.Countdown != null); AddUntilStep("countdown started", () => multiplayerRoom.ActiveCountdowns.Any());
AddStep("transfer host to local user", () => transferHost(localUser)); AddStep("transfer host to local user", () => transferHost(localUser));
AddUntilStep("local user is host", () => multiplayerRoom.Host?.Equals(multiplayerClient.Object.LocalUser) == true); AddUntilStep("local user is host", () => multiplayerRoom.Host?.Equals(multiplayerClient.Object.LocalUser) == true);
ClickButtonWhenEnabled<MultiplayerReadyButton>(); ClickButtonWhenEnabled<MultiplayerReadyButton>();
checkLocalUserState(MultiplayerUserState.Ready); checkLocalUserState(MultiplayerUserState.Ready);
AddAssert("countdown still active", () => multiplayerRoom.Countdown != null); AddAssert("countdown still active", () => multiplayerRoom.ActiveCountdowns.Any());
} }
[Test] [Test]
@ -392,7 +391,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void setRoomCountdown(TimeSpan duration) private void setRoomCountdown(TimeSpan duration)
{ {
multiplayerRoom.Countdown = new MatchStartCountdown { TimeRemaining = duration }; multiplayerRoom.ActiveCountdowns.Add(new MatchStartCountdown { TimeRemaining = duration });
raiseRoomUpdated();
}
private void clearRoomCountdown()
{
multiplayerRoom.ActiveCountdowns.Clear();
raiseRoomUpdated(); raiseRoomUpdated();
} }

View File

@ -1,20 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using MessagePack;
namespace osu.Game.Online.Multiplayer.Countdown
{
/// <summary>
/// Indicates a change to the <see cref="MultiplayerRoom"/>'s countdown.
/// </summary>
[MessagePackObject]
public class CountdownChangedEvent : MatchServerEvent
{
/// <summary>
/// The new countdown.
/// </summary>
[Key(0)]
public MultiplayerCountdown? Countdown { get; set; }
}
}

View File

@ -0,0 +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.
using MessagePack;
using Newtonsoft.Json;
namespace osu.Game.Online.Multiplayer.Countdown
{
/// <summary>
/// Indicates that a countdown started in the <see cref="MultiplayerRoom"/>.
/// </summary>
[MessagePackObject]
public class CountdownStartedEvent : MatchServerEvent
{
/// <summary>
/// The countdown that was started.
/// </summary>
[Key(0)]
public readonly MultiplayerCountdown Countdown;
[JsonConstructor]
[SerializationConstructor]
public CountdownStartedEvent(MultiplayerCountdown countdown)
{
Countdown = countdown;
}
}
}

View File

@ -0,0 +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.
using MessagePack;
using Newtonsoft.Json;
namespace osu.Game.Online.Multiplayer.Countdown
{
/// <summary>
/// Indicates that a countdown was stopped in the <see cref="MultiplayerRoom"/>.
/// </summary>
[MessagePackObject]
public class CountdownStoppedEvent : MatchServerEvent
{
/// <summary>
/// The identifier of the countdown that was stopped.
/// </summary>
[Key(0)]
public readonly int ID;
[JsonConstructor]
[SerializationConstructor]
public CountdownStoppedEvent(int id)
{
ID = id;
}
}
}

View File

@ -2,6 +2,7 @@
// 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 MessagePack; using MessagePack;
using Newtonsoft.Json;
namespace osu.Game.Online.Multiplayer.Countdown namespace osu.Game.Online.Multiplayer.Countdown
{ {
@ -11,5 +12,14 @@ namespace osu.Game.Online.Multiplayer.Countdown
[MessagePackObject] [MessagePackObject]
public class StopCountdownRequest : MatchUserRequest public class StopCountdownRequest : MatchUserRequest
{ {
[Key(0)]
public readonly int ID;
[JsonConstructor]
[SerializationConstructor]
public StopCountdownRequest(int id)
{
ID = id;
}
} }
} }

View File

@ -13,7 +13,8 @@ namespace osu.Game.Online.Multiplayer
[Serializable] [Serializable]
[MessagePackObject] [MessagePackObject]
// IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types. // IMPORTANT: Add rules to SignalRUnionWorkaroundResolver for new derived types.
[Union(0, typeof(CountdownChangedEvent))] [Union(0, typeof(CountdownStartedEvent))]
[Union(1, typeof(CountdownStoppedEvent))]
public abstract class MatchServerEvent public abstract class MatchServerEvent
{ {
} }

View File

@ -550,8 +550,14 @@ namespace osu.Game.Online.Multiplayer
switch (e) switch (e)
{ {
case CountdownChangedEvent countdownChangedEvent: case CountdownStartedEvent countdownStartedEvent:
Room.Countdown = countdownChangedEvent.Countdown; Room.ActiveCountdowns.Add(countdownStartedEvent.Countdown);
break;
case CountdownStoppedEvent countdownStoppedEvent:
MultiplayerCountdown? countdown = Room.ActiveCountdowns.FirstOrDefault(countdown => countdown.ID == countdownStoppedEvent.ID);
if (countdown != null)
Room.ActiveCountdowns.Remove(countdown);
break; break;
} }

View File

@ -15,13 +15,19 @@ namespace osu.Game.Online.Multiplayer
[Union(1, typeof(ForceGameplayStartCountdown))] [Union(1, typeof(ForceGameplayStartCountdown))]
public abstract class MultiplayerCountdown public abstract class MultiplayerCountdown
{ {
/// <summary>
/// A unique identifier for this countdown.
/// </summary>
[Key(0)]
public int ID { get; set; }
/// <summary> /// <summary>
/// The amount of time remaining in the countdown. /// The amount of time remaining in the countdown.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is only sent once from the server upon initial retrieval of the <see cref="MultiplayerRoom"/> or via a <see cref="CountdownChangedEvent"/>. /// This is only sent once from the server upon initial retrieval of the <see cref="MultiplayerRoom"/> or via a <see cref="CountdownStartedEvent"/>.
/// </remarks> /// </remarks>
[Key(0)] [Key(1)]
public TimeSpan TimeRemaining { get; set; } public TimeSpan TimeRemaining { get; set; }
} }
} }

View File

@ -53,10 +53,10 @@ namespace osu.Game.Online.Multiplayer
public IList<MultiplayerPlaylistItem> Playlist { get; set; } = new List<MultiplayerPlaylistItem>(); public IList<MultiplayerPlaylistItem> Playlist { get; set; } = new List<MultiplayerPlaylistItem>();
/// <summary> /// <summary>
/// The currently-running countdown. /// The currently running countdowns.
/// </summary> /// </summary>
[Key(7)] [Key(7)]
public MultiplayerCountdown? Countdown { get; set; } public IList<MultiplayerCountdown> ActiveCountdowns { get; set; } = new List<MultiplayerCountdown>();
[JsonConstructor] [JsonConstructor]
[SerializationConstructor] [SerializationConstructor]

View File

@ -23,7 +23,8 @@ namespace osu.Game.Online
(typeof(ChangeTeamRequest), typeof(MatchUserRequest)), (typeof(ChangeTeamRequest), typeof(MatchUserRequest)),
(typeof(StartMatchCountdownRequest), typeof(MatchUserRequest)), (typeof(StartMatchCountdownRequest), typeof(MatchUserRequest)),
(typeof(StopCountdownRequest), typeof(MatchUserRequest)), (typeof(StopCountdownRequest), typeof(MatchUserRequest)),
(typeof(CountdownChangedEvent), typeof(MatchServerEvent)), (typeof(CountdownStartedEvent), typeof(MatchServerEvent)),
(typeof(CountdownStoppedEvent), typeof(MatchServerEvent)),
(typeof(TeamVersusRoomState), typeof(MatchRoomState)), (typeof(TeamVersusRoomState), typeof(MatchRoomState)),
(typeof(TeamVersusUserState), typeof(MatchUserState)), (typeof(TeamVersusUserState), typeof(MatchUserState)),
(typeof(MatchStartCountdown), typeof(MultiplayerCountdown)), (typeof(MatchStartCountdown), typeof(MultiplayerCountdown)),

View File

@ -109,7 +109,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
Debug.Assert(clickOperation == null); Debug.Assert(clickOperation == null);
clickOperation = ongoingOperationTracker.BeginOperation(); clickOperation = ongoingOperationTracker.BeginOperation();
if (isReady() && Client.IsHost && Room.Countdown == null) if (isReady() && Client.IsHost && !Room.ActiveCountdowns.Any(c => c is MatchStartCountdown))
startMatch(); startMatch();
else else
toggleReady(); toggleReady();
@ -140,10 +140,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
private void cancelCountdown() private void cancelCountdown()
{ {
if (Client.Room == null)
return;
Debug.Assert(clickOperation == null); Debug.Assert(clickOperation == null);
clickOperation = ongoingOperationTracker.BeginOperation(); clickOperation = ongoingOperationTracker.BeginOperation();
Client.SendMatchRequest(new StopCountdownRequest()).ContinueWith(_ => endOperation()); MultiplayerCountdown countdown = Client.Room.ActiveCountdowns.Single(c => c is MatchStartCountdown);
Client.SendMatchRequest(new StopCountdownRequest(countdown.ID)).ContinueWith(_ => endOperation());
} }
private void endOperation() private void endOperation()
@ -192,7 +196,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
// When the local user is the host and spectating the match, the ready button should be enabled only if any users are ready. // When the local user is the host and spectating the match, the ready button should be enabled only if any users are ready.
if (localUser?.State == MultiplayerUserState.Spectating) if (localUser?.State == MultiplayerUserState.Spectating)
readyButton.Enabled.Value &= Client.IsHost && newCountReady > 0 && Room.Countdown == null; readyButton.Enabled.Value &= Client.IsHost && newCountReady > 0 && !Room.ActiveCountdowns.Any(c => c is MatchStartCountdown);
if (newCountReady == countReady) if (newCountReady == countReady)
return; return;

View File

@ -4,6 +4,7 @@
#nullable disable #nullable disable
using System; using System;
using System.Linq;
using Humanizer; using Humanizer;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions; using osu.Framework.Extensions;
@ -79,7 +80,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
private void onRoomUpdated() => Scheduler.AddOnce(() => private void onRoomUpdated() => Scheduler.AddOnce(() =>
{ {
bool countdownActive = multiplayerClient.Room?.Countdown is MatchStartCountdown; bool countdownActive = multiplayerClient.Room?.ActiveCountdowns.Any(c => c is MatchStartCountdown) == true;
if (countdownActive) if (countdownActive)
{ {
@ -121,7 +122,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
}); });
} }
if (multiplayerClient.Room?.Countdown != null && multiplayerClient.IsHost) if (multiplayerClient.Room?.ActiveCountdowns.Any(c => c is MatchStartCountdown) == true && multiplayerClient.IsHost)
{ {
flow.Add(new OsuButton flow.Add(new OsuButton
{ {

View File

@ -57,23 +57,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
private void onRoomUpdated() => Scheduler.AddOnce(() => private void onRoomUpdated() => Scheduler.AddOnce(() =>
{ {
MultiplayerCountdown newCountdown; MultiplayerCountdown newCountdown = room?.ActiveCountdowns.SingleOrDefault(c => c is MatchStartCountdown);
switch (room?.Countdown)
{
case MatchStartCountdown:
newCountdown = room.Countdown;
break;
// Clear the countdown with any other (including non-null) countdown values.
default:
newCountdown = null;
break;
}
if (newCountdown != countdown) if (newCountdown != countdown)
{ {
countdown = room?.Countdown; countdown = newCountdown;
countdownChangeTime = Time.Current; countdownChangeTime = Time.Current;
} }
@ -213,7 +201,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
case MultiplayerUserState.Spectating: case MultiplayerUserState.Spectating:
case MultiplayerUserState.Ready: case MultiplayerUserState.Ready:
if (room?.Host?.Equals(localUser) == true && room.Countdown == null) if (room?.Host?.Equals(localUser) == true && !room.ActiveCountdowns.Any(c => c is MatchStartCountdown))
setGreen(); setGreen();
else else
setYellow(); setYellow();
@ -248,8 +236,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
{ {
get get
{ {
if (room?.Countdown != null && multiplayerClient.IsHost && multiplayerClient.LocalUser?.State == MultiplayerUserState.Ready && !room.Settings.AutoStartEnabled) if (room?.ActiveCountdowns.Any(c => c is MatchStartCountdown) == true
&& multiplayerClient.IsHost
&& multiplayerClient.LocalUser?.State == MultiplayerUserState.Ready
&& !room.Settings.AutoStartEnabled)
{
return "Cancel countdown"; return "Cancel countdown";
}
return base.TooltipText; return base.TooltipText;
} }