mirror of
https://github.com/ppy/osu
synced 2025-03-31 07:38:21 +00:00
Merge pull request #17767 from peppy/multiplayer-gameplay-leaderboard-test-improvements
Refactor multiplayer gameplay leaderboard tests to remove reliance on `Test` implementations
This commit is contained in:
commit
94e2a90af2
@ -0,0 +1,204 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public abstract class MultiplayerGameplayLeaderboardTestScene : OsuTestScene
|
||||
{
|
||||
protected readonly BindableList<MultiplayerRoomUser> MultiplayerUsers = new BindableList<MultiplayerRoomUser>();
|
||||
|
||||
protected MultiplayerGameplayLeaderboard Leaderboard { get; private set; }
|
||||
|
||||
protected virtual MultiplayerRoomUser CreateUser(int userId) => new MultiplayerRoomUser(userId);
|
||||
|
||||
protected abstract MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor);
|
||||
|
||||
private readonly BindableList<int> multiplayerUserIds = new BindableList<int>();
|
||||
|
||||
private OsuConfigManager config;
|
||||
|
||||
private readonly Mock<SpectatorClient> spectatorClient = new Mock<SpectatorClient>();
|
||||
private readonly Mock<MultiplayerClient> multiplayerClient = new Mock<MultiplayerClient>();
|
||||
|
||||
private readonly Dictionary<int, FrameHeader> lastHeaders = new Dictionary<int, FrameHeader>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
|
||||
Dependencies.CacheAs(spectatorClient.Object);
|
||||
Dependencies.CacheAs(multiplayerClient.Object);
|
||||
|
||||
// To emulate `MultiplayerClient.CurrentMatchPlayingUserIds` we need a bindable list of *only IDs*.
|
||||
// This tracks the list of users 1:1.
|
||||
MultiplayerUsers.BindCollectionChanged((c, e) =>
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
Debug.Assert(e.NewItems != null);
|
||||
|
||||
foreach (var user in e.NewItems.OfType<MultiplayerRoomUser>())
|
||||
multiplayerUserIds.Add(user.UserID);
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
Debug.Assert(e.OldItems != null);
|
||||
|
||||
foreach (var user in e.OldItems.OfType<MultiplayerRoomUser>())
|
||||
multiplayerUserIds.Remove(user.UserID);
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
multiplayerUserIds.Clear();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
multiplayerClient.SetupGet(c => c.CurrentMatchPlayingUserIds)
|
||||
.Returns(() => multiplayerUserIds);
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public virtual void SetUpSteps()
|
||||
{
|
||||
AddStep("reset counts", () => spectatorClient.Invocations.Clear());
|
||||
|
||||
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = new APIUser
|
||||
{
|
||||
Id = 1,
|
||||
});
|
||||
|
||||
AddStep("populate users", () =>
|
||||
{
|
||||
MultiplayerUsers.Clear();
|
||||
for (int i = 0; i < 16; i++)
|
||||
MultiplayerUsers.Add(CreateUser(i));
|
||||
});
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
{
|
||||
Leaderboard?.Expire();
|
||||
|
||||
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
||||
var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
OsuScoreProcessor scoreProcessor = new OsuScoreProcessor();
|
||||
scoreProcessor.ApplyBeatmap(playableBeatmap);
|
||||
|
||||
Child = scoreProcessor;
|
||||
|
||||
LoadComponentAsync(Leaderboard = CreateLeaderboard(scoreProcessor), Add);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => Leaderboard.IsLoaded);
|
||||
|
||||
AddStep("check watch requests were sent", () =>
|
||||
{
|
||||
foreach (var user in MultiplayerUsers)
|
||||
spectatorClient.Verify(s => s.WatchUser(user.UserID), Times.Once);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreUpdates()
|
||||
{
|
||||
AddRepeatStep("update state", UpdateUserStatesRandomly, 100);
|
||||
AddToggleStep("switch compact mode", expanded => Leaderboard.Expanded.Value = expanded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserQuit()
|
||||
{
|
||||
AddUntilStep("mark users quit", () =>
|
||||
{
|
||||
if (MultiplayerUsers.Count == 0)
|
||||
return true;
|
||||
|
||||
MultiplayerUsers.RemoveAt(0);
|
||||
return false;
|
||||
});
|
||||
|
||||
AddStep("check stop watching requests were sent", () =>
|
||||
{
|
||||
foreach (var user in MultiplayerUsers)
|
||||
spectatorClient.Verify(s => s.StopWatchingUser(user.UserID), Times.Once);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangeScoringMode()
|
||||
{
|
||||
AddRepeatStep("update state", UpdateUserStatesRandomly, 5);
|
||||
AddStep("change to classic", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Classic));
|
||||
AddStep("change to standardised", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised));
|
||||
}
|
||||
|
||||
protected void UpdateUserStatesRandomly()
|
||||
{
|
||||
foreach (var user in MultiplayerUsers)
|
||||
{
|
||||
if (RNG.NextBool())
|
||||
continue;
|
||||
|
||||
int userId = user.UserID;
|
||||
|
||||
if (!lastHeaders.TryGetValue(userId, out var header))
|
||||
{
|
||||
lastHeaders[userId] = header = new FrameHeader(new ScoreInfo
|
||||
{
|
||||
Statistics = new Dictionary<HitResult, int>
|
||||
{
|
||||
[HitResult.Miss] = 0,
|
||||
[HitResult.Meh] = 0,
|
||||
[HitResult.Great] = 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
switch (RNG.Next(0, 3))
|
||||
{
|
||||
case 0:
|
||||
header.Combo = 0;
|
||||
header.Statistics[HitResult.Miss]++;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
header.Combo++;
|
||||
header.MaxCombo = Math.Max(header.MaxCombo, header.Combo);
|
||||
header.Statistics[HitResult.Meh]++;
|
||||
break;
|
||||
|
||||
default:
|
||||
header.Combo++;
|
||||
header.MaxCombo = Math.Max(header.MaxCombo, header.Combo);
|
||||
header.Statistics[HitResult.Great]++;
|
||||
break;
|
||||
}
|
||||
|
||||
spectatorClient.Raise(s => s.OnNewFrames -= null, userId, new FrameDataBundle(header, new[] { new LegacyReplayFrame(Time.Current, 0, 0, ReplayButtonState.None) }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,161 +1,22 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerTestScene
|
||||
public class TestSceneMultiplayerGameplayLeaderboard : MultiplayerGameplayLeaderboardTestScene
|
||||
{
|
||||
private static IEnumerable<int> users => Enumerable.Range(0, 16);
|
||||
|
||||
public new TestMultiplayerSpectatorClient SpectatorClient => (TestMultiplayerSpectatorClient)OnlinePlayDependencies?.SpectatorClient;
|
||||
|
||||
private MultiplayerGameplayLeaderboard leaderboard;
|
||||
private OsuConfigManager config;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
protected override MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor)
|
||||
{
|
||||
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = UserLookupCache.GetUserAsync(1).GetResultSafely());
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
return new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, MultiplayerUsers.ToArray())
|
||||
{
|
||||
leaderboard?.Expire();
|
||||
|
||||
OsuScoreProcessor scoreProcessor;
|
||||
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
||||
|
||||
var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
var multiplayerUsers = new List<MultiplayerRoomUser>();
|
||||
|
||||
foreach (int user in users)
|
||||
{
|
||||
SpectatorClient.SendStartPlay(user, Beatmap.Value.BeatmapInfo.OnlineID);
|
||||
multiplayerUsers.Add(OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = user }, true));
|
||||
}
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
scoreProcessor = new OsuScoreProcessor(),
|
||||
};
|
||||
|
||||
scoreProcessor.ApplyBeatmap(playableBeatmap);
|
||||
|
||||
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, multiplayerUsers.ToArray())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}, Add);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => leaderboard.IsLoaded);
|
||||
AddUntilStep("wait for user population", () => MultiplayerClient.CurrentMatchPlayingUserIds.Count > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreUpdates()
|
||||
{
|
||||
AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 100);
|
||||
AddToggleStep("switch compact mode", expanded => leaderboard.Expanded.Value = expanded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserQuit()
|
||||
{
|
||||
foreach (int user in users)
|
||||
AddStep($"mark user {user} quit", () => MultiplayerClient.RemoveUser(UserLookupCache.GetUserAsync(user).GetResultSafely().AsNonNull()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangeScoringMode()
|
||||
{
|
||||
AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 5);
|
||||
AddStep("change to classic", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Classic));
|
||||
AddStep("change to standardised", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised));
|
||||
}
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
|
||||
protected class TestDependencies : MultiplayerTestSceneDependencies
|
||||
{
|
||||
protected override TestSpectatorClient CreateSpectatorClient() => new TestMultiplayerSpectatorClient();
|
||||
}
|
||||
|
||||
public class TestMultiplayerSpectatorClient : TestSpectatorClient
|
||||
{
|
||||
private readonly Dictionary<int, FrameHeader> lastHeaders = new Dictionary<int, FrameHeader>();
|
||||
|
||||
public void RandomlyUpdateState()
|
||||
{
|
||||
foreach ((int userId, _) in WatchedUserStates)
|
||||
{
|
||||
if (RNG.NextBool())
|
||||
continue;
|
||||
|
||||
if (!lastHeaders.TryGetValue(userId, out var header))
|
||||
{
|
||||
lastHeaders[userId] = header = new FrameHeader(new ScoreInfo
|
||||
{
|
||||
Statistics = new Dictionary<HitResult, int>
|
||||
{
|
||||
[HitResult.Miss] = 0,
|
||||
[HitResult.Meh] = 0,
|
||||
[HitResult.Great] = 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
switch (RNG.Next(0, 3))
|
||||
{
|
||||
case 0:
|
||||
header.Combo = 0;
|
||||
header.Statistics[HitResult.Miss]++;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
header.Combo++;
|
||||
header.MaxCombo = Math.Max(header.MaxCombo, header.Combo);
|
||||
header.Statistics[HitResult.Meh]++;
|
||||
break;
|
||||
|
||||
default:
|
||||
header.Combo++;
|
||||
header.MaxCombo = Math.Max(header.MaxCombo, header.Combo);
|
||||
header.Statistics[HitResult.Great]++;
|
||||
break;
|
||||
}
|
||||
|
||||
((ISpectatorClient)this).UserSentFrames(userId, new FrameDataBundle(header, new[] { new LegacyReplayFrame(Time.Current, 0, 0, ReplayButtonState.None) }));
|
||||
}
|
||||
}
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,121 +1,56 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerTestScene
|
||||
public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerGameplayLeaderboardTestScene
|
||||
{
|
||||
private static IEnumerable<int> users => Enumerable.Range(0, 16);
|
||||
|
||||
public new TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient SpectatorClient =>
|
||||
(TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient)OnlinePlayDependencies?.SpectatorClient;
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
|
||||
protected class TestDependencies : MultiplayerTestSceneDependencies
|
||||
protected override MultiplayerRoomUser CreateUser(int userId)
|
||||
{
|
||||
protected override TestSpectatorClient CreateSpectatorClient() => new TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient();
|
||||
var user = base.CreateUser(userId);
|
||||
user.MatchState = new TeamVersusUserState
|
||||
{
|
||||
TeamID = RNG.Next(0, 2)
|
||||
};
|
||||
return user;
|
||||
}
|
||||
|
||||
private MultiplayerGameplayLeaderboard leaderboard;
|
||||
private GameplayMatchScoreDisplay gameplayScoreDisplay;
|
||||
|
||||
protected override Room CreateRoom()
|
||||
{
|
||||
var room = base.CreateRoom();
|
||||
room.Type.Value = MatchType.TeamVersus;
|
||||
return room;
|
||||
}
|
||||
protected override MultiplayerGameplayLeaderboard CreateLeaderboard(OsuScoreProcessor scoreProcessor) =>
|
||||
new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, MultiplayerUsers.ToArray())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = UserLookupCache.GetUserAsync(1).GetResultSafely());
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
AddStep("Add external display components", () =>
|
||||
{
|
||||
leaderboard?.Expire();
|
||||
|
||||
OsuScoreProcessor scoreProcessor;
|
||||
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
||||
|
||||
var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
var multiplayerUsers = new List<MultiplayerRoomUser>();
|
||||
|
||||
foreach (int user in users)
|
||||
LoadComponentAsync(new MatchScoreDisplay
|
||||
{
|
||||
SpectatorClient.SendStartPlay(user, Beatmap.Value.BeatmapInfo.OnlineID);
|
||||
var roomUser = OnlinePlayDependencies.MultiplayerClient.AddUser(new APIUser { Id = user }, true);
|
||||
Team1Score = { BindTarget = Leaderboard.TeamScores[0] },
|
||||
Team2Score = { BindTarget = Leaderboard.TeamScores[1] }
|
||||
}, Add);
|
||||
|
||||
roomUser.MatchState = new TeamVersusUserState
|
||||
{
|
||||
TeamID = RNG.Next(0, 2)
|
||||
};
|
||||
|
||||
multiplayerUsers.Add(roomUser);
|
||||
}
|
||||
|
||||
Children = new Drawable[]
|
||||
LoadComponentAsync(new GameplayMatchScoreDisplay
|
||||
{
|
||||
scoreProcessor = new OsuScoreProcessor(),
|
||||
};
|
||||
|
||||
scoreProcessor.ApplyBeatmap(playableBeatmap);
|
||||
|
||||
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(Ruleset.Value, scoreProcessor, multiplayerUsers.ToArray())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}, gameplayLeaderboard =>
|
||||
{
|
||||
LoadComponentAsync(new MatchScoreDisplay
|
||||
{
|
||||
Team1Score = { BindTarget = leaderboard.TeamScores[0] },
|
||||
Team2Score = { BindTarget = leaderboard.TeamScores[1] }
|
||||
}, Add);
|
||||
|
||||
LoadComponentAsync(gameplayScoreDisplay = new GameplayMatchScoreDisplay
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Team1Score = { BindTarget = leaderboard.TeamScores[0] },
|
||||
Team2Score = { BindTarget = leaderboard.TeamScores[1] }
|
||||
}, Add);
|
||||
|
||||
Add(gameplayLeaderboard);
|
||||
});
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => leaderboard.IsLoaded);
|
||||
AddUntilStep("wait for user population", () => MultiplayerClient.CurrentMatchPlayingUserIds.Count > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreUpdates()
|
||||
{
|
||||
AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 100);
|
||||
AddToggleStep("switch compact mode", expanded =>
|
||||
{
|
||||
leaderboard.Expanded.Value = expanded;
|
||||
gameplayScoreDisplay.Expanded.Value = expanded;
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Team1Score = { BindTarget = Leaderboard.TeamScores[0] },
|
||||
Team2Score = { BindTarget = Leaderboard.TeamScores[1] },
|
||||
Expanded = { BindTarget = Leaderboard.Expanded },
|
||||
}, Add);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
/// <summary>
|
||||
/// Invoked when a user leaves the room of their own accord.
|
||||
/// </summary>
|
||||
public event Action<MultiplayerRoomUser>? UserLeft;
|
||||
public virtual event Action<MultiplayerRoomUser>? UserLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a user was kicked from the room forcefully.
|
||||
@ -107,7 +107,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
/// <summary>
|
||||
/// The users in the joined <see cref="Room"/> which are participating in the current gameplay loop.
|
||||
/// </summary>
|
||||
public IBindableList<int> CurrentMatchPlayingUserIds => PlayingUserIds;
|
||||
public virtual IBindableList<int> CurrentMatchPlayingUserIds => PlayingUserIds;
|
||||
|
||||
protected readonly BindableList<int> PlayingUserIds = new BindableList<int>();
|
||||
|
||||
|
@ -54,17 +54,17 @@ namespace osu.Game.Online.Spectator
|
||||
/// <summary>
|
||||
/// Called whenever new frames arrive from the server.
|
||||
/// </summary>
|
||||
public event Action<int, FrameDataBundle>? OnNewFrames;
|
||||
public virtual event Action<int, FrameDataBundle>? OnNewFrames;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever a user starts a play session, or immediately if the user is being watched and currently in a play session.
|
||||
/// </summary>
|
||||
public event Action<int, SpectatorState>? OnUserBeganPlaying;
|
||||
public virtual event Action<int, SpectatorState>? OnUserBeganPlaying;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever a user finishes a play session.
|
||||
/// </summary>
|
||||
public event Action<int, SpectatorState>? OnUserFinishedPlaying;
|
||||
public virtual event Action<int, SpectatorState>? OnUserFinishedPlaying;
|
||||
|
||||
/// <summary>
|
||||
/// All users currently being watched.
|
||||
@ -221,7 +221,7 @@ namespace osu.Game.Online.Spectator
|
||||
});
|
||||
}
|
||||
|
||||
public void WatchUser(int userId)
|
||||
public virtual void WatchUser(int userId)
|
||||
{
|
||||
Debug.Assert(ThreadSafety.IsUpdateThread);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user