mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 09:07:25 +08:00
Merge branch 'master' into async-deadlock-safety
This commit is contained in:
commit
af9fad00c3
@ -51,7 +51,7 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1215.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.109.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.107.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
|
@ -95,7 +95,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.That(decodedAfterEncode, Is.Not.Null);
|
||||
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.User.Username, Is.EqualTo(scoreInfo.User.Username));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.BeatmapInfoID, Is.EqualTo(scoreInfo.BeatmapInfoID));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.Ruleset, Is.EqualTo(scoreInfo.Ruleset));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(scoreInfo.TotalScore));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.MaxCombo, Is.EqualTo(scoreInfo.MaxCombo));
|
||||
|
@ -16,7 +16,8 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
{
|
||||
private BeatmapInfo getExampleBeatmap() => new BeatmapInfo
|
||||
{
|
||||
Ruleset = new RulesetInfo { OnlineID = 5 },
|
||||
Ruleset = new RulesetInfo { OnlineID = 0 },
|
||||
RulesetID = 0,
|
||||
StarRating = 4.0d,
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestNoSubmissionOnResultsWithNoToken()
|
||||
{
|
||||
prepareTokenResponse(false);
|
||||
prepareTestAPI(false);
|
||||
|
||||
createPlayerTest();
|
||||
|
||||
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestSubmissionOnResults()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest();
|
||||
|
||||
@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestSubmissionForDifferentRuleset()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest(createRuleset: () => new TaikoRuleset());
|
||||
|
||||
@ -116,7 +116,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestSubmissionForConvertedBeatmap()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest(createRuleset: () => new ManiaRuleset(), createBeatmap: _ => createTestBeatmap(new OsuRuleset().RulesetInfo));
|
||||
|
||||
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestNoSubmissionOnExitWithNoToken()
|
||||
{
|
||||
prepareTokenResponse(false);
|
||||
prepareTestAPI(false);
|
||||
|
||||
createPlayerTest();
|
||||
|
||||
@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestNoSubmissionOnEmptyFail()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest(true);
|
||||
|
||||
@ -168,7 +168,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestSubmissionOnFail()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest(true);
|
||||
|
||||
@ -185,7 +185,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestNoSubmissionOnEmptyExit()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest();
|
||||
|
||||
@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestSubmissionOnExit()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest();
|
||||
|
||||
@ -213,7 +213,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestSubmissionOnExitDuringImport()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest();
|
||||
AddStep("block imports", () => Player.AllowImportCompletion.Wait());
|
||||
@ -226,13 +226,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("exit", () => Player.Exit());
|
||||
AddStep("allow import to proceed", () => Player.AllowImportCompletion.Release(1));
|
||||
AddAssert("ensure submission", () => Player.SubmittedScore != null && Player.ImportedScore != null);
|
||||
AddUntilStep("ensure submission", () => Player.SubmittedScore != null && Player.ImportedScore != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNoSubmissionOnLocalBeatmap()
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest(false, r =>
|
||||
{
|
||||
@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[TestCase(10)]
|
||||
public void TestNoSubmissionOnCustomRuleset(int? rulesetId)
|
||||
{
|
||||
prepareTokenResponse(true);
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest(false, createRuleset: () => new OsuRuleset { RulesetInfo = { OnlineID = rulesetId ?? -1 } });
|
||||
|
||||
@ -275,7 +275,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}));
|
||||
}
|
||||
|
||||
private void prepareTokenResponse(bool validToken)
|
||||
private void prepareTestAPI(bool validToken)
|
||||
{
|
||||
AddStep("Prepare test API", () =>
|
||||
{
|
||||
@ -289,6 +289,31 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
else
|
||||
tokenRequest.TriggerFailure(new APIException("something went wrong!", null));
|
||||
return true;
|
||||
|
||||
case SubmitSoloScoreRequest submissionRequest:
|
||||
if (validToken)
|
||||
{
|
||||
var requestScore = submissionRequest.Score;
|
||||
|
||||
submissionRequest.TriggerSuccess(new MultiplayerScore
|
||||
{
|
||||
ID = 1234,
|
||||
User = dummyAPI.LocalUser.Value,
|
||||
Rank = requestScore.Rank,
|
||||
TotalScore = requestScore.TotalScore,
|
||||
Accuracy = requestScore.Accuracy,
|
||||
MaxCombo = requestScore.MaxCombo,
|
||||
Mods = requestScore.Mods,
|
||||
Statistics = requestScore.Statistics,
|
||||
Passed = requestScore.Passed,
|
||||
EndedAt = DateTimeOffset.Now,
|
||||
Position = 1
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1,13 +1,25 @@
|
||||
// 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 enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
@ -83,16 +95,60 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID);
|
||||
}
|
||||
|
||||
private void addItem(Func<BeatmapInfo> beatmap)
|
||||
[Test]
|
||||
public void TestCorrectRulesetSelectedAfterNewItemAdded()
|
||||
{
|
||||
addItem(() => OtherBeatmap, new CatchRuleset().RulesetInfo);
|
||||
AddUntilStep("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID);
|
||||
|
||||
AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle);
|
||||
ClickButtonWhenEnabled<MultiplayerReadyButton>();
|
||||
|
||||
AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready);
|
||||
ClickButtonWhenEnabled<MultiplayerReadyButton>();
|
||||
|
||||
AddUntilStep("wait for player", () => CurrentScreen is Player player && player.IsLoaded);
|
||||
AddAssert("ruleset is correct", () => ((Player)CurrentScreen).Ruleset.Value.Equals(new OsuRuleset().RulesetInfo));
|
||||
AddStep("exit player", () => CurrentScreen.Exit());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCorrectModsSelectedAfterNewItemAdded()
|
||||
{
|
||||
addItem(() => OtherBeatmap, mods: new Mod[] { new OsuModDoubleTime() });
|
||||
AddUntilStep("selected beatmap is initial beatmap", () => Beatmap.Value.BeatmapInfo.OnlineID == InitialBeatmap.OnlineID);
|
||||
|
||||
AddUntilStep("wait for idle", () => Client.LocalUser?.State == MultiplayerUserState.Idle);
|
||||
ClickButtonWhenEnabled<MultiplayerReadyButton>();
|
||||
|
||||
AddUntilStep("wait for ready", () => Client.LocalUser?.State == MultiplayerUserState.Ready);
|
||||
ClickButtonWhenEnabled<MultiplayerReadyButton>();
|
||||
|
||||
AddUntilStep("wait for player", () => CurrentScreen is Player player && player.IsLoaded);
|
||||
AddAssert("mods are correct", () => !((Player)CurrentScreen).Mods.Value.Any());
|
||||
AddStep("exit player", () => CurrentScreen.Exit());
|
||||
}
|
||||
|
||||
private void addItem(Func<BeatmapInfo> beatmap, RulesetInfo? ruleset = null, IReadOnlyList<Mod>? mods = null)
|
||||
{
|
||||
Screens.Select.SongSelect? songSelect = null;
|
||||
|
||||
AddStep("click add button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSubScreen.AddItemButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
|
||||
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap()));
|
||||
AddUntilStep("wait for song select", () => (songSelect = CurrentSubScreen as Screens.Select.SongSelect) != null);
|
||||
AddUntilStep("wait for loaded", () => songSelect.AsNonNull().BeatmapSetsLoaded);
|
||||
|
||||
if (ruleset != null)
|
||||
AddStep($"set {ruleset.Name} ruleset", () => songSelect.AsNonNull().Ruleset.Value = ruleset);
|
||||
|
||||
if (mods != null)
|
||||
AddStep($"set mods to {string.Join(",", mods.Select(m => m.Acronym))}", () => songSelect.AsNonNull().Mods.Value = mods);
|
||||
|
||||
AddStep("select other beatmap", () => songSelect.AsNonNull().FinaliseSelection(beatmap()));
|
||||
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
@ -23,6 +24,8 @@ using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
@ -439,6 +442,84 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("Beatmap matches current item", () => Beatmap.Value.BeatmapInfo.OnlineID == client.Room?.Playlist.First().BeatmapID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayStartsWithCorrectRulesetWhileAtSongSelect()
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pressReadyButton();
|
||||
|
||||
AddStep("Enter song select", () =>
|
||||
{
|
||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||
|
||||
AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == client.Room?.Playlist.First().RulesetID);
|
||||
|
||||
AddStep("Switch ruleset", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Ruleset.Value = new CatchRuleset().RulesetInfo);
|
||||
|
||||
AddUntilStep("Ruleset doesn't match current item", () => Ruleset.Value.OnlineID != client.Room?.Playlist.First().RulesetID);
|
||||
|
||||
AddStep("start match externally", () => client.StartMatch());
|
||||
|
||||
AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player);
|
||||
|
||||
AddAssert("Ruleset matches current item", () => Ruleset.Value.OnlineID == client.Room?.Playlist.First().RulesetID);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayStartsWithCorrectModsWhileAtSongSelect()
|
||||
{
|
||||
createRoom(() => new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pressReadyButton();
|
||||
|
||||
AddStep("Enter song select", () =>
|
||||
{
|
||||
var currentSubScreen = ((Screens.OnlinePlay.Multiplayer.Multiplayer)multiplayerComponents.CurrentScreen).CurrentSubScreen;
|
||||
((MultiplayerMatchSubScreen)currentSubScreen).OpenSongSelection(client.Room?.Settings.PlaylistItemId);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for song select", () => this.ChildrenOfType<MultiplayerMatchSongSelect>().FirstOrDefault()?.BeatmapSetsLoaded == true);
|
||||
|
||||
AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
|
||||
AddStep("Switch required mods", () => ((MultiplayerMatchSongSelect)multiplayerComponents.MultiplayerScreen.CurrentSubScreen).Mods.Value = new Mod[] { new OsuModDoubleTime() });
|
||||
|
||||
AddAssert("Mods don't match current item", () => !SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
|
||||
AddStep("start match externally", () => client.StartMatch());
|
||||
|
||||
AddUntilStep("play started", () => multiplayerComponents.CurrentScreen is Player);
|
||||
|
||||
AddAssert("Mods match current item", () => SelectedMods.Value.Select(m => m.Acronym).SequenceEqual(client.Room.AsNonNull().Playlist.First().RequiredMods.Select(m => m.Acronym)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap()
|
||||
{
|
||||
|
@ -1,38 +1,27 @@
|
||||
// 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 osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays.Rankings.Tables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Rulesets;
|
||||
using System.Threading;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Rankings;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneRankingsTables : OsuTestScene
|
||||
{
|
||||
protected override bool UseOnlineAPI => true;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||
|
||||
private readonly BasicScrollContainer scrollFlow;
|
||||
private readonly LoadingLayer loading;
|
||||
private CancellationTokenSource cancellationToken;
|
||||
private APIRequest request;
|
||||
|
||||
public TestSceneRankingsTables()
|
||||
{
|
||||
@ -53,73 +42,120 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AddStep("Osu performance", () => createPerformanceTable(new OsuRuleset().RulesetInfo, null));
|
||||
AddStep("Mania scores", () => createScoreTable(new ManiaRuleset().RulesetInfo));
|
||||
AddStep("Taiko country scores", () => createCountryTable(new TaikoRuleset().RulesetInfo));
|
||||
AddStep("Catch US performance page 10", () => createPerformanceTable(new CatchRuleset().RulesetInfo, "US", 10));
|
||||
AddStep("Osu spotlight table (chart 271)", () => createSpotlightTable(new OsuRuleset().RulesetInfo, 271));
|
||||
AddStep("User performance", createPerformanceTable);
|
||||
AddStep("User scores", createScoreTable);
|
||||
AddStep("Country scores", createCountryTable);
|
||||
}
|
||||
|
||||
private void createCountryTable(RulesetInfo ruleset, int page = 1)
|
||||
private void createCountryTable()
|
||||
{
|
||||
onLoadStarted();
|
||||
|
||||
request = new GetCountryRankingsRequest(ruleset, page);
|
||||
((GetCountryRankingsRequest)request).Success += rankings => Schedule(() =>
|
||||
var countries = new List<CountryStatistics>
|
||||
{
|
||||
var table = new CountriesTable(page, rankings.Countries);
|
||||
loadTable(table);
|
||||
});
|
||||
new CountryStatistics
|
||||
{
|
||||
Country = new Country { FlagName = "US", FullName = "United States" },
|
||||
FlagName = "US",
|
||||
ActiveUsers = 2_972_623,
|
||||
PlayCount = 3_086_515_743,
|
||||
RankedScore = 449_407_643_332_546,
|
||||
Performance = 371_974_024
|
||||
},
|
||||
new CountryStatistics
|
||||
{
|
||||
Country = new Country { FlagName = "RU", FullName = "Russian Federation" },
|
||||
FlagName = "RU",
|
||||
ActiveUsers = 1_609_989,
|
||||
PlayCount = 1_637_052_841,
|
||||
RankedScore = 221_660_827_473_004,
|
||||
Performance = 163_426_476
|
||||
}
|
||||
};
|
||||
|
||||
api.Queue(request);
|
||||
var table = new CountriesTable(1, countries);
|
||||
loadTable(table);
|
||||
}
|
||||
|
||||
private void createPerformanceTable(RulesetInfo ruleset, string country, int page = 1)
|
||||
private static List<UserStatistics> createUserStatistics() => new List<UserStatistics>
|
||||
{
|
||||
new UserStatistics
|
||||
{
|
||||
User = new APIUser
|
||||
{
|
||||
Username = "first active user",
|
||||
Country = new Country { FlagName = "JP" },
|
||||
Active = true,
|
||||
},
|
||||
Accuracy = 0.9972,
|
||||
PlayCount = 233_215,
|
||||
TotalScore = 983_231_234_656,
|
||||
RankedScore = 593_231_345_897,
|
||||
PP = 23_934,
|
||||
GradesCount = new UserStatistics.Grades
|
||||
{
|
||||
SS = 35_132,
|
||||
S = 23_345,
|
||||
A = 12_234
|
||||
}
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
User = new APIUser
|
||||
{
|
||||
Username = "inactive user",
|
||||
Country = new Country { FlagName = "AU" },
|
||||
Active = false,
|
||||
},
|
||||
Accuracy = 0.9831,
|
||||
PlayCount = 195_342,
|
||||
TotalScore = 683_231_234_656,
|
||||
RankedScore = 393_231_345_897,
|
||||
PP = 20_934,
|
||||
GradesCount = new UserStatistics.Grades
|
||||
{
|
||||
SS = 32_132,
|
||||
S = 20_345,
|
||||
A = 9_234
|
||||
}
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
User = new APIUser
|
||||
{
|
||||
Username = "second active user",
|
||||
Country = new Country { FlagName = "PL" },
|
||||
Active = true,
|
||||
},
|
||||
Accuracy = 0.9584,
|
||||
PlayCount = 100_903,
|
||||
TotalScore = 97_242_983_434,
|
||||
RankedScore = 3_156_345_897,
|
||||
PP = 9_568,
|
||||
GradesCount = new UserStatistics.Grades
|
||||
{
|
||||
SS = 13_152,
|
||||
S = 24_375,
|
||||
A = 9_960
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
private void createPerformanceTable()
|
||||
{
|
||||
onLoadStarted();
|
||||
|
||||
request = new GetUserRankingsRequest(ruleset, country: country, page: page);
|
||||
((GetUserRankingsRequest)request).Success += rankings => Schedule(() =>
|
||||
{
|
||||
var table = new PerformanceTable(page, rankings.Users);
|
||||
loadTable(table);
|
||||
});
|
||||
|
||||
api.Queue(request);
|
||||
loadTable(new PerformanceTable(1, createUserStatistics()));
|
||||
}
|
||||
|
||||
private void createScoreTable(RulesetInfo ruleset, int page = 1)
|
||||
private void createScoreTable()
|
||||
{
|
||||
onLoadStarted();
|
||||
|
||||
request = new GetUserRankingsRequest(ruleset, UserRankingsType.Score, page);
|
||||
((GetUserRankingsRequest)request).Success += rankings => Schedule(() =>
|
||||
{
|
||||
var table = new ScoresTable(page, rankings.Users);
|
||||
loadTable(table);
|
||||
});
|
||||
|
||||
api.Queue(request);
|
||||
}
|
||||
|
||||
private void createSpotlightTable(RulesetInfo ruleset, int spotlight)
|
||||
{
|
||||
onLoadStarted();
|
||||
|
||||
request = new GetSpotlightRankingsRequest(ruleset, spotlight, RankingsSortCriteria.All);
|
||||
((GetSpotlightRankingsRequest)request).Success += rankings => Schedule(() =>
|
||||
{
|
||||
var table = new ScoresTable(1, rankings.Users);
|
||||
loadTable(table);
|
||||
});
|
||||
|
||||
api.Queue(request);
|
||||
loadTable(new ScoresTable(1, createUserStatistics()));
|
||||
}
|
||||
|
||||
private void onLoadStarted()
|
||||
{
|
||||
loading.Show();
|
||||
request?.Cancel();
|
||||
cancellationToken?.Cancel();
|
||||
cancellationToken = new CancellationTokenSource();
|
||||
}
|
||||
|
57
osu.Game.Tests/Visual/Online/TestSceneScoreboardTime.cs
Normal file
57
osu.Game.Tests/Visual/Online/TestSceneScoreboardTime.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneScoreboardTime : OsuTestScene
|
||||
{
|
||||
private StopwatchClock stopwatch;
|
||||
|
||||
[Test]
|
||||
public void TestVariousUnits()
|
||||
{
|
||||
AddStep("create various scoreboard times", () => Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Clock = new FramedClock(stopwatch = new StopwatchClock()), // prevent time from naturally elapsing.
|
||||
Direction = FillDirection.Vertical,
|
||||
ChildrenEnumerable = testCases.Select(dateTime => new ScoreboardTime(dateTime, 24).With(time => time.Anchor = time.Origin = Anchor.TopCentre))
|
||||
});
|
||||
|
||||
AddStep("start stopwatch", () => stopwatch.Start());
|
||||
}
|
||||
|
||||
private static IEnumerable<DateTimeOffset> testCases => new[]
|
||||
{
|
||||
DateTimeOffset.Now,
|
||||
DateTimeOffset.Now.AddSeconds(-1),
|
||||
DateTimeOffset.Now.AddSeconds(-25),
|
||||
DateTimeOffset.Now.AddSeconds(-59),
|
||||
DateTimeOffset.Now.AddMinutes(-1),
|
||||
DateTimeOffset.Now.AddMinutes(-25),
|
||||
DateTimeOffset.Now.AddMinutes(-59),
|
||||
DateTimeOffset.Now.AddHours(-1),
|
||||
DateTimeOffset.Now.AddHours(-13),
|
||||
DateTimeOffset.Now.AddHours(-23),
|
||||
DateTimeOffset.Now.AddDays(-1),
|
||||
DateTimeOffset.Now.AddDays(-6),
|
||||
DateTimeOffset.Now.AddDays(-16),
|
||||
DateTimeOffset.Now.AddMonths(-1),
|
||||
DateTimeOffset.Now.AddMonths(-11),
|
||||
DateTimeOffset.Now.AddYears(-1),
|
||||
DateTimeOffset.Now.AddYears(-5)
|
||||
};
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get<Storage>();
|
||||
FileBasedIPC ipc = null;
|
||||
|
||||
WaitForOrAssert(() => (ipc = osu.Dependencies.Get<MatchIPCInfo>() as FileBasedIPC) != null, @"ipc could not be populated in a reasonable amount of time");
|
||||
WaitForOrAssert(() => (ipc = osu.Dependencies.Get<MatchIPCInfo>() as FileBasedIPC)?.IsLoaded == true, @"ipc could not be populated in a reasonable amount of time");
|
||||
|
||||
Assert.True(ipc.SetIPCLocation(testStableInstallDirectory));
|
||||
Assert.True(storage.AllTournaments.Exists("stable.json"));
|
||||
|
@ -26,6 +26,9 @@ namespace osu.Game.Database
|
||||
/// <summary>
|
||||
/// Create a detached copy of the each item in the collection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Items which are already detached (ie. not managed by realm) will not be modified.
|
||||
/// </remarks>
|
||||
/// <param name="items">A list of managed <see cref="RealmObject"/>s to detach.</param>
|
||||
/// <typeparam name="T">The type of object.</typeparam>
|
||||
/// <returns>A list containing non-managed copies of provided items.</returns>
|
||||
@ -42,6 +45,9 @@ namespace osu.Game.Database
|
||||
/// <summary>
|
||||
/// Create a detached copy of the item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the item if already detached (ie. not managed by realm) it will not be detached again and the original instance will be returned. This allows this method to be potentially called at multiple levels while only incurring the clone overhead once.
|
||||
/// </remarks>
|
||||
/// <param name="item">The managed <see cref="RealmObject"/> to detach.</param>
|
||||
/// <typeparam name="T">The type of object.</typeparam>
|
||||
/// <returns>A non-managed copy of provided item. Will return the provided item if already detached.</returns>
|
||||
|
@ -12,17 +12,17 @@ namespace osu.Game.Online.Solo
|
||||
{
|
||||
public class SubmitSoloScoreRequest : APIRequest<MultiplayerScore>
|
||||
{
|
||||
public readonly SubmittableScore Score;
|
||||
|
||||
private readonly long scoreId;
|
||||
|
||||
private readonly int beatmapId;
|
||||
|
||||
private readonly SubmittableScore score;
|
||||
|
||||
public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo)
|
||||
{
|
||||
this.beatmapId = beatmapId;
|
||||
this.scoreId = scoreId;
|
||||
score = new SubmittableScore(scoreInfo);
|
||||
Score = new SubmittableScore(scoreInfo);
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
@ -33,7 +33,7 @@ namespace osu.Game.Online.Solo
|
||||
req.Method = HttpMethod.Put;
|
||||
req.Timeout = 30000;
|
||||
|
||||
req.AddRaw(JsonConvert.SerializeObject(score, new JsonSerializerSettings
|
||||
req.AddRaw(JsonConvert.SerializeObject(Score, new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
}));
|
||||
|
@ -128,6 +128,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
if (showPerformancePoints)
|
||||
columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeaderspp, Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30)));
|
||||
|
||||
columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersTime, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)));
|
||||
columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersMods, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)));
|
||||
|
||||
return columns.ToArray();
|
||||
@ -202,6 +203,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
});
|
||||
}
|
||||
|
||||
content.Add(new ScoreboardTime(score.Date, text_size)
|
||||
{
|
||||
Margin = new MarginPadding { Right = 10 }
|
||||
});
|
||||
|
||||
content.Add(new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
|
56
osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs
Normal file
56
osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs
Normal file
@ -0,0 +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;
|
||||
using Humanizer;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
{
|
||||
public class ScoreboardTime : DrawableDate
|
||||
{
|
||||
public ScoreboardTime(DateTimeOffset date, float textSize = OsuFont.DEFAULT_FONT_SIZE, bool italic = true)
|
||||
: base(date, textSize, italic)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string Format()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var difference = now - Date;
|
||||
|
||||
// web uses momentjs's custom locales to format the date for the purposes of the scoreboard.
|
||||
// this is intended to be a best-effort, more legible approximation of that.
|
||||
// compare:
|
||||
// * https://github.com/ppy/osu-web/blob/a8f5a68fb435cb19a4faa4c7c4bce08c4f096933/resources/assets/lib/scoreboard-time.tsx
|
||||
// * https://momentjs.com/docs/#/customization/ (reference for the customisation format)
|
||||
|
||||
// TODO: support localisation (probably via `CommonStrings.CountHours()` etc.)
|
||||
// requires pluralisable string support framework-side
|
||||
|
||||
if (difference.TotalHours < 1)
|
||||
return CommonStrings.TimeNow.ToString();
|
||||
if (difference.TotalDays < 1)
|
||||
return "hr".ToQuantity((int)difference.TotalHours);
|
||||
|
||||
// this is where this gets more complicated because of how the calendar works.
|
||||
// since there's no `TotalMonths` / `TotalYears`, we have to iteratively add months/years
|
||||
// and test against cutoff dates to determine how many months/years to show.
|
||||
|
||||
if (Date > now.AddMonths(-1))
|
||||
return difference.TotalDays < 2 ? "1dy" : $"{(int)difference.TotalDays}dys";
|
||||
|
||||
for (int months = 1; months <= 11; ++months)
|
||||
{
|
||||
if (Date > now.AddMonths(-(months + 1)))
|
||||
return months == 1 ? "1mo" : $"{months}mos";
|
||||
}
|
||||
|
||||
int years = 1;
|
||||
while (Date <= now.AddYears(-(years + 1)))
|
||||
years += 1;
|
||||
return years == 1 ? "1yr" : $"{years}yrs";
|
||||
}
|
||||
}
|
||||
}
|
@ -54,13 +54,15 @@ namespace osu.Game.Overlays.Rankings.Tables
|
||||
Spacing = new Vector2(0, row_spacing),
|
||||
});
|
||||
|
||||
rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground { Height = row_height }));
|
||||
rankings.ForEach(s => backgroundFlow.Add(CreateRowBackground(s)));
|
||||
|
||||
Columns = mainHeaders.Concat(CreateAdditionalHeaders()).Cast<TableColumn>().ToArray();
|
||||
Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular();
|
||||
Content = rankings.Select((s, i) => CreateRowContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular();
|
||||
}
|
||||
|
||||
private Drawable[] createContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray();
|
||||
protected virtual Drawable CreateRowBackground(TModel item) => new TableRowBackground { Height = row_height };
|
||||
|
||||
protected virtual Drawable[] CreateRowContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray();
|
||||
|
||||
private static RankingsTableColumn[] mainHeaders => new[]
|
||||
{
|
||||
|
@ -24,6 +24,31 @@ namespace osu.Game.Overlays.Rankings.Tables
|
||||
|
||||
protected virtual IEnumerable<LocalisableString> GradeColumns => new List<LocalisableString> { RankingsStrings.Statss, RankingsStrings.Stats, RankingsStrings.Stata };
|
||||
|
||||
protected override Drawable CreateRowBackground(UserStatistics item)
|
||||
{
|
||||
var background = base.CreateRowBackground(item);
|
||||
|
||||
// see: https://github.com/ppy/osu-web/blob/9de00a0b874c56893d98261d558d78d76259d81b/resources/views/multiplayer/rooms/_rankings_table.blade.php#L23
|
||||
if (!item.User.Active)
|
||||
background.Alpha = 0.5f;
|
||||
|
||||
return background;
|
||||
}
|
||||
|
||||
protected override Drawable[] CreateRowContent(int index, UserStatistics item)
|
||||
{
|
||||
var content = base.CreateRowContent(index, item);
|
||||
|
||||
// see: https://github.com/ppy/osu-web/blob/9de00a0b874c56893d98261d558d78d76259d81b/resources/views/multiplayer/rooms/_rankings_table.blade.php#L23
|
||||
if (!item.User.Active)
|
||||
{
|
||||
foreach (var d in content)
|
||||
d.Alpha = 0.5f;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[]
|
||||
{
|
||||
new RankingsTableColumn(RankingsStrings.StatAccuracy, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -9,6 +10,7 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
@ -66,7 +68,8 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
|
||||
Current.BindValueChanged(val =>
|
||||
{
|
||||
textBox.Text = val.NewValue.ToString();
|
||||
decimal decimalValue = slider.Current.Value.ToDecimal(NumberFormatInfo.InvariantInfo);
|
||||
textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}");
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
@ -300,6 +300,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
updateWorkingBeatmap();
|
||||
beginHandlingTrack();
|
||||
Scheduler.AddOnce(UpdateMods);
|
||||
Scheduler.AddOnce(updateRuleset);
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
@ -353,8 +354,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
.ToList();
|
||||
|
||||
UpdateMods();
|
||||
|
||||
Ruleset.Value = rulesets.GetRuleset(selected.RulesetID);
|
||||
updateRuleset();
|
||||
|
||||
if (!selected.AllowedMods.Any())
|
||||
{
|
||||
@ -387,6 +387,14 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList();
|
||||
}
|
||||
|
||||
private void updateRuleset()
|
||||
{
|
||||
if (SelectedItem.Value == null || !this.IsCurrentScreen())
|
||||
return;
|
||||
|
||||
Ruleset.Value = rulesets.GetRuleset(SelectedItem.Value.RulesetID);
|
||||
}
|
||||
|
||||
private void beginHandlingTrack()
|
||||
{
|
||||
Beatmap.BindValueChanged(applyLoopingToTrack, true);
|
||||
|
@ -28,8 +28,8 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
bool match =
|
||||
criteria.Ruleset == null ||
|
||||
BeatmapInfo.RulesetID == criteria.Ruleset.ID ||
|
||||
(BeatmapInfo.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps);
|
||||
BeatmapInfo.RulesetID == criteria.Ruleset.OnlineID ||
|
||||
(BeatmapInfo.RulesetID == 0 && criteria.Ruleset.OnlineID > 0 && criteria.AllowConvertedBeatmaps);
|
||||
|
||||
if (BeatmapInfo.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true)
|
||||
{
|
||||
|
@ -37,7 +37,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.7.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.107.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1215.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.109.0" />
|
||||
<PackageReference Include="Sentry" Version="3.12.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
|
@ -61,7 +61,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.107.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1215.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.109.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
<PropertyGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user