mirror of
https://github.com/ppy/osu.git
synced 2025-03-28 01:47:33 +08:00
Merge branch 'master' into async-deadlock-safety
This commit is contained in:
commit
af9fad00c3
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<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" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.107.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
|
@ -95,7 +95,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.That(decodedAfterEncode, Is.Not.Null);
|
Assert.That(decodedAfterEncode, Is.Not.Null);
|
||||||
|
|
||||||
Assert.That(decodedAfterEncode.ScoreInfo.User.Username, Is.EqualTo(scoreInfo.User.Username));
|
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.Ruleset, Is.EqualTo(scoreInfo.Ruleset));
|
||||||
Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(scoreInfo.TotalScore));
|
Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(scoreInfo.TotalScore));
|
||||||
Assert.That(decodedAfterEncode.ScoreInfo.MaxCombo, Is.EqualTo(scoreInfo.MaxCombo));
|
Assert.That(decodedAfterEncode.ScoreInfo.MaxCombo, Is.EqualTo(scoreInfo.MaxCombo));
|
||||||
|
@ -16,7 +16,8 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
|||||||
{
|
{
|
||||||
private BeatmapInfo getExampleBeatmap() => new BeatmapInfo
|
private BeatmapInfo getExampleBeatmap() => new BeatmapInfo
|
||||||
{
|
{
|
||||||
Ruleset = new RulesetInfo { OnlineID = 5 },
|
Ruleset = new RulesetInfo { OnlineID = 0 },
|
||||||
|
RulesetID = 0,
|
||||||
StarRating = 4.0d,
|
StarRating = 4.0d,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNoSubmissionOnResultsWithNoToken()
|
public void TestNoSubmissionOnResultsWithNoToken()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(false);
|
prepareTestAPI(false);
|
||||||
|
|
||||||
createPlayerTest();
|
createPlayerTest();
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSubmissionOnResults()
|
public void TestSubmissionOnResults()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest();
|
createPlayerTest();
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSubmissionForDifferentRuleset()
|
public void TestSubmissionForDifferentRuleset()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest(createRuleset: () => new TaikoRuleset());
|
createPlayerTest(createRuleset: () => new TaikoRuleset());
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSubmissionForConvertedBeatmap()
|
public void TestSubmissionForConvertedBeatmap()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest(createRuleset: () => new ManiaRuleset(), createBeatmap: _ => createTestBeatmap(new OsuRuleset().RulesetInfo));
|
createPlayerTest(createRuleset: () => new ManiaRuleset(), createBeatmap: _ => createTestBeatmap(new OsuRuleset().RulesetInfo));
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNoSubmissionOnExitWithNoToken()
|
public void TestNoSubmissionOnExitWithNoToken()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(false);
|
prepareTestAPI(false);
|
||||||
|
|
||||||
createPlayerTest();
|
createPlayerTest();
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNoSubmissionOnEmptyFail()
|
public void TestNoSubmissionOnEmptyFail()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest(true);
|
createPlayerTest(true);
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSubmissionOnFail()
|
public void TestSubmissionOnFail()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest(true);
|
createPlayerTest(true);
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNoSubmissionOnEmptyExit()
|
public void TestNoSubmissionOnEmptyExit()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest();
|
createPlayerTest();
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSubmissionOnExit()
|
public void TestSubmissionOnExit()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest();
|
createPlayerTest();
|
||||||
|
|
||||||
@ -213,7 +213,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSubmissionOnExitDuringImport()
|
public void TestSubmissionOnExitDuringImport()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest();
|
createPlayerTest();
|
||||||
AddStep("block imports", () => Player.AllowImportCompletion.Wait());
|
AddStep("block imports", () => Player.AllowImportCompletion.Wait());
|
||||||
@ -226,13 +226,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("exit", () => Player.Exit());
|
AddStep("exit", () => Player.Exit());
|
||||||
AddStep("allow import to proceed", () => Player.AllowImportCompletion.Release(1));
|
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]
|
[Test]
|
||||||
public void TestNoSubmissionOnLocalBeatmap()
|
public void TestNoSubmissionOnLocalBeatmap()
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest(false, r =>
|
createPlayerTest(false, r =>
|
||||||
{
|
{
|
||||||
@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[TestCase(10)]
|
[TestCase(10)]
|
||||||
public void TestNoSubmissionOnCustomRuleset(int? rulesetId)
|
public void TestNoSubmissionOnCustomRuleset(int? rulesetId)
|
||||||
{
|
{
|
||||||
prepareTokenResponse(true);
|
prepareTestAPI(true);
|
||||||
|
|
||||||
createPlayerTest(false, createRuleset: () => new OsuRuleset { RulesetInfo = { OnlineID = rulesetId ?? -1 } });
|
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", () =>
|
AddStep("Prepare test API", () =>
|
||||||
{
|
{
|
||||||
@ -289,6 +289,31 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
else
|
else
|
||||||
tokenRequest.TriggerFailure(new APIException("something went wrong!", null));
|
tokenRequest.TriggerFailure(new APIException("something went wrong!", null));
|
||||||
return true;
|
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;
|
return false;
|
||||||
|
@ -1,13 +1,25 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.Multiplayer;
|
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;
|
||||||
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Multiplayer
|
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);
|
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", () =>
|
AddStep("click add button", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSubScreen.AddItemButton>().Single());
|
InputManager.MoveMouseTo(this.ChildrenOfType<MultiplayerMatchSubScreen.AddItemButton>().Single());
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for song select", () => CurrentSubScreen is Screens.Select.SongSelect select && select.BeatmapSetsLoaded);
|
AddUntilStep("wait for song select", () => (songSelect = CurrentSubScreen as Screens.Select.SongSelect) != null);
|
||||||
AddStep("select other beatmap", () => ((Screens.Select.SongSelect)CurrentSubScreen).FinaliseSelection(beatmap()));
|
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);
|
AddUntilStep("wait for return to match", () => CurrentSubScreen is MultiplayerMatchSubScreen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -23,6 +24,8 @@ using osu.Game.Online.Multiplayer;
|
|||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.OnlinePlay.Components;
|
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);
|
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]
|
[Test]
|
||||||
public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap()
|
public void TestLocalPlayDoesNotStartWhileSpectatingWithNoBeatmap()
|
||||||
{
|
{
|
||||||
|
@ -1,38 +1,27 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Overlays.Rankings.Tables;
|
using osu.Game.Overlays.Rankings.Tables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Online.API.Requests;
|
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using System.Threading;
|
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.Framework.Allocation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Rankings;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
public class TestSceneRankingsTables : OsuTestScene
|
public class TestSceneRankingsTables : OsuTestScene
|
||||||
{
|
{
|
||||||
protected override bool UseOnlineAPI => true;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IAPIProvider api { get; set; }
|
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||||
|
|
||||||
private readonly BasicScrollContainer scrollFlow;
|
private readonly BasicScrollContainer scrollFlow;
|
||||||
private readonly LoadingLayer loading;
|
private readonly LoadingLayer loading;
|
||||||
private CancellationTokenSource cancellationToken;
|
private CancellationTokenSource cancellationToken;
|
||||||
private APIRequest request;
|
|
||||||
|
|
||||||
public TestSceneRankingsTables()
|
public TestSceneRankingsTables()
|
||||||
{
|
{
|
||||||
@ -53,73 +42,120 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
AddStep("Osu performance", () => createPerformanceTable(new OsuRuleset().RulesetInfo, null));
|
AddStep("User performance", createPerformanceTable);
|
||||||
AddStep("Mania scores", () => createScoreTable(new ManiaRuleset().RulesetInfo));
|
AddStep("User scores", createScoreTable);
|
||||||
AddStep("Taiko country scores", () => createCountryTable(new TaikoRuleset().RulesetInfo));
|
AddStep("Country scores", createCountryTable);
|
||||||
AddStep("Catch US performance page 10", () => createPerformanceTable(new CatchRuleset().RulesetInfo, "US", 10));
|
|
||||||
AddStep("Osu spotlight table (chart 271)", () => createSpotlightTable(new OsuRuleset().RulesetInfo, 271));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCountryTable(RulesetInfo ruleset, int page = 1)
|
private void createCountryTable()
|
||||||
{
|
{
|
||||||
onLoadStarted();
|
onLoadStarted();
|
||||||
|
|
||||||
request = new GetCountryRankingsRequest(ruleset, page);
|
var countries = new List<CountryStatistics>
|
||||||
((GetCountryRankingsRequest)request).Success += rankings => Schedule(() =>
|
|
||||||
{
|
{
|
||||||
var table = new CountriesTable(page, rankings.Countries);
|
new CountryStatistics
|
||||||
loadTable(table);
|
{
|
||||||
});
|
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();
|
onLoadStarted();
|
||||||
|
loadTable(new PerformanceTable(1, createUserStatistics()));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createScoreTable(RulesetInfo ruleset, int page = 1)
|
private void createScoreTable()
|
||||||
{
|
{
|
||||||
onLoadStarted();
|
onLoadStarted();
|
||||||
|
loadTable(new ScoresTable(1, createUserStatistics()));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onLoadStarted()
|
private void onLoadStarted()
|
||||||
{
|
{
|
||||||
loading.Show();
|
loading.Show();
|
||||||
request?.Cancel();
|
|
||||||
cancellationToken?.Cancel();
|
cancellationToken?.Cancel();
|
||||||
cancellationToken = new CancellationTokenSource();
|
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>();
|
TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get<Storage>();
|
||||||
FileBasedIPC ipc = null;
|
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(ipc.SetIPCLocation(testStableInstallDirectory));
|
||||||
Assert.True(storage.AllTournaments.Exists("stable.json"));
|
Assert.True(storage.AllTournaments.Exists("stable.json"));
|
||||||
|
@ -26,6 +26,9 @@ namespace osu.Game.Database
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a detached copy of the each item in the collection.
|
/// Create a detached copy of the each item in the collection.
|
||||||
/// </summary>
|
/// </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>
|
/// <param name="items">A list of managed <see cref="RealmObject"/>s to detach.</param>
|
||||||
/// <typeparam name="T">The type of object.</typeparam>
|
/// <typeparam name="T">The type of object.</typeparam>
|
||||||
/// <returns>A list containing non-managed copies of provided items.</returns>
|
/// <returns>A list containing non-managed copies of provided items.</returns>
|
||||||
@ -42,6 +45,9 @@ namespace osu.Game.Database
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a detached copy of the item.
|
/// Create a detached copy of the item.
|
||||||
/// </summary>
|
/// </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>
|
/// <param name="item">The managed <see cref="RealmObject"/> to detach.</param>
|
||||||
/// <typeparam name="T">The type of object.</typeparam>
|
/// <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>
|
/// <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 class SubmitSoloScoreRequest : APIRequest<MultiplayerScore>
|
||||||
{
|
{
|
||||||
|
public readonly SubmittableScore Score;
|
||||||
|
|
||||||
private readonly long scoreId;
|
private readonly long scoreId;
|
||||||
|
|
||||||
private readonly int beatmapId;
|
private readonly int beatmapId;
|
||||||
|
|
||||||
private readonly SubmittableScore score;
|
|
||||||
|
|
||||||
public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo)
|
public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo)
|
||||||
{
|
{
|
||||||
this.beatmapId = beatmapId;
|
this.beatmapId = beatmapId;
|
||||||
this.scoreId = scoreId;
|
this.scoreId = scoreId;
|
||||||
score = new SubmittableScore(scoreInfo);
|
Score = new SubmittableScore(scoreInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override WebRequest CreateWebRequest()
|
protected override WebRequest CreateWebRequest()
|
||||||
@ -33,7 +33,7 @@ namespace osu.Game.Online.Solo
|
|||||||
req.Method = HttpMethod.Put;
|
req.Method = HttpMethod.Put;
|
||||||
req.Timeout = 30000;
|
req.Timeout = 30000;
|
||||||
|
|
||||||
req.AddRaw(JsonConvert.SerializeObject(score, new JsonSerializerSettings
|
req.AddRaw(JsonConvert.SerializeObject(Score, new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||||
}));
|
}));
|
||||||
|
@ -128,6 +128,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
if (showPerformancePoints)
|
if (showPerformancePoints)
|
||||||
columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeaderspp, Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30)));
|
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)));
|
columns.Add(new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersMods, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)));
|
||||||
|
|
||||||
return columns.ToArray();
|
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
|
content.Add(new FillFlowContainer
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Horizontal,
|
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),
|
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();
|
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[]
|
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 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[]
|
protected override RankingsTableColumn[] CreateAdditionalHeaders() => new[]
|
||||||
{
|
{
|
||||||
new RankingsTableColumn(RankingsStrings.StatAccuracy, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -9,6 +10,7 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Timing
|
namespace osu.Game.Screens.Edit.Timing
|
||||||
{
|
{
|
||||||
@ -66,7 +68,8 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
|
|
||||||
Current.BindValueChanged(val =>
|
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);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,6 +300,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
updateWorkingBeatmap();
|
updateWorkingBeatmap();
|
||||||
beginHandlingTrack();
|
beginHandlingTrack();
|
||||||
Scheduler.AddOnce(UpdateMods);
|
Scheduler.AddOnce(UpdateMods);
|
||||||
|
Scheduler.AddOnce(updateRuleset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnExiting(IScreen next)
|
public override bool OnExiting(IScreen next)
|
||||||
@ -353,8 +354,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
UpdateMods();
|
UpdateMods();
|
||||||
|
updateRuleset();
|
||||||
Ruleset.Value = rulesets.GetRuleset(selected.RulesetID);
|
|
||||||
|
|
||||||
if (!selected.AllowedMods.Any())
|
if (!selected.AllowedMods.Any())
|
||||||
{
|
{
|
||||||
@ -387,6 +387,14 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList();
|
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()
|
private void beginHandlingTrack()
|
||||||
{
|
{
|
||||||
Beatmap.BindValueChanged(applyLoopingToTrack, true);
|
Beatmap.BindValueChanged(applyLoopingToTrack, true);
|
||||||
|
@ -28,8 +28,8 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
|
|
||||||
bool match =
|
bool match =
|
||||||
criteria.Ruleset == null ||
|
criteria.Ruleset == null ||
|
||||||
BeatmapInfo.RulesetID == criteria.Ruleset.ID ||
|
BeatmapInfo.RulesetID == criteria.Ruleset.OnlineID ||
|
||||||
(BeatmapInfo.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps);
|
(BeatmapInfo.RulesetID == 0 && criteria.Ruleset.OnlineID > 0 && criteria.AllowConvertedBeatmaps);
|
||||||
|
|
||||||
if (BeatmapInfo.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true)
|
if (BeatmapInfo.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true)
|
||||||
{
|
{
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.7.1" />
|
<PackageReference Include="Realm" Version="10.7.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.107.0" />
|
<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="Sentry" Version="3.12.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.107.0" />
|
<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>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user