2021-04-09 17:16:10 +08:00
// 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 System.Threading ;
using System.Threading.Tasks ;
using NUnit.Framework ;
using osu.Framework.Allocation ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Testing ;
using osu.Framework.Timing ;
using osu.Game.Database ;
using osu.Game.Online.Spectator ;
using osu.Game.Rulesets.Osu.Scoring ;
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate ;
using osu.Game.Screens.Play.HUD ;
2021-05-12 11:17:42 +08:00
using osu.Game.Tests.Visual.Spectator ;
2021-04-09 17:16:10 +08:00
using osu.Game.Users ;
namespace osu.Game.Tests.Visual.Multiplayer
{
2021-04-26 15:48:32 +08:00
public class TestSceneMultiSpectatorLeaderboard : MultiplayerTestScene
2021-04-09 17:16:10 +08:00
{
2021-05-20 14:55:07 +08:00
[Cached(typeof(SpectatorClient))]
private TestSpectatorClient spectatorClient = new TestSpectatorClient ( ) ;
2021-04-09 17:16:10 +08:00
[Cached(typeof(UserLookupCache))]
private UserLookupCache lookupCache = new TestUserLookupCache ( ) ;
protected override Container < Drawable > Content = > content ;
private readonly Container content ;
private readonly Dictionary < int , ManualClock > clocks = new Dictionary < int , ManualClock >
{
2021-04-26 16:22:16 +08:00
{ PLAYER_1_ID , new ManualClock ( ) } ,
{ PLAYER_2_ID , new ManualClock ( ) }
2021-04-09 17:16:10 +08:00
} ;
2021-04-26 15:48:32 +08:00
public TestSceneMultiSpectatorLeaderboard ( )
2021-04-09 17:16:10 +08:00
{
base . Content . AddRange ( new Drawable [ ]
{
2021-05-20 14:55:07 +08:00
spectatorClient ,
2021-04-09 17:16:10 +08:00
lookupCache ,
content = new Container { RelativeSizeAxes = Axes . Both }
} ) ;
}
[SetUpSteps]
public new void SetUpSteps ( )
{
2021-04-22 22:39:02 +08:00
MultiSpectatorLeaderboard leaderboard = null ;
2021-04-09 17:16:10 +08:00
AddStep ( "reset" , ( ) = >
{
Clear ( ) ;
foreach ( var ( userId , clock ) in clocks )
{
2021-05-20 16:41:46 +08:00
spectatorClient . EndPlay ( userId ) ;
2021-04-09 17:16:10 +08:00
clock . CurrentTime = 0 ;
}
} ) ;
AddStep ( "create leaderboard" , ( ) = >
{
foreach ( var ( userId , _ ) in clocks )
2021-05-20 14:55:07 +08:00
spectatorClient . StartPlay ( userId , 0 ) ;
2021-04-09 17:16:10 +08:00
Beatmap . Value = CreateWorkingBeatmap ( Ruleset . Value ) ;
var playable = Beatmap . Value . GetPlayableBeatmap ( Ruleset . Value ) ;
var scoreProcessor = new OsuScoreProcessor ( ) ;
scoreProcessor . ApplyBeatmap ( playable ) ;
2021-04-22 22:39:02 +08:00
LoadComponentAsync ( leaderboard = new MultiSpectatorLeaderboard ( scoreProcessor , clocks . Keys . ToArray ( ) ) { Expanded = { Value = true } } , Add ) ;
2021-04-09 17:16:10 +08:00
} ) ;
AddUntilStep ( "wait for load" , ( ) = > leaderboard . IsLoaded ) ;
AddStep ( "add clock sources" , ( ) = >
{
foreach ( var ( userId , clock ) in clocks )
leaderboard . AddClock ( userId , clock ) ;
} ) ;
}
[Test]
public void TestLeaderboardTracksCurrentTime ( )
{
AddStep ( "send frames" , ( ) = >
{
2021-04-26 16:22:16 +08:00
// For player 1, send frames in sets of 1.
// For player 2, send frames in sets of 10.
2021-04-09 17:16:10 +08:00
for ( int i = 0 ; i < 100 ; i + + )
{
2021-05-20 14:55:07 +08:00
spectatorClient . SendFrames ( PLAYER_1_ID , i , 1 ) ;
2021-04-09 17:16:10 +08:00
if ( i % 10 = = 0 )
2021-05-20 14:55:07 +08:00
spectatorClient . SendFrames ( PLAYER_2_ID , i , 10 ) ;
2021-04-09 17:16:10 +08:00
}
} ) ;
2021-04-26 16:22:16 +08:00
assertCombo ( PLAYER_1_ID , 1 ) ;
assertCombo ( PLAYER_2_ID , 10 ) ;
2021-04-09 17:16:10 +08:00
2021-04-26 16:22:16 +08:00
// Advance to a point where only user player 1's frame changes.
2021-04-09 17:16:10 +08:00
setTime ( 500 ) ;
2021-04-26 16:22:16 +08:00
assertCombo ( PLAYER_1_ID , 5 ) ;
assertCombo ( PLAYER_2_ID , 10 ) ;
2021-04-09 17:16:10 +08:00
2021-04-09 17:23:41 +08:00
// Advance to a point where both user's frame changes.
2021-04-09 17:16:10 +08:00
setTime ( 1100 ) ;
2021-04-26 16:22:16 +08:00
assertCombo ( PLAYER_1_ID , 11 ) ;
assertCombo ( PLAYER_2_ID , 20 ) ;
2021-04-09 17:23:41 +08:00
2021-04-26 16:22:16 +08:00
// Advance user player 2 only to a point where its frame changes.
setTime ( PLAYER_2_ID , 2100 ) ;
assertCombo ( PLAYER_1_ID , 11 ) ;
assertCombo ( PLAYER_2_ID , 30 ) ;
2021-04-09 17:23:41 +08:00
// Advance both users beyond their last frame
setTime ( 101 * 100 ) ;
2021-04-26 16:22:16 +08:00
assertCombo ( PLAYER_1_ID , 100 ) ;
assertCombo ( PLAYER_2_ID , 100 ) ;
2021-04-09 17:16:10 +08:00
}
2021-04-09 17:18:23 +08:00
[Test]
public void TestNoFrames ( )
{
2021-04-26 16:22:16 +08:00
assertCombo ( PLAYER_1_ID , 0 ) ;
assertCombo ( PLAYER_2_ID , 0 ) ;
2021-04-09 17:18:23 +08:00
}
2021-04-09 17:16:10 +08:00
private void setTime ( double time ) = > AddStep ( $"set time {time}" , ( ) = >
{
foreach ( var ( _ , clock ) in clocks )
clock . CurrentTime = time ;
} ) ;
2021-04-09 17:23:41 +08:00
private void setTime ( int userId , double time )
= > AddStep ( $"set user {userId} time {time}" , ( ) = > clocks [ userId ] . CurrentTime = time ) ;
2021-04-09 17:16:10 +08:00
private void assertCombo ( int userId , int expectedCombo )
= > AddUntilStep ( $"player {userId} has {expectedCombo} combo" , ( ) = > this . ChildrenOfType < GameplayLeaderboardScore > ( ) . Single ( s = > s . User ? . Id = = userId ) . Combo . Value = = expectedCombo ) ;
private class TestUserLookupCache : UserLookupCache
{
protected override Task < User > ComputeValueAsync ( int lookup , CancellationToken token = default )
{
return Task . FromResult ( new User
{
Id = lookup ,
Username = $"User {lookup}"
} ) ;
}
}
}
}