From ee591829899d2fd64938bd9ac406e88ad72b9082 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 18:12:19 +0900 Subject: [PATCH 01/65] Implement initial structure for room scores --- .../Requests/GetRoomPlaylistScoresRequest.cs | 21 +++++ osu.Game/Online/API/RoomScore.cs | 79 +++++++++++++++++++ .../Screens/Multi/Match/MatchSubScreen.cs | 16 ++++ .../Screens/Multi/Play/TimeshiftPlayer.cs | 8 ++ .../Multi/Ranking/TimeshiftResultsScreen.cs | 34 ++++++++ 5 files changed, 158 insertions(+) create mode 100644 osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs create mode 100644 osu.Game/Online/API/RoomScore.cs create mode 100644 osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs diff --git a/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs b/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs new file mode 100644 index 0000000000..dd7f80fd46 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs @@ -0,0 +1,21 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; + +namespace osu.Game.Online.API.Requests +{ + public class GetRoomPlaylistScoresRequest : APIRequest> + { + private readonly int roomId; + private readonly int playlistItemId; + + public GetRoomPlaylistScoresRequest(int roomId, int playlistItemId) + { + this.roomId = roomId; + this.playlistItemId = playlistItemId; + } + + protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores"; + } +} diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs new file mode 100644 index 0000000000..5d0a9539aa --- /dev/null +++ b/osu.Game/Online/API/RoomScore.cs @@ -0,0 +1,79 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Online.API +{ + public class RoomScore + { + [JsonProperty("id")] + public int ID { get; set; } + + [JsonProperty("user_id")] + public int UserID { get; set; } + + [JsonProperty("room_id")] + public int RoomID { get; set; } + + [JsonProperty("playlist_item_id")] + public int PlaylistItemID { get; set; } + + [JsonProperty("beatmap_id")] + public int BeatmapID { get; set; } + + [JsonProperty("rank")] + [JsonConverter(typeof(StringEnumConverter))] + public ScoreRank Rank { get; set; } + + [JsonProperty("total_score")] + public long TotalScore { get; set; } + + [JsonProperty("accuracy")] + public double Accuracy { get; set; } + + [JsonProperty("max_combo")] + public int MaxCombo { get; set; } + + [JsonProperty("mods")] + public APIMod[] Mods { get; set; } + + [JsonProperty("statistics")] + public Dictionary Statistics = new Dictionary(); + + [JsonProperty("passed")] + public bool Passed { get; set; } + + [JsonProperty("ended_at")] + public DateTimeOffset EndedAt { get; set; } + + public ScoreInfo CreateScoreInfo(PlaylistItem playlistItem) + { + var scoreInfo = new ScoreInfo + { + OnlineScoreID = ID, + TotalScore = TotalScore, + MaxCombo = MaxCombo, + Beatmap = playlistItem.Beatmap.Value, + BeatmapInfoID = playlistItem.BeatmapID, + Ruleset = playlistItem.Ruleset.Value, + RulesetID = playlistItem.RulesetID, + User = null, // todo: do we have a user object? + Accuracy = Accuracy, + Date = EndedAt, + Hash = string.Empty, // todo: temporary? + Rank = Rank, + Mods = Array.Empty(), // todo: how? + }; + + return scoreInfo; + } + } +} diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index caa547ac72..c37f51bcb4 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -10,6 +10,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Rulesets.Mods; @@ -162,6 +164,9 @@ namespace osu.Game.Screens.Multi.Match }; } + [Resolved] + private IAPIProvider api { get; set; } + protected override void LoadComplete() { base.LoadComplete(); @@ -185,6 +190,17 @@ namespace osu.Game.Screens.Multi.Match managerAdded = beatmapManager.ItemAdded.GetBoundCopy(); managerAdded.BindValueChanged(beatmapAdded); + + if (roomId.Value != null) + { + var req = new GetRoomPlaylistScoresRequest(roomId.Value.Value, playlist[0].ID); + + req.Success += scores => + { + }; + + api.Queue(req); + } } public override bool OnExiting(IScreen next) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 7f58de29fb..fbe9e3480f 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -14,7 +14,9 @@ using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; using osu.Game.Scoring; +using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Play; +using osu.Game.Screens.Ranking; namespace osu.Game.Screens.Multi.Play { @@ -88,6 +90,12 @@ namespace osu.Game.Screens.Multi.Play return false; } + protected override ResultsScreen CreateResults(ScoreInfo score) + { + Debug.Assert(roomId.Value != null); + return new TimeshiftResultsScreen(score, roomId.Value.Value, playlistItem); + } + protected override ScoreInfo CreateScore() { submitScore(); diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs new file mode 100644 index 0000000000..60cffc06df --- /dev/null +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . 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 osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Multiplayer; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking; + +namespace osu.Game.Screens.Multi.Ranking +{ + public class TimeshiftResultsScreen : ResultsScreen + { + private readonly int roomId; + private readonly PlaylistItem playlistItem; + + public TimeshiftResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry = true) + : base(score, allowRetry) + { + this.roomId = roomId; + this.playlistItem = playlistItem; + } + + protected override APIRequest FetchScores(Action> scoresCallback) + { + var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); + req.Success += r => scoresCallback?.Invoke(r.Select(s => s.CreateScoreInfo(playlistItem))); + return req; + } + } +} From 38502ba88c29615642e986c7dce99bbe16b92e87 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 14:54:51 +0900 Subject: [PATCH 02/65] Remove some unnecessary members --- osu.Game/Online/API/RoomScore.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index 5d0a9539aa..3ad6169833 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -20,15 +20,6 @@ namespace osu.Game.Online.API [JsonProperty("user_id")] public int UserID { get; set; } - [JsonProperty("room_id")] - public int RoomID { get; set; } - - [JsonProperty("playlist_item_id")] - public int PlaylistItemID { get; set; } - - [JsonProperty("beatmap_id")] - public int BeatmapID { get; set; } - [JsonProperty("rank")] [JsonConverter(typeof(StringEnumConverter))] public ScoreRank Rank { get; set; } From f9c64d7be38558d5b737ade5053622cbff276906 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 19:57:58 +0900 Subject: [PATCH 03/65] Implement creation of mods --- osu.Game/Online/API/RoomScore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index 3ad6169833..cb4f47c812 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Online.Multiplayer; @@ -61,7 +62,7 @@ namespace osu.Game.Online.API Date = EndedAt, Hash = string.Empty, // todo: temporary? Rank = Rank, - Mods = Array.Empty(), // todo: how? + Mods = Mods.Select(m => m.ToMod(playlistItem.Ruleset.Value.CreateInstance())).ToArray() }; return scoreInfo; From 7ac08620b80e335f747be668dfe5eaeae2a78703 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 19:58:07 +0900 Subject: [PATCH 04/65] Add a user object for now --- osu.Game/Online/API/RoomScore.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index cb4f47c812..00907eaa38 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -7,9 +7,9 @@ using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Users; namespace osu.Game.Online.API { @@ -18,8 +18,8 @@ namespace osu.Game.Online.API [JsonProperty("id")] public int ID { get; set; } - [JsonProperty("user_id")] - public int UserID { get; set; } + [JsonProperty("user")] + public User User { get; set; } [JsonProperty("rank")] [JsonConverter(typeof(StringEnumConverter))] @@ -57,7 +57,7 @@ namespace osu.Game.Online.API BeatmapInfoID = playlistItem.BeatmapID, Ruleset = playlistItem.Ruleset.Value, RulesetID = playlistItem.RulesetID, - User = null, // todo: do we have a user object? + User = User, Accuracy = Accuracy, Date = EndedAt, Hash = string.Empty, // todo: temporary? From d88bfa2080f7c1dcf852dfa278fd7f068dd3d8dc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 20:07:51 +0900 Subject: [PATCH 05/65] Cache ruleset + fix possible nullrefs --- osu.Game/Online/API/RoomScore.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index 00907eaa38..a1b08fe40e 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -7,6 +7,7 @@ using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Users; @@ -48,6 +49,8 @@ namespace osu.Game.Online.API public ScoreInfo CreateScoreInfo(PlaylistItem playlistItem) { + var rulesetInstance = playlistItem.Ruleset.Value.CreateInstance(); + var scoreInfo = new ScoreInfo { OnlineScoreID = ID, @@ -62,7 +65,7 @@ namespace osu.Game.Online.API Date = EndedAt, Hash = string.Empty, // todo: temporary? Rank = Rank, - Mods = Mods.Select(m => m.ToMod(playlistItem.Ruleset.Value.CreateInstance())).ToArray() + Mods = Mods?.Select(m => m.ToMod(rulesetInstance)).ToArray() ?? Array.Empty() }; return scoreInfo; From 0e28ded80fc4b078b10c9666f1399ce8fa994aa8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 20:07:54 +0900 Subject: [PATCH 06/65] Forward statistics --- osu.Game/Online/API/RoomScore.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/API/RoomScore.cs b/osu.Game/Online/API/RoomScore.cs index a1b08fe40e..3c7f8c9833 100644 --- a/osu.Game/Online/API/RoomScore.cs +++ b/osu.Game/Online/API/RoomScore.cs @@ -60,6 +60,7 @@ namespace osu.Game.Online.API BeatmapInfoID = playlistItem.BeatmapID, Ruleset = playlistItem.Ruleset.Value, RulesetID = playlistItem.RulesetID, + Statistics = Statistics, User = User, Accuracy = Accuracy, Date = EndedAt, From 0f373acacb55be124e573144a8b112291ea82fdd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 20:08:45 +0900 Subject: [PATCH 07/65] Add test scene --- .../TestSceneTimeshiftResultsScreen.cs | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs new file mode 100644 index 0000000000..7f43aea56e --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -0,0 +1,76 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Multi.Ranking; +using osu.Game.Tests.Beatmaps; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneTimeshiftResultsScreen : ScreenTestScene + { + [Test] + public void TestShowResults() + { + var score = new TestScoreInfo(new OsuRuleset().RulesetInfo); + var roomScores = new List(); + + for (int i = 0; i < 10; i++) + { + roomScores.Add(new RoomScore + { + ID = i, + Accuracy = 0.9 - 0.01 * i, + EndedAt = DateTimeOffset.Now.Subtract(TimeSpan.FromHours(i)), + Passed = true, + Rank = ScoreRank.B, + MaxCombo = 999, + TotalScore = 999999 - i * 1000, + User = new User + { + Id = 2, + Username = $"peppy{i}", + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }, + Statistics = + { + { HitResult.Miss, 1 }, + { HitResult.Meh, 50 }, + { HitResult.Good, 100 }, + { HitResult.Great, 300 }, + } + }); + } + + AddStep("bind request handler", () => ((DummyAPIAccess)API).HandleRequest = request => + { + switch (request) + { + case GetRoomPlaylistScoresRequest r: + r.TriggerSuccess(roomScores); + break; + } + }); + + AddStep("load results", () => + { + LoadScreen(new TimeshiftResultsScreen(score, 1, new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo } + })); + }); + + AddWaitStep("wait for display", 10); + } + } +} From a606f41297931301e66225c6ada60cf5fa6b326b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 May 2020 22:25:00 +0900 Subject: [PATCH 08/65] Add button to open results --- .../TestSceneTimeshiftResultsScreen.cs | 14 ++++++-- .../Screens/Multi/Match/MatchSubScreen.cs | 33 +++++++++++++++++-- .../Multi/Ranking/TimeshiftResultsScreen.cs | 2 +- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 7f43aea56e..d87a2e3408 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -19,9 +19,19 @@ namespace osu.Game.Tests.Visual.Multiplayer public class TestSceneTimeshiftResultsScreen : ScreenTestScene { [Test] - public void TestShowResults() + public void TestShowResultsWithScore() + { + createResults(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + } + + [Test] + public void TestShowResultsNullScore() + { + createResults(null); + } + + private void createResults(ScoreInfo score) { - var score = new TestScoreInfo(new OsuRuleset().RulesetInfo); var roomScores = new List(); for (int i = 0; i < 10; i++) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index c37f51bcb4..01a90139c3 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; @@ -18,6 +20,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Play; +using osu.Game.Screens.Multi.Ranking; using osu.Game.Screens.Select; using Footer = osu.Game.Screens.Multi.Match.Components.Footer; @@ -114,10 +117,29 @@ namespace osu.Game.Screens.Multi.Match { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = 5 }, - Child = new OverlinedPlaylist(true) // Temporarily always allow selection + Child = new GridContainer { RelativeSizeAxes = Axes.Both, - SelectedItem = { BindTarget = SelectedItem } + Content = new[] + { + new Drawable[] + { + new OverlinedPlaylist(true) // Temporarily always allow selection + { + RelativeSizeAxes = Axes.Both, + SelectedItem = { BindTarget = SelectedItem } + } + }, + new Drawable[] + { + new TriangleButton + { + RelativeSizeAxes = Axes.X, + Text = "Show beatmap results", + Action = showBeatmapResults + } + } + } } }, new Container @@ -257,5 +279,12 @@ namespace osu.Game.Screens.Multi.Match break; } } + + private void showBeatmapResults() + { + Debug.Assert(roomId.Value != null); + + this.Push(new TimeshiftResultsScreen(null, roomId.Value.Value, SelectedItem.Value, false)); + } } } diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 60cffc06df..f2afe15d35 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Multi.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); - req.Success += r => scoresCallback?.Invoke(r.Select(s => s.CreateScoreInfo(playlistItem))); + req.Success += r => scoresCallback?.Invoke(r.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); return req; } } From 1ccdfd736429a43f913491a6d562086c97e5133d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:03:13 +0900 Subject: [PATCH 09/65] Pull playlist beatmap checksum from api --- osu.Game/Online/API/APIPlaylistBeatmap.cs | 23 +++++++++++++++++++ .../API/Requests/Responses/APIBeatmap.cs | 2 +- osu.Game/Online/Multiplayer/PlaylistItem.cs | 3 +-- 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/API/APIPlaylistBeatmap.cs diff --git a/osu.Game/Online/API/APIPlaylistBeatmap.cs b/osu.Game/Online/API/APIPlaylistBeatmap.cs new file mode 100644 index 0000000000..4f7786e880 --- /dev/null +++ b/osu.Game/Online/API/APIPlaylistBeatmap.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; +using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; + +namespace osu.Game.Online.API +{ + public class APIPlaylistBeatmap : APIBeatmap + { + [JsonProperty("checksum")] + public string Checksum { get; set; } + + public override BeatmapInfo ToBeatmap(RulesetStore rulesets) + { + var b = base.ToBeatmap(rulesets); + b.MD5Hash = Checksum; + return b; + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index e023a2502f..ae65ac09b2 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -64,7 +64,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"max_combo")] private int? maxCombo { get; set; } - public BeatmapInfo ToBeatmap(RulesetStore rulesets) + public virtual BeatmapInfo ToBeatmap(RulesetStore rulesets) { var set = BeatmapSet?.ToBeatmapSet(rulesets); diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs index 9d6e8eb8e3..416091a1aa 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs @@ -7,7 +7,6 @@ using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -37,7 +36,7 @@ namespace osu.Game.Online.Multiplayer public readonly BindableList RequiredMods = new BindableList(); [JsonProperty("beatmap")] - private APIBeatmap apiBeatmap { get; set; } + private APIPlaylistBeatmap apiBeatmap { get; set; } private APIMod[] allowedModsBacking; From 68fbe9f4c144ad8be6be89286053fb08ccd5f50d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:03:50 +0900 Subject: [PATCH 10/65] Add checksum validation to the ready/start button --- .../Multi/Match/Components/ReadyButton.cs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index e1f86fcc97..a64f24dd7e 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Linq.Expressions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -52,24 +53,14 @@ namespace osu.Game.Screens.Multi.Match.Components private void updateSelectedItem(PlaylistItem item) { - hasBeatmap = false; - - int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; - if (beatmapId == null) - return; - - hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId) != null; + hasBeatmap = findBeatmap(expr => beatmaps.QueryBeatmap(expr)); } private void beatmapUpdated(ValueChangedEvent> weakSet) { if (weakSet.NewValue.TryGetTarget(out var set)) { - int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; - if (beatmapId == null) - return; - - if (set.Beatmaps.Any(b => b.OnlineBeatmapID == beatmapId)) + if (findBeatmap(expr => set.Beatmaps.AsQueryable().FirstOrDefault(expr))) Schedule(() => hasBeatmap = true); } } @@ -78,15 +69,22 @@ namespace osu.Game.Screens.Multi.Match.Components { if (weakSet.NewValue.TryGetTarget(out var set)) { - int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; - if (beatmapId == null) - return; - - if (set.Beatmaps.Any(b => b.OnlineBeatmapID == beatmapId)) + if (findBeatmap(expr => set.Beatmaps.AsQueryable().FirstOrDefault(expr))) Schedule(() => hasBeatmap = false); } } + private bool findBeatmap(Func>, BeatmapInfo> expression) + { + int? beatmapId = SelectedItem.Value?.Beatmap.Value?.OnlineBeatmapID; + string checksum = SelectedItem.Value?.Beatmap.Value?.MD5Hash; + + if (beatmapId == null || checksum == null) + return false; + + return expression(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum) != null; + } + protected override void Update() { base.Update(); From b41bb5a6824d8f4467b4178708cce88ace77011c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:04:00 +0900 Subject: [PATCH 11/65] Update databased MD5 hash on save --- osu.Game/Beatmaps/BeatmapManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f626b45e42..e5907809f3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -201,7 +201,9 @@ namespace osu.Game.Beatmaps using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) new LegacyBeatmapEncoder(beatmapContent).Encode(sw); - stream.Seek(0, SeekOrigin.Begin); + var attachedInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); + var md5Hash = stream.ComputeMD5Hash(); + attachedInfo.MD5Hash = md5Hash; UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } From 17e91695e0cf982b76b34db1184aca8be4a7020b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:04:51 +0900 Subject: [PATCH 12/65] Add checksum validation to the panel download buttons --- .../Screens/Multi/DrawableRoomPlaylistItem.cs | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs index c024304856..414c1f5748 100644 --- a/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/Multi/DrawableRoomPlaylistItem.cs @@ -188,7 +188,7 @@ namespace osu.Game.Screens.Multi X = -18, Children = new Drawable[] { - new PlaylistDownloadButton(item.Beatmap.Value.BeatmapSet) + new PlaylistDownloadButton(item) { Size = new Vector2(50, 30) }, @@ -212,9 +212,15 @@ namespace osu.Game.Screens.Multi private class PlaylistDownloadButton : BeatmapPanelDownloadButton { - public PlaylistDownloadButton(BeatmapSetInfo beatmapSet) - : base(beatmapSet) + private readonly PlaylistItem playlistItem; + + [Resolved] + private BeatmapManager beatmapManager { get; set; } + + public PlaylistDownloadButton(PlaylistItem playlistItem) + : base(playlistItem.Beatmap.Value.BeatmapSet) { + this.playlistItem = playlistItem; Alpha = 0; } @@ -223,11 +229,26 @@ namespace osu.Game.Screens.Multi base.LoadComplete(); State.BindValueChanged(stateChanged, true); + FinishTransforms(true); } private void stateChanged(ValueChangedEvent state) { - this.FadeTo(state.NewValue == DownloadState.LocallyAvailable ? 0 : 1, 500); + switch (state.NewValue) + { + case DownloadState.LocallyAvailable: + // Perform a local query of the beatmap by beatmap checksum, and reset the state if not matching. + if (beatmapManager.QueryBeatmap(b => b.MD5Hash == playlistItem.Beatmap.Value.MD5Hash) == null) + State.Value = DownloadState.NotDownloaded; + else + this.FadeTo(0, 500); + + break; + + default: + this.FadeTo(1, 500); + break; + } } } From 3c85561cdce09b7531aac9ea14bd8a681c159d8c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 14:31:43 +0900 Subject: [PATCH 13/65] Add tests --- .../TestSceneDrawableRoomPlaylist.cs | 73 +++++++++++++++++++ osu.Game/Tests/Beatmaps/TestBeatmap.cs | 21 +++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 5ef4dd6773..55b026eff6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -4,12 +4,18 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; +using osu.Game.Overlays; +using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Multi; @@ -23,6 +29,18 @@ namespace osu.Game.Tests.Visual.Multiplayer { private TestPlaylist playlist; + private BeatmapManager manager; + private RulesetStore rulesets; + + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + + manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); + } + [Test] public void TestNonEditableNonSelectable() { @@ -182,6 +200,28 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click delete button", () => InputManager.Click(MouseButton.Left)); } + [Test] + public void TestDownloadButtonHiddenInitiallyWhenBeatmapExists() + { + createPlaylist(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo); + + AddAssert("download button hidden", () => !playlist.ChildrenOfType().Single().IsPresent); + } + + [Test] + public void TestDownloadButtonVisibleInitiallyWhenBeatmapDoesNotExist() + { + var byOnlineId = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo; + byOnlineId.BeatmapSet.OnlineBeatmapSetID = 1337; // Some random ID that does not exist locally. + + var byChecksum = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo; + byChecksum.MD5Hash = "1337"; // Some random checksum that does not exist locally. + + createPlaylist(byOnlineId, byChecksum); + + AddAssert("download buttons shown", () => playlist.ChildrenOfType().All(d => d.IsPresent)); + } + private void moveToItem(int index, Vector2? offset = null) => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType>().ElementAt(index), offset)); @@ -235,6 +275,39 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded)); } + private void createPlaylist(params BeatmapInfo[] beatmaps) + { + AddStep("create playlist", () => + { + Child = playlist = new TestPlaylist(false, false) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(500, 300) + }; + + int index = 0; + + foreach (var b in beatmaps) + { + playlist.Items.Add(new PlaylistItem + { + ID = index++, + Beatmap = { Value = b }, + Ruleset = { Value = new OsuRuleset().RulesetInfo }, + RequiredMods = + { + new OsuModHardRock(), + new OsuModDoubleTime(), + new OsuModAutoplay() + } + }); + } + }); + + AddUntilStep("wait for items to load", () => playlist.ItemMap.Values.All(i => i.IsLoaded)); + } + private class TestPlaylist : DrawableRoomPlaylist { public new IReadOnlyDictionary> ItemMap => base.ItemMap; diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index a7c84bf692..9fc20fd0f2 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . 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.IO; using System.Text; +using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.IO; using osu.Game.Rulesets; @@ -43,10 +45,25 @@ namespace osu.Game.Tests.Beatmaps private static Beatmap createTestBeatmap() { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) - using (var reader = new LineBufferedReader(stream)) - return Decoder.GetDecoder(reader).Decode(reader); + { + using (var reader = new LineBufferedReader(stream)) + { + var b = Decoder.GetDecoder(reader).Decode(reader); + + b.BeatmapInfo.MD5Hash = test_beatmap_hash.Value.md5; + b.BeatmapInfo.Hash = test_beatmap_hash.Value.sha2; + + return b; + } + } } + private static readonly Lazy<(string md5, string sha2)> test_beatmap_hash = new Lazy<(string md5, string sha2)>(() => + { + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) + return (stream.ComputeMD5Hash(), stream.ComputeSHA2Hash()); + }); + private const string test_beatmap_data = @"osu file format v14 [General] From fac96f6dddf9dba724fa0b298444694310c508af Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 17:02:01 +0900 Subject: [PATCH 14/65] Fix match beatmap not updating after re-download --- .../Multiplayer/TestSceneMatchSubScreen.cs | 57 +++++++++++++++++-- .../Match/Components/MatchSettingsOverlay.cs | 2 +- .../Screens/Multi/Match/MatchSubScreen.cs | 13 +---- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs index d678d5a814..6154e646f8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs @@ -5,7 +5,9 @@ using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -29,14 +31,20 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached(typeof(IRoomManager))] private readonly TestRoomManager roomManager = new TestRoomManager(); - [Resolved] - private BeatmapManager beatmaps { get; set; } - - [Resolved] - private RulesetStore rulesets { get; set; } + private BeatmapManager manager; + private RulesetStore rulesets; private TestMatchSubScreen match; + [BackgroundDependencyLoader] + private void load(GameHost host, AudioManager audio) + { + Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); + Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default)); + + manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); + } + [SetUp] public void Setup() => Schedule(() => { @@ -75,10 +83,49 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("first playlist item selected", () => match.SelectedItem.Value == Room.Playlist[0]); } + [Test] + public void TestBeatmapUpdatedOnReImport() + { + BeatmapSetInfo importedSet = null; + + AddStep("import altered beatmap", () => + { + var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo); + beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1; + + importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result; + }); + + AddStep("load room", () => + { + Room.Name.Value = "my awesome room"; + Room.Host.Value = new User { Id = 2, Username = "peppy" }; + Room.Playlist.Add(new PlaylistItem + { + Beatmap = { Value = importedSet.Beatmaps[0] }, + Ruleset = { Value = new OsuRuleset().RulesetInfo } + }); + }); + + AddStep("create room", () => + { + InputManager.MoveMouseTo(match.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("match has altered beatmap", () => match.Beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize == 1); + + AddStep("re-import original beatmap", () => manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); + + AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.BeatmapInfo.BaseDifficulty.CircleSize != 1); + } + private class TestMatchSubScreen : MatchSubScreen { public new Bindable SelectedItem => base.SelectedItem; + public new Bindable Beatmap => base.Beatmap; + public TestMatchSubScreen(Room room) : base(room) { diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 54c4f8f7c7..49a0fc434b 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -433,7 +433,7 @@ namespace osu.Game.Screens.Multi.Match.Components } } - private class CreateRoomButton : TriangleButton + public class CreateRoomButton : TriangleButton { public CreateRoomButton() { diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index e1d72d9600..bbfbaf81af 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -207,6 +207,8 @@ namespace osu.Game.Screens.Multi.Match Ruleset.Value = item.Ruleset.Value; } + private void beatmapUpdated(ValueChangedEvent> weakSet) => Schedule(updateWorkingBeatmap); + private void updateWorkingBeatmap() { var beatmap = SelectedItem.Value?.Beatmap.Value; @@ -217,17 +219,6 @@ namespace osu.Game.Screens.Multi.Match Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } - private void beatmapUpdated(ValueChangedEvent> weakSet) - { - Schedule(() => - { - if (Beatmap.Value != beatmapManager.DefaultBeatmap) - return; - - updateWorkingBeatmap(); - }); - } - private void onStart() { switch (type.Value) From dfb9687fb5bfc47670ea6b017410e46c76c5905c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Jun 2020 17:22:09 +0900 Subject: [PATCH 15/65] Extract update into PreUpdate(), add test --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 3 +++ osu.Game/Beatmaps/BeatmapManager.cs | 17 +++++++++++++---- osu.Game/Database/ArchiveModelManager.cs | 10 ++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 5eb11a3264..88bb39a521 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -602,6 +602,8 @@ namespace osu.Game.Tests.Beatmaps.IO Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); + string oldMd5Hash = beatmapToUpdate.BeatmapInfo.MD5Hash; + using (var stream = new MemoryStream()) { using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) @@ -624,6 +626,7 @@ namespace osu.Game.Tests.Beatmaps.IO Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap; Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1)); Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000)); + Assert.That(updatedBeatmap.BeatmapInfo.MD5Hash, Is.Not.EqualTo(oldMd5Hash)); } finally { diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e5907809f3..668ac6ee10 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -201,10 +201,6 @@ namespace osu.Game.Beatmaps using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) new LegacyBeatmapEncoder(beatmapContent).Encode(sw); - var attachedInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); - var md5Hash = stream.ComputeMD5Hash(); - attachedInfo.MD5Hash = md5Hash; - UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } @@ -213,6 +209,19 @@ namespace osu.Game.Beatmaps workingCache.Remove(working); } + protected override void PreUpdate(BeatmapSetInfo item) + { + base.PreUpdate(item); + + foreach (var info in item.Beatmaps) + { + var file = item.Files.FirstOrDefault(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + + using (var stream = Files.Store.GetStream(file)) + info.MD5Hash = stream.ComputeMD5Hash(); + } + } + private readonly WeakList workingCache = new WeakList(); /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ae55a7b14a..f7e81ae4bc 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -430,10 +430,20 @@ namespace osu.Game.Database { item.Hash = computeHash(item); + PreUpdate(item); + ModelStore.Update(item); } } + /// + /// Perform any final actions before the update to database executes. + /// + /// The that is being updated. + protected virtual void PreUpdate(TModel item) + { + } + /// /// Delete an item from the manager. /// Is a no-op for already deleted items. From 13622eff1f12e8338f96ff5005ccbc2a9e12b8f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 12:54:07 +0900 Subject: [PATCH 16/65] Fix response value --- .../Multiplayer/TestSceneTimeshiftResultsScreen.cs | 2 +- .../Online/API/Requests/GetRoomPlaylistScoresRequest.cs | 9 ++++++++- osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index d87a2e3408..8559e7e2f4 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Multiplayer switch (request) { case GetRoomPlaylistScoresRequest r: - r.TriggerSuccess(roomScores); + r.TriggerSuccess(new RoomPlaylistScores { Scores = roomScores }); break; } }); diff --git a/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs b/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs index dd7f80fd46..38f852870b 100644 --- a/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetRoomPlaylistScoresRequest.cs @@ -2,10 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using Newtonsoft.Json; namespace osu.Game.Online.API.Requests { - public class GetRoomPlaylistScoresRequest : APIRequest> + public class GetRoomPlaylistScoresRequest : APIRequest { private readonly int roomId; private readonly int playlistItemId; @@ -18,4 +19,10 @@ namespace osu.Game.Online.API.Requests protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores"; } + + public class RoomPlaylistScores + { + [JsonProperty("scores")] + public List Scores { get; set; } + } } diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index f2afe15d35..d95cee2ab8 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -27,7 +27,7 @@ namespace osu.Game.Screens.Multi.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); - req.Success += r => scoresCallback?.Invoke(r.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); + req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); return req; } } From 22f4e9012c58e96b8b03f5e9bb4b9639efa83c34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 12:54:16 +0900 Subject: [PATCH 17/65] Remove temporary code --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 01a90139c3..3609be2dfe 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -13,7 +13,6 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Rulesets.Mods; @@ -212,17 +211,6 @@ namespace osu.Game.Screens.Multi.Match managerAdded = beatmapManager.ItemAdded.GetBoundCopy(); managerAdded.BindValueChanged(beatmapAdded); - - if (roomId.Value != null) - { - var req = new GetRoomPlaylistScoresRequest(roomId.Value.Value, playlist[0].ID); - - req.Success += scores => - { - }; - - api.Queue(req); - } } public override bool OnExiting(IScreen next) From 5f1d44a2bef173fc2745e0e1212672db4e3b5d86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 15:52:47 +0900 Subject: [PATCH 18/65] Update inspectcode / CodeFileSanity versions used in CI --- build/InspectCode.cake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/InspectCode.cake b/build/InspectCode.cake index 2e7a1d1b28..c8f4f37c94 100644 --- a/build/InspectCode.cake +++ b/build/InspectCode.cake @@ -1,5 +1,5 @@ -#addin "nuget:?package=CodeFileSanity&version=0.0.33" -#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.2" +#addin "nuget:?package=CodeFileSanity&version=0.0.36" +#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2020.1.3" #tool "nuget:?package=NVika.MSBuild&version=1.0.1" var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First(); From 3c07defa1a48a4a778d7e21b9fe9e8fc9bd1abe5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 15:57:01 +0900 Subject: [PATCH 19/65] Push to main multiplayer screen instead --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 8 ++++---- osu.Game/Screens/Multi/Multiplayer.cs | 14 -------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 3609be2dfe..b0717d3d28 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -20,6 +20,7 @@ using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Play; using osu.Game.Screens.Multi.Ranking; +using osu.Game.Screens.Play; using osu.Game.Screens.Select; using Footer = osu.Game.Screens.Multi.Match.Components.Footer; @@ -260,10 +261,10 @@ namespace osu.Game.Screens.Multi.Match { default: case GameTypeTimeshift _: - multiplayer?.Start(() => new TimeshiftPlayer(SelectedItem.Value) + multiplayer?.Push(new PlayerLoader(() => new TimeshiftPlayer(SelectedItem.Value) { Exited = () => leaderboardChatDisplay.RefreshScores() - }); + })); break; } } @@ -271,8 +272,7 @@ namespace osu.Game.Screens.Multi.Match private void showBeatmapResults() { Debug.Assert(roomId.Value != null); - - this.Push(new TimeshiftResultsScreen(null, roomId.Value.Value, SelectedItem.Value, false)); + multiplayer?.Push(new TimeshiftResultsScreen(null, roomId.Value.Value, SelectedItem.Value, false)); } } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 863a28609b..e724152e08 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -24,7 +23,6 @@ using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; using osu.Game.Screens.Multi.Match.Components; -using osu.Game.Screens.Play; using osuTK; namespace osu.Game.Screens.Multi @@ -197,18 +195,6 @@ namespace osu.Game.Screens.Multi Logger.Log($"Polling adjusted (listing: {roomManager.TimeBetweenListingPolls}, selection: {roomManager.TimeBetweenSelectionPolls})"); } - /// - /// Push a to the main screen stack to begin gameplay. - /// Generally called from a via DI resolution. - /// - public void Start(Func player) - { - if (!this.IsCurrentScreen()) - return; - - this.Push(new PlayerLoader(player)); - } - public void APIStateChanged(IAPIProvider api, APIState state) { if (state != APIState.Online) From f3b514964894fd38c69cff189d59f71b04b2fe82 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 16:48:44 +0900 Subject: [PATCH 20/65] Move some suggestions to warnings, resolve issues --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 2 +- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 3 +-- .../Drawables/Connections/FollowPointConnection.cs | 3 +-- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 4 ++-- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 7 ++----- osu.Game.Tournament.Tests/LadderTestScene.cs | 3 +-- osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs | 3 +-- osu.Game.Tournament/TournamentGameBase.cs | 7 ++----- osu.Game/Beatmaps/BeatmapManager.cs | 3 +-- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 3 +-- osu.Game/Online/Chat/ChannelManager.cs | 9 +++------ osu.Game/Online/Chat/StandAloneChatDisplay.cs | 3 +-- osu.Game/OsuGameBase.cs | 9 ++++----- osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs | 3 +-- osu.Game/Rulesets/Objects/HitObject.cs | 3 +-- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 3 +-- osu.Game/Scoring/ScoreInfo.cs | 8 ++------ osu.Game/Screens/Edit/Timing/Section.cs | 2 +- .../Screens/Multi/Lounge/Components/FilterControl.cs | 3 +-- osu.Game/Screens/Select/BeatmapCarousel.cs | 5 +---- osu.Game/Users/Drawables/DrawableAvatar.cs | 2 +- osu.sln.DotSettings | 4 ++++ 22 files changed, 34 insertions(+), 58 deletions(-) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index ade8460dd7..dd50b05c75 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -48,7 +48,7 @@ namespace osu.Desktop.Updater try { - if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); + updateManager ??= await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); var info = await updateManager.CheckForUpdate(!useDeltaPatching); if (info.ReleasesToApply.Count == 0) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 5cd2f1f581..918ed77683 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -35,8 +35,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills { var catchCurrent = (CatchDifficultyHitObject)current; - if (lastPlayerPosition == null) - lastPlayerPosition = catchCurrent.LastNormalizedPosition; + lastPlayerPosition ??= catchCurrent.LastNormalizedPosition; float playerPosition = Math.Clamp( lastPlayerPosition.Value, diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 8a0ef22c4a..2c41e6b0e9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -135,8 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections fp.Alpha = 0; fp.Scale = new Vector2(1.5f * osuEnd.Scale); - if (firstTransformStartTime == null) - firstTransformStartTime = fadeInTime; + firstTransformStartTime ??= fadeInTime; fp.AnimationStartTime = fadeInTime; diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 5eb11a3264..195fec6278 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -175,7 +175,7 @@ namespace osu.Game.Tests.Beatmaps.IO var breakTemp = TestResources.GetTestBeatmapForImport(); MemoryStream brokenOsu = new MemoryStream(); - MemoryStream brokenOsz = new MemoryStream(File.ReadAllBytes(breakTemp)); + MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(breakTemp)); File.Delete(breakTemp); @@ -522,7 +522,7 @@ namespace osu.Game.Tests.Beatmaps.IO using (var resourceForkFile = File.CreateText(resourceForkFilePath)) { - resourceForkFile.WriteLine("adding content so that it's not empty"); + await resourceForkFile.WriteLineAsync("adding content so that it's not empty"); } try diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index 90bf419644..57f0d7e957 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -183,11 +183,8 @@ namespace osu.Game.Tests.Scores.IO { var beatmapManager = osu.Dependencies.Get(); - if (score.Beatmap == null) - score.Beatmap = beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); - - if (score.Ruleset == null) - score.Ruleset = new OsuRuleset().RulesetInfo; + score.Beatmap ??= beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps.First(); + score.Ruleset ??= new OsuRuleset().RulesetInfo; var scoreManager = osu.Dependencies.Get(); await scoreManager.Import(score, archive); diff --git a/osu.Game.Tournament.Tests/LadderTestScene.cs b/osu.Game.Tournament.Tests/LadderTestScene.cs index b962d035ab..2f4373679c 100644 --- a/osu.Game.Tournament.Tests/LadderTestScene.cs +++ b/osu.Game.Tournament.Tests/LadderTestScene.cs @@ -24,8 +24,7 @@ namespace osu.Game.Tournament.Tests [BackgroundDependencyLoader] private void load() { - if (Ladder.Ruleset.Value == null) - Ladder.Ruleset.Value = rulesetStore.AvailableRulesets.First(); + Ladder.Ruleset.Value ??= rulesetStore.AvailableRulesets.First(); Ruleset.BindTo(Ladder.Ruleset); } diff --git a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs index 8be66ff98c..e10154b722 100644 --- a/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs +++ b/osu.Game.Tournament/Screens/Drawings/DrawingsScreen.cs @@ -47,8 +47,7 @@ namespace osu.Game.Tournament.Screens.Drawings this.storage = storage; - if (TeamList == null) - TeamList = new StorageBackedTeamList(storage); + TeamList ??= new StorageBackedTeamList(storage); if (!TeamList.Teams.Any()) { diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 85db9e61fb..928c6deb3c 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -150,11 +150,8 @@ namespace osu.Game.Tournament ladder = JsonConvert.DeserializeObject(sr.ReadToEnd()); } - if (ladder == null) - ladder = new LadderInfo(); - - if (ladder.Ruleset.Value == null) - ladder.Ruleset.Value = RulesetStore.AvailableRulesets.First(); + ladder ??= new LadderInfo(); + ladder.Ruleset.Value ??= RulesetStore.AvailableRulesets.First(); Ruleset.BindTo(ladder.Ruleset); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f626b45e42..0785f9ef0e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -239,8 +239,7 @@ namespace osu.Game.Beatmaps if (working == null) { - if (beatmapInfo.Metadata == null) - beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; + beatmapInfo.Metadata ??= beatmapInfo.BeatmapSet.Metadata; workingCache.Add(working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager)); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 388abf4648..be5cd78dc8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -425,8 +425,7 @@ namespace osu.Game.Beatmaps.Formats private void handleHitObject(string line) { // If the ruleset wasn't specified, assume the osu!standard ruleset. - if (parser == null) - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion); + parser ??= new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion); var obj = parser.Parse(line); if (obj != null) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 53872ddcba..90d4c2a03b 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -114,8 +114,7 @@ namespace osu.Game.Online.Chat /// An optional target channel. If null, will be used. public void PostMessage(string text, bool isAction = false, Channel target = null) { - if (target == null) - target = CurrentChannel.Value; + target ??= CurrentChannel.Value; if (target == null) return; @@ -198,8 +197,7 @@ namespace osu.Game.Online.Chat /// An optional target channel. If null, will be used. public void PostCommand(string text, Channel target = null) { - if (target == null) - target = CurrentChannel.Value; + target ??= CurrentChannel.Value; if (target == null) return; @@ -378,8 +376,7 @@ namespace osu.Game.Online.Chat } } - if (CurrentChannel.Value == null) - CurrentChannel.Value = channel; + CurrentChannel.Value ??= channel; return channel; } diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 4fbeac1db9..f8810c778f 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -73,8 +73,7 @@ namespace osu.Game.Online.Chat [BackgroundDependencyLoader(true)] private void load(ChannelManager manager) { - if (ChannelManager == null) - ChannelManager = manager; + ChannelManager ??= manager; } protected virtual StandAloneDrawableChannel CreateDrawableChannel(Channel channel) => diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 5e44562144..3e7311092e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -164,7 +164,7 @@ namespace osu.Game dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore(Resources, "Skins/Legacy"))); dependencies.CacheAs(SkinManager); - if (API == null) API = new APIAccess(LocalConfig); + API ??= new APIAccess(LocalConfig); dependencies.CacheAs(API); @@ -311,11 +311,10 @@ namespace osu.Game { base.SetHost(host); - if (Storage == null) // may be non-null for certain tests - Storage = new OsuStorage(host); + // may be non-null for certain tests + Storage ??= new OsuStorage(host); - if (LocalConfig == null) - LocalConfig = new OsuConfigManager(Storage); + LocalConfig ??= new OsuConfigManager(Storage); } private readonly List fileImporters = new List(); diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index a72f182450..cb6abb7cc6 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -68,8 +68,7 @@ namespace osu.Game.Overlays.Chat.Tabs if (!Items.Contains(channel)) AddItem(channel); - if (Current.Value == null) - Current.Value = channel; + Current.Value ??= channel; } /// diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index e2cc98813a..1d60b266e3 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -133,8 +133,7 @@ namespace osu.Game.Rulesets.Objects { Kiai = controlPointInfo.EffectPointAt(StartTime + control_point_leniency).KiaiMode; - if (HitWindows == null) - HitWindows = CreateHitWindows(); + HitWindows ??= CreateHitWindows(); HitWindows?.SetDifficulty(difficulty.OverallDifficulty); } diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index bc9401a095..d574991fa0 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -181,8 +181,7 @@ namespace osu.Game.Rulesets.UI private void setClock() { // in case a parent gameplay clock isn't available, just use the parent clock. - if (parentGameplayClock == null) - parentGameplayClock = Clock; + parentGameplayClock ??= Clock; Clock = GameplayClock; ProcessCustomClock = false; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index a40f436a6e..7b37c267bc 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -115,9 +115,7 @@ namespace osu.Game.Scoring get => User?.Username; set { - if (User == null) - User = new User(); - + User ??= new User(); User.Username = value; } } @@ -129,9 +127,7 @@ namespace osu.Game.Scoring get => User?.Id ?? 1; set { - if (User == null) - User = new User(); - + User ??= new User(); User.Id = value ?? 1; } } diff --git a/osu.Game/Screens/Edit/Timing/Section.cs b/osu.Game/Screens/Edit/Timing/Section.cs index ccf1582486..603fb77f31 100644 --- a/osu.Game/Screens/Edit/Timing/Section.cs +++ b/osu.Game/Screens/Edit/Timing/Section.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Edit.Timing { checkbox = new OsuCheckbox { - LabelText = typeof(T).Name.Replace(typeof(ControlPoint).Name, string.Empty) + LabelText = typeof(T).Name.Replace(nameof(Beatmaps.ControlPoints.ControlPoint), string.Empty) } } }, diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs index 300418441e..2742ef3404 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs @@ -34,8 +34,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components [BackgroundDependencyLoader] private void load() { - if (filter == null) - filter = new Bindable(); + filter ??= new Bindable(); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 2d714d1794..e174c46610 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -607,10 +607,7 @@ namespace osu.Game.Screens.Select // todo: remove the need for this. foreach (var b in beatmapSet.Beatmaps) - { - if (b.Metadata == null) - b.Metadata = beatmapSet.Metadata; - } + b.Metadata ??= beatmapSet.Metadata; var set = new CarouselBeatmapSet(beatmapSet) { diff --git a/osu.Game/Users/Drawables/DrawableAvatar.cs b/osu.Game/Users/Drawables/DrawableAvatar.cs index 09750c5bfe..42d2dbb1c6 100644 --- a/osu.Game/Users/Drawables/DrawableAvatar.cs +++ b/osu.Game/Users/Drawables/DrawableAvatar.cs @@ -43,7 +43,7 @@ namespace osu.Game.Users.Drawables Texture texture = null; if (user != null && user.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); - if (texture == null) texture = textures.Get(@"Online/avatar-guest"); + texture ??= textures.Get(@"Online/avatar-guest"); ClickableArea clickableArea; Add(clickableArea = new ClickableArea diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index b9fc3de734..6e8ecb42d6 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -60,6 +60,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -105,6 +106,8 @@ HINT WARNING WARNING + WARNING + WARNING WARNING WARNING WARNING @@ -222,6 +225,7 @@ WARNING WARNING WARNING + WARNING True WARNING From 8aa8d2c88011adafa5b84ac0adaa7ff88eddda35 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 16:59:37 +0900 Subject: [PATCH 21/65] Resolve NREs --- osu.Desktop/OsuGameDesktop.cs | 2 +- osu.Game.Tests/Chat/MessageFormatterTests.cs | 10 +++++----- .../Visual/UserInterface/TestSceneOsuIcon.cs | 2 +- .../Converters/TypedListConverter.cs | 18 +++++++++++++++--- osu.Game/Online/API/APIAccess.cs | 2 +- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 9351e17419..5f74883803 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -59,7 +59,7 @@ namespace osu.Desktop try { using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu")) - stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", ""); + stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString()?.Split('"')[1].Replace("osu!.exe", ""); if (checkExists(stableInstallPath)) return stableInstallPath; diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index fbb0416c45..d1a859c84b 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -428,23 +428,23 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(5, result.Links.Count); Link f = result.Links.Find(l => l.Url == "https://osu.ppy.sh/wiki/wiki links"); - Assert.AreEqual(44, f.Index); + Assert.AreEqual(44, f!.Index); Assert.AreEqual(10, f.Length); f = result.Links.Find(l => l.Url == "http://www.simple-test.com"); - Assert.AreEqual(10, f.Index); + Assert.AreEqual(10, f!.Index); Assert.AreEqual(11, f.Length); f = result.Links.Find(l => l.Url == "http://google.com"); - Assert.AreEqual(97, f.Index); + Assert.AreEqual(97, f!.Index); Assert.AreEqual(4, f.Length); f = result.Links.Find(l => l.Url == "https://osu.ppy.sh"); - Assert.AreEqual(78, f.Index); + Assert.AreEqual(78, f!.Index); Assert.AreEqual(18, f.Length); f = result.Links.Find(l => l.Url == "\uD83D\uDE12"); - Assert.AreEqual(101, f.Index); + Assert.AreEqual(101, f!.Index); Assert.AreEqual(3, f.Length); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs index 061039b297..246eb119e8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.UserInterface }); foreach (var p in typeof(OsuIcon).GetProperties(BindingFlags.Public | BindingFlags.Static)) - flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)p.GetValue(null))); + flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)p.GetValue(null)!)); AddStep("toggle shadows", () => flow.Children.ForEach(i => i.SpriteIcon.Shadow = !i.SpriteIcon.Shadow)); AddStep("change icons", () => flow.Children.ForEach(i => i.SpriteIcon.Icon = new IconUsage((char)(i.SpriteIcon.Icon.Icon + 1)))); diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index f98fa05821..ddfdf08f52 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -41,13 +41,25 @@ namespace osu.Game.IO.Serialization.Converters var list = new List(); var obj = JObject.Load(reader); - var lookupTable = serializer.Deserialize>(obj["$lookup_table"].CreateReader()); - foreach (var tok in obj["$items"]) + if (!obj.TryGetValue("$lookup_table", out var lookupTableToken) || lookupTableToken == null) + return list; + + var lookupTable = serializer.Deserialize>(lookupTableToken.CreateReader()); + if (lookupTable == null) + return list; + + if (!obj.TryGetValue("$items", out var itemsToken) || itemsToken == null) + return list; + + foreach (var tok in itemsToken) { var itemReader = tok.CreateReader(); - var typeName = lookupTable[(int)tok["$type"]]; + if (!obj.TryGetValue("$type", out var typeToken) || typeToken == null) + throw new JsonException("Expected $type token."); + + var typeName = lookupTable[(int)typeToken]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); serializer.Populate(itemReader, instance); diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 4945f7f185..f9e2da9af8 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -250,7 +250,7 @@ namespace osu.Game.Online.API { try { - return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true).ToObject(); + return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true)!.ToObject(); } catch { From 092f5b6521c1617d363d9c953a01438e4b38607e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 17:41:05 +0900 Subject: [PATCH 22/65] Fix incorrect reference + simplify --- .../Serialization/Converters/TypedListConverter.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index ddfdf08f52..50b28ea74b 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -42,24 +42,24 @@ namespace osu.Game.IO.Serialization.Converters var obj = JObject.Load(reader); - if (!obj.TryGetValue("$lookup_table", out var lookupTableToken) || lookupTableToken == null) + if (obj["$lookup_table"] == null) return list; - var lookupTable = serializer.Deserialize>(lookupTableToken.CreateReader()); + var lookupTable = serializer.Deserialize>(obj["$lookup_table"].CreateReader()); if (lookupTable == null) return list; - if (!obj.TryGetValue("$items", out var itemsToken) || itemsToken == null) + if (obj["$items"] == null) return list; - foreach (var tok in itemsToken) + foreach (var tok in obj["$items"]) { var itemReader = tok.CreateReader(); - if (!obj.TryGetValue("$type", out var typeToken) || typeToken == null) + if (tok["$type"] == null) throw new JsonException("Expected $type token."); - var typeName = lookupTable[(int)typeToken]; + var typeName = lookupTable[(int)tok["$type"]]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName)); serializer.Populate(itemReader, instance); From c0881e14ab176ac500e9308607b37f1368ac8d78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 17:44:14 +0900 Subject: [PATCH 23/65] Add "struct can be made readonly" inspection --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 6e8ecb42d6..85be2077be 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -199,6 +199,7 @@ WARNING WARNING HINT + WARNING DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW From 6133c7d74784d0f848add452953ebf56aa39c0a2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 18:51:02 +0900 Subject: [PATCH 24/65] Change suggestion to warning --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 85be2077be..85d5fce29a 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -141,6 +141,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING From 9920911390833ffb35fddffaaa62803ebf92ecd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jun 2020 17:20:08 +0900 Subject: [PATCH 25/65] Fix tournament displayed beatmap potentially being out of order on quick changes --- osu.Game.Tournament/IPC/FileBasedIPC.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 53ba597a7e..de4d482d13 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -34,6 +34,7 @@ namespace osu.Game.Tournament.IPC private int lastBeatmapId; private ScheduledDelegate scheduled; + private GetBeatmapRequest beatmapLookupRequest; public Storage Storage { get; private set; } @@ -77,6 +78,8 @@ namespace osu.Game.Tournament.IPC if (lastBeatmapId != beatmapId) { + beatmapLookupRequest?.Cancel(); + lastBeatmapId = beatmapId; var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.BeatmapInfo != null); @@ -85,9 +88,9 @@ namespace osu.Game.Tournament.IPC Beatmap.Value = existing.BeatmapInfo; else { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); - req.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); - API.Queue(req); + beatmapLookupRequest = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); + beatmapLookupRequest.Success += b => Beatmap.Value = b.ToBeatmap(Rulesets); + API.Queue(beatmapLookupRequest); } } From 6b88141e58b6d3863b1aeb9db41d39225cd00bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 21:30:59 +0200 Subject: [PATCH 26/65] Add mania sample conversion test --- .../ManiaBeatmapSampleConversionTest.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs new file mode 100644 index 0000000000..dbf1cf5f72 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . 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.Utils; +using osu.Game.Audio; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class ManiaBeatmapSampleConversionTest : BeatmapConversionTest, SampleConvertValue> + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + + public void Test(string name) => base.Test(name); + + protected override IEnumerable CreateConvertValue(HitObject hitObject) + { + yield return new SampleConvertValue + { + StartTime = hitObject.StartTime, + EndTime = hitObject.GetEndTime(), + Column = ((ManiaHitObject)hitObject).Column, + NodeSamples = getSampleNames((hitObject as HoldNote)?.NodeSamples) + }; + } + + private IList> getSampleNames(List> hitSampleInfo) + => hitSampleInfo?.Select(samples => + (IList)samples.Select(sample => sample.LookupNames.First()).ToList()) + .ToList(); + + protected override Ruleset CreateRuleset() => new ManiaRuleset(); + } + + public struct SampleConvertValue : IEquatable + { + /// + /// A sane value to account for osu!stable using ints everywhere. + /// + private const float conversion_lenience = 2; + + public double StartTime; + public double EndTime; + public int Column; + public IList> NodeSamples; + + public bool Equals(SampleConvertValue other) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) + && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) + && samplesEqual(NodeSamples, other.NodeSamples); + + private static bool samplesEqual(ICollection> first, ICollection> second) + { + if (first == null && second == null) + return true; + + // both items can't be null now, so if any single one is, then they're not equal + if (first == null || second == null) + return false; + + return first.Count == second.Count + && first.Zip(second).All(samples => samples.First.SequenceEqual(samples.Second)); + } + } +} From 35544ede50069851ff7cfa0fecdf141fe94345db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 21:54:19 +0200 Subject: [PATCH 27/65] Add failing test cases --- .../ManiaBeatmapSampleConversionTest.cs | 2 ++ .../convert-samples-expected-conversion.json | 30 +++++++++++++++++++ .../Testing/Beatmaps/convert-samples.osu | 16 ++++++++++ .../mania-samples-expected-conversion.json | 25 ++++++++++++++++ .../Testing/Beatmaps/mania-samples.osu | 19 ++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs index dbf1cf5f72..2f6918d263 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Mania.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + [TestCase("convert-samples")] + [TestCase("mania-samples")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json new file mode 100644 index 0000000000..b8ce85eef5 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json @@ -0,0 +1,30 @@ +{ + "Mappings": [{ + "StartTime": 1000.0, + "Objects": [{ + "StartTime": 1000.0, + "EndTime": 2750.0, + "Column": 1, + "NodeSamples": [ + ["normal-hitnormal"], + ["soft-hitnormal"], + ["drum-hitnormal"] + ] + }, { + "StartTime": 1875.0, + "EndTime": 2750.0, + "Column": 0, + "NodeSamples": [ + ["soft-hitnormal"], + ["drum-hitnormal"] + ] + }] + }, { + "StartTime": 3750.0, + "Objects": [{ + "StartTime": 3750.0, + "EndTime": 3750.0, + "Column": 3 + }] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu new file mode 100644 index 0000000000..16b73992d2 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu @@ -0,0 +1,16 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:5 +CircleSize:5 +OverallDifficulty:5 +ApproachRate:5 +SliderMultiplier:1.4 +SliderTickRate:1 + +[TimingPoints] +0,500,4,1,0,100,1,0 + +[HitObjects] +88,99,1000,6,0,L|306:259,2,245,0|0|0,1:0|2:0|3:0,0:0:0:0: +259,118,3750,1,0,0:0:0:0: diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json new file mode 100644 index 0000000000..e22540614d --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json @@ -0,0 +1,25 @@ +{ + "Mappings": [{ + "StartTime": 500.0, + "Objects": [{ + "StartTime": 500.0, + "EndTime": 1500.0, + "Column": 0, + "NodeSamples": [ + ["normal-hitnormal"], + [] + ] + }] + }, { + "StartTime": 2000.0, + "Objects": [{ + "StartTime": 2000.0, + "EndTime": 3000.0, + "Column": 2, + "NodeSamples": [ + ["drum-hitnormal"], + [] + ] + }] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu new file mode 100644 index 0000000000..7c75b45e5f --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu @@ -0,0 +1,19 @@ +osu file format v14 + +[General] +Mode: 3 + +[Difficulty] +HPDrainRate:5 +CircleSize:5 +OverallDifficulty:5 +ApproachRate:5 +SliderMultiplier:1.4 +SliderTickRate:1 + +[TimingPoints] +0,500,4,1,0,100,1,0 + +[HitObjects] +51,192,500,128,0,1500:1:0:0:0: +256,192,2000,128,0,3000:3:0:0:0: From ac019bddd61b798f86c0f9e545a5d1e45de5a746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 22:28:55 +0200 Subject: [PATCH 28/65] Only play samples at start of hold note in mania maps --- .../Beatmaps/ManiaBeatmapConverter.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 32abf5e7f9..b025ac7992 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using System.Collections.Generic; using osu.Framework.Utils; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -239,7 +240,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Duration = endTimeData.Duration, Column = column, Samples = HitObject.Samples, - NodeSamples = (HitObject as IHasRepeats)?.NodeSamples + NodeSamples = (HitObject as IHasRepeats)?.NodeSamples ?? defaultNodeSamples }); } else if (HitObject is IHasXPosition) @@ -254,6 +255,16 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return pattern; } + + /// + /// osu!mania-specific beatmaps in stable only play samples at the start of the hold note. + /// + private List> defaultNodeSamples + => new List> + { + HitObject.Samples, + new List() + }; } } } From c4cae006aa800e78f29b46dfcdccda0220838a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 22:47:14 +0200 Subject: [PATCH 29/65] Correctly slice node sample list when converting --- .../Legacy/DistanceObjectPatternGenerator.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 1bd796511b..b49b881656 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -472,15 +472,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// The time to retrieve the sample info list from. /// - private IList sampleInfoListAt(double time) + private IList sampleInfoListAt(double time) => nodeSamplesAt(time)?.First() ?? HitObject.Samples; + + /// + /// Retrieves the list of node samples that occur at time greater than or equal to . + /// + /// The time to retrieve node samples at. + private IEnumerable> nodeSamplesAt(double time) { if (!(HitObject is IHasPathWithRepeats curveData)) - return HitObject.Samples; + return null; double segmentTime = (EndTime - HitObject.StartTime) / spanCount; int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); - return curveData.NodeSamples[index]; + return curveData.NodeSamples.Skip(index); } /// @@ -511,7 +517,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy Duration = endTime - startTime, Column = column, Samples = HitObject.Samples, - NodeSamples = (HitObject as IHasRepeats)?.NodeSamples + NodeSamples = nodeSamplesAt(startTime)?.ToList() }; } From 4c6116e6e7c9aa1300430954ef4e6cbcc793f39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 Jun 2020 23:50:58 +0200 Subject: [PATCH 30/65] Fix compilation failure in Android test project --- .../ManiaBeatmapSampleConversionTest.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs index 2f6918d263..d8f87195d1 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs @@ -58,17 +58,19 @@ namespace osu.Game.Rulesets.Mania.Tests && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) && samplesEqual(NodeSamples, other.NodeSamples); - private static bool samplesEqual(ICollection> first, ICollection> second) + private static bool samplesEqual(ICollection> firstSampleList, ICollection> secondSampleList) { - if (first == null && second == null) + if (firstSampleList == null && secondSampleList == null) return true; // both items can't be null now, so if any single one is, then they're not equal - if (first == null || second == null) + if (firstSampleList == null || secondSampleList == null) return false; - return first.Count == second.Count - && first.Zip(second).All(samples => samples.First.SequenceEqual(samples.Second)); + return firstSampleList.Count == secondSampleList.Count + // cannot use .Zip() without the selector function as it doesn't compile in android test project + && firstSampleList.Zip(secondSampleList, (first, second) => (first, second)) + .All(samples => samples.first.SequenceEqual(samples.second)); } } } From 896177801a57e0bd2161309d05775be4d0d087bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 5 Jun 2020 00:07:27 +0200 Subject: [PATCH 31/65] Avoid creating copies of node samples every time --- .../Patterns/Legacy/DistanceObjectPatternGenerator.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index b49b881656..9fbdf58e21 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -478,7 +478,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// Retrieves the list of node samples that occur at time greater than or equal to . /// /// The time to retrieve node samples at. - private IEnumerable> nodeSamplesAt(double time) + private List> nodeSamplesAt(double time) { if (!(HitObject is IHasPathWithRepeats curveData)) return null; @@ -486,7 +486,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy double segmentTime = (EndTime - HitObject.StartTime) / spanCount; int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); - return curveData.NodeSamples.Skip(index); + + // avoid slicing the list & creating copies, if at all possible. + return index == 0 ? curveData.NodeSamples : curveData.NodeSamples.Skip(index).ToList(); } /// @@ -517,7 +519,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy Duration = endTime - startTime, Column = column, Samples = HitObject.Samples, - NodeSamples = nodeSamplesAt(startTime)?.ToList() + NodeSamples = nodeSamplesAt(startTime) }; } From 0107e9ba16deb94cd8f04c5dcf36b7ca2a781adc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Jun 2020 19:18:00 +0900 Subject: [PATCH 32/65] Change lookups to use SingleOrDefault() --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 668ac6ee10..1f92d5461f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -215,7 +215,7 @@ namespace osu.Game.Beatmaps foreach (var info in item.Beatmaps) { - var file = item.Files.FirstOrDefault(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + var file = item.Files.SingleOrDefault(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; using (var stream = Files.Store.GetStream(file)) info.MD5Hash = stream.ComputeMD5Hash(); diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index e62a9bb39d..39c5ccab27 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -42,7 +42,7 @@ namespace osu.Game.Beatmaps } } - private string getPathForFile(string filename) => BeatmapSetInfo.Files.FirstOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + private string getPathForFile(string filename) => BeatmapSetInfo.Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; private TextureStore textureStore; From bb89114b70eb820096ae3dc1b371c0d3ce8c05c7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Jun 2020 20:52:27 +0900 Subject: [PATCH 33/65] Show a loading spinner on multiplayer lounge loads --- .../TestSceneLoungeRoomsContainer.cs | 3 ++ .../TestSceneMatchSettingsOverlay.cs | 2 ++ .../Multiplayer/TestSceneMatchSubScreen.cs | 2 ++ osu.Game/Screens/Multi/IRoomManager.cs | 5 +++ .../Screens/Multi/Lounge/LoungeSubScreen.cs | 33 +++++++++++++++++-- osu.Game/Screens/Multi/RoomManager.cs | 14 +++++++- 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 77b41c89b0..83f2297bd2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -141,6 +141,9 @@ namespace osu.Game.Tests.Visual.Multiplayer } public readonly BindableList Rooms = new BindableList(); + + public Bindable InitialRoomsReceived { get; } = new Bindable(true); + IBindableList IRoomManager.Rooms => Rooms; public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => Rooms.Add(room); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs index 34c6940552..fdc20dc477 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSettingsOverlay.cs @@ -133,6 +133,8 @@ namespace osu.Game.Tests.Visual.Multiplayer remove { } } + public Bindable InitialRoomsReceived { get; } = new Bindable(true); + public IBindableList Rooms { get; } = null; public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs index d678d5a814..9d0c159549 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSubScreen.cs @@ -93,6 +93,8 @@ namespace osu.Game.Tests.Visual.Multiplayer remove => throw new NotImplementedException(); } + public Bindable InitialRoomsReceived { get; } = new Bindable(true); + public IBindableList Rooms { get; } = new BindableList(); public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index f6c979851e..bf75843c3e 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -14,6 +14,11 @@ namespace osu.Game.Screens.Multi /// event Action RoomsUpdated; + /// + /// Whether an initial listing of rooms has been received. + /// + Bindable InitialRoomsReceived { get; } + /// /// All the active s. /// diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 7c10f0f975..d4b6a3b79f 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -22,12 +22,16 @@ namespace osu.Game.Screens.Multi.Lounge protected readonly FilterControl Filter; + private readonly Bindable initialRoomsReceived = new Bindable(); + private readonly Container content; private readonly LoadingLayer loadingLayer; [Resolved] private Bindable selectedRoom { get; set; } + private bool joiningRoom; + public LoungeSubScreen() { SearchContainer searchContainer; @@ -73,6 +77,14 @@ namespace osu.Game.Screens.Multi.Lounge }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived); + initialRoomsReceived.BindValueChanged(onInitialRoomsReceivedChanged, true); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -126,12 +138,29 @@ namespace osu.Game.Screens.Multi.Lounge private void joinRequested(Room room) { - loadingLayer.Show(); + joiningRoom = true; + updateLoadingLayer(); + RoomManager?.JoinRoom(room, r => { Open(room); + joiningRoom = false; + updateLoadingLayer(); + }, _ => + { + joiningRoom = false; + updateLoadingLayer(); + }); + } + + private void onInitialRoomsReceivedChanged(ValueChangedEvent received) => updateLoadingLayer(); + + private void updateLoadingLayer() + { + if (joiningRoom || !initialRoomsReceived.Value) + loadingLayer.Show(); + else loadingLayer.Hide(); - }, _ => loadingLayer.Hide()); } /// diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index ad461af57f..4d6ac46c84 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -25,6 +25,9 @@ namespace osu.Game.Screens.Multi public event Action RoomsUpdated; private readonly BindableList rooms = new BindableList(); + + public Bindable InitialRoomsReceived { get; } = new Bindable(); + public IBindableList Rooms => rooms; public double TimeBetweenListingPolls @@ -62,7 +65,11 @@ namespace osu.Game.Screens.Multi InternalChildren = new Drawable[] { - listingPollingComponent = new ListingPollingComponent { RoomsReceived = onListingReceived }, + listingPollingComponent = new ListingPollingComponent + { + InitialRoomsReceived = { BindTarget = InitialRoomsReceived }, + RoomsReceived = onListingReceived + }, selectionPollingComponent = new SelectionPollingComponent { RoomReceived = onSelectedRoomReceived } }; } @@ -262,6 +269,8 @@ namespace osu.Game.Screens.Multi { public Action> RoomsReceived; + public readonly Bindable InitialRoomsReceived = new Bindable(); + [Resolved] private IAPIProvider api { get; set; } @@ -273,6 +282,8 @@ namespace osu.Game.Screens.Multi { currentFilter.BindValueChanged(_ => { + InitialRoomsReceived.Value = false; + if (IsLoaded) PollImmediately(); }); @@ -292,6 +303,7 @@ namespace osu.Game.Screens.Multi pollReq.Success += result => { + InitialRoomsReceived.Value = true; RoomsReceived?.Invoke(result); tcs.SetResult(true); }; From 72ada020a2515a1ed839fecbeb0af52c3ce86abc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jun 2020 13:42:16 +0900 Subject: [PATCH 34/65] Don't attempt to use virtual track for intro sequence clock --- osu.Game/Screens/Menu/IntroTriangles.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index 188a49c147..cb05dcc932 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -7,6 +7,7 @@ using System.IO; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Audio.Track; using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -61,7 +62,7 @@ namespace osu.Game.Screens.Menu LoadComponentAsync(new TrianglesIntroSequence(logo, background) { RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(MenuMusic.Value ? Track : null), + Clock = new FramedClock(MenuMusic.Value && !(Track is TrackVirtual) ? Track : null), LoadMenu = LoadMenu }, t => { From dfed27bd4633e5b2d1268f8851fec97a698d61e6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 8 Jun 2020 14:24:21 +0900 Subject: [PATCH 35/65] Add back stream seeking for sanity --- osu.Game/Beatmaps/BeatmapManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f11e94e63d..4e3714a582 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -201,6 +201,8 @@ namespace osu.Game.Beatmaps using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) new LegacyBeatmapEncoder(beatmapContent).Encode(sw); + stream.Seek(0, SeekOrigin.Begin); + UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } From 443977aa8d71071a7566a4be643ffea72b77fee1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 8 Jun 2020 14:40:17 +0900 Subject: [PATCH 36/65] Remove PreUpdate, update hash in Save() --- osu.Game/Beatmaps/BeatmapManager.cs | 22 ++++++++-------------- osu.Game/Database/ArchiveModelManager.cs | 11 ----------- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 4e3714a582..cbcdf51551 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -203,7 +203,14 @@ namespace osu.Game.Beatmaps stream.Seek(0, SeekOrigin.Begin); - UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); + using (ContextFactory.GetForWrite()) + { + var beatmapInfo = setInfo.Beatmaps.Single(b => b.ID == info.ID); + beatmapInfo.MD5Hash = stream.ComputeMD5Hash(); + + stream.Seek(0, SeekOrigin.Begin); + UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); + } } var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); @@ -211,19 +218,6 @@ namespace osu.Game.Beatmaps workingCache.Remove(working); } - protected override void PreUpdate(BeatmapSetInfo item) - { - base.PreUpdate(item); - - foreach (var info in item.Beatmaps) - { - var file = item.Files.SingleOrDefault(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; - - using (var stream = Files.Store.GetStream(file)) - info.MD5Hash = stream.ComputeMD5Hash(); - } - } - private readonly WeakList workingCache = new WeakList(); /// diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index b9479af623..915d980d24 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -429,21 +429,10 @@ namespace osu.Game.Database using (ContextFactory.GetForWrite()) { item.Hash = computeHash(item); - - PreUpdate(item); - ModelStore.Update(item); } } - /// - /// Perform any final actions before the update to database executes. - /// - /// The that is being updated. - protected virtual void PreUpdate(TModel item) - { - } - /// /// Delete an item from the manager. /// Is a no-op for already deleted items. From 63003757c4fee77ca055861cb0538019509138a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jun 2020 14:48:26 +0900 Subject: [PATCH 37/65] Remove WorkingBeatmap cache when deleting or updating a beatmap --- osu.Game/Beatmaps/BeatmapManager.cs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e7cef13c68..73e4c119e4 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -79,6 +79,8 @@ namespace osu.Game.Beatmaps beatmaps = (BeatmapStore)ModelStore; beatmaps.BeatmapHidden += b => beatmapHidden.Value = new WeakReference(b); beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference(b); + beatmaps.ItemRemoved += removeWorkingCache; + beatmaps.ItemUpdated += removeWorkingCache; onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage); } @@ -206,9 +208,7 @@ namespace osu.Game.Beatmaps UpdateFile(setInfo, setInfo.Files.Single(f => string.Equals(f.Filename, info.Path, StringComparison.OrdinalIgnoreCase)), stream); } - var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); - if (working != null) - workingCache.Remove(working); + removeWorkingCache(info); } private readonly WeakList workingCache = new WeakList(); @@ -410,6 +410,24 @@ namespace osu.Game.Beatmaps return endTime - startTime; } + private void removeWorkingCache(BeatmapSetInfo info) + { + if (info.Beatmaps == null) return; + + foreach (var b in info.Beatmaps) + removeWorkingCache(b); + } + + private void removeWorkingCache(BeatmapInfo info) + { + lock (workingCache) + { + var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == info.ID); + if (working != null) + workingCache.Remove(working); + } + } + public void Dispose() { onlineLookupQueue?.Dispose(); From dd61d6ed04f47aa77739e974b29949a898d79c74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jun 2020 14:48:42 +0900 Subject: [PATCH 38/65] Attempt to reimport intro if a bad state is detected --- osu.Game/Screens/Menu/IntroScreen.cs | 62 ++++++++++++++++--------- osu.Game/Screens/Menu/IntroTriangles.cs | 5 +- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 0d5f3d1142..b99d8ae9d1 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -41,9 +41,9 @@ namespace osu.Game.Screens.Menu protected IBindable MenuMusic { get; private set; } - private WorkingBeatmap introBeatmap; + private WorkingBeatmap initialBeatmap; - protected Track Track { get; private set; } + protected Track Track => initialBeatmap?.Track; private readonly BindableDouble exitingVolumeFade = new BindableDouble(1); @@ -58,6 +58,11 @@ namespace osu.Game.Screens.Menu [Resolved] private AudioManager audio { get; set; } + /// + /// Whether the is provided by osu! resources, rather than a user beatmap. + /// + protected bool UsingThemedIntro { get; private set; } + [BackgroundDependencyLoader] private void load(OsuConfigManager config, SkinManager skinManager, BeatmapManager beatmaps, Framework.Game game) { @@ -71,29 +76,45 @@ namespace osu.Game.Screens.Menu BeatmapSetInfo setInfo = null; + // if the user has requested not to play theme music, we should attempt to find a random beatmap from their collection. if (!MenuMusic.Value) { var sets = beatmaps.GetAllUsableBeatmapSets(IncludedDetails.Minimal); + if (sets.Count > 0) - setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID); - } - - if (setInfo == null) - { - setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == BeatmapHash); - - if (setInfo == null) { - // we need to import the default menu background beatmap - setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).Result; - - setInfo.Protected = true; - beatmaps.Update(setInfo); + setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID); + initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); } } - introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); - Track = introBeatmap.Track; + // we generally want a song to be playing on startup, so use the intro music even if a user has specified not to if no other track is available. + if (setInfo == null) + { + if (!loadThemedIntro()) + { + // if we detect that the theme track or beatmap is unavailable this is either first startup or things are in a bad state. + // this could happen if a user has nuked their files store. for now, reimport to repair this. + var import = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).Result; + import.Protected = true; + beatmaps.Update(import); + + loadThemedIntro(); + } + } + + bool loadThemedIntro() + { + setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == BeatmapHash); + + if (setInfo != null) + { + initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); + UsingThemedIntro = !(Track is TrackVirtual); + } + + return UsingThemedIntro; + } } public override void OnResuming(IScreen last) @@ -119,7 +140,7 @@ namespace osu.Game.Screens.Menu public override void OnSuspending(IScreen next) { base.OnSuspending(next); - Track = null; + initialBeatmap = null; } protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack(); @@ -127,7 +148,7 @@ namespace osu.Game.Screens.Menu protected void StartTrack() { // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu. - if (MenuMusic.Value) + if (UsingThemedIntro) Track.Restart(); } @@ -141,8 +162,7 @@ namespace osu.Game.Screens.Menu if (!resuming) { - beatmap.Value = introBeatmap; - introBeatmap = null; + beatmap.Value = initialBeatmap; logo.MoveTo(new Vector2(0.5f)); logo.ScaleTo(Vector2.One); diff --git a/osu.Game/Screens/Menu/IntroTriangles.cs b/osu.Game/Screens/Menu/IntroTriangles.cs index cb05dcc932..225ad02ec4 100644 --- a/osu.Game/Screens/Menu/IntroTriangles.cs +++ b/osu.Game/Screens/Menu/IntroTriangles.cs @@ -7,7 +7,6 @@ using System.IO; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osu.Framework.Audio.Track; using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -47,7 +46,7 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load() { - if (MenuVoice.Value && !MenuMusic.Value) + if (MenuVoice.Value && !UsingThemedIntro) welcome = audio.Samples.Get(@"welcome"); } @@ -62,7 +61,7 @@ namespace osu.Game.Screens.Menu LoadComponentAsync(new TrianglesIntroSequence(logo, background) { RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(MenuMusic.Value && !(Track is TrackVirtual) ? Track : null), + Clock = new FramedClock(UsingThemedIntro ? Track : null), LoadMenu = LoadMenu }, t => { From 712fd6a944cc957bcff10721451ffe613c7180c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 Jun 2020 17:49:45 +0900 Subject: [PATCH 39/65] Fetch existing private message channels on re-joining --- .../API/Requests/CreateChannelRequest.cs | 34 +++++++++++++++++++ .../API/Requests/Responses/APIChatChannel.cs | 18 ++++++++++ osu.Game/Online/Chat/Channel.cs | 3 +- osu.Game/Online/Chat/ChannelManager.cs | 29 ++++++++++------ 4 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Online/API/Requests/CreateChannelRequest.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APIChatChannel.cs diff --git a/osu.Game/Online/API/Requests/CreateChannelRequest.cs b/osu.Game/Online/API/Requests/CreateChannelRequest.cs new file mode 100644 index 0000000000..42cb201969 --- /dev/null +++ b/osu.Game/Online/API/Requests/CreateChannelRequest.cs @@ -0,0 +1,34 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using System.Net.Http; +using osu.Framework.IO.Network; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class CreateChannelRequest : APIRequest + { + private readonly Channel channel; + + public CreateChannelRequest(Channel channel) + { + this.channel = channel; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + req.Method = HttpMethod.Post; + + req.AddParameter("type", $"{ChannelType.PM}"); + req.AddParameter("target_id", $"{channel.Users.First().Id}"); + + return req; + } + + protected override string Target => @"chat/channels"; + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APIChatChannel.cs b/osu.Game/Online/API/Requests/Responses/APIChatChannel.cs new file mode 100644 index 0000000000..fc3b2a8e31 --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIChatChannel.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using Newtonsoft.Json; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIChatChannel + { + [JsonProperty(@"channel_id")] + public int? ChannelID { get; set; } + + [JsonProperty(@"recent_messages")] + public List RecentMessages { get; set; } + } +} diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index dbb2da5c03..8c1e1ad128 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -84,7 +84,8 @@ namespace osu.Game.Online.Chat public long? LastReadId; /// - /// Signalles if the current user joined this channel or not. Defaults to false. + /// Signals if the current user joined this channel or not. Defaults to false. + /// Note that this does not guarantee a join has completed. Check Id > 0 for confirmation. /// public Bindable Joined = new Bindable(); diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index b17e0812da..9350887feb 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -86,7 +86,7 @@ namespace osu.Game.Online.Chat return; CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Users.Any(u => u.Id == user.Id)) - ?? new Channel(user); + ?? JoinChannel(new Channel(user)); } private void currentChannelChanged(ValueChangedEvent e) @@ -140,7 +140,7 @@ namespace osu.Game.Online.Chat target.AddLocalEcho(message); // if this is a PM and the first message, we need to do a special request to create the PM channel - if (target.Type == ChannelType.PM && !target.Joined.Value) + if (target.Type == ChannelType.PM && target.Id == 0) { var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(target.Users.First(), message); @@ -356,26 +356,35 @@ namespace osu.Game.Online.Chat // ensure we are joined to the channel if (!channel.Joined.Value) { + channel.Joined.Value = true; + switch (channel.Type) { case ChannelType.Multiplayer: // join is implicit. happens when you join a multiplayer game. // this will probably change in the future. - channel.Joined.Value = true; joinChannel(channel, fetchInitialMessages); return channel; - case ChannelType.Private: - // can't do this yet. + case ChannelType.PM: + var createRequest = new CreateChannelRequest(channel); + createRequest.Success += resChannel => + { + if (resChannel.ChannelID.HasValue) + { + channel.Id = resChannel.ChannelID.Value; + + handleChannelMessages(resChannel.RecentMessages); + channel.MessagesLoaded = true; // this will mark the channel as having received messages even if there were none. + } + }; + + api.Queue(createRequest); break; default: var req = new JoinChannelRequest(channel, api.LocalUser.Value); - req.Success += () => - { - channel.Joined.Value = true; - joinChannel(channel, fetchInitialMessages); - }; + req.Success += () => joinChannel(channel, fetchInitialMessages); req.Failure += ex => LeaveChannel(channel); api.Queue(req); return channel; From ff555c41c6b667ebd91ba46f166cbd247ffeece3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2020 08:57:44 +0000 Subject: [PATCH 40/65] Bump Sentry from 2.1.1 to 2.1.3 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 2.1.1 to 2.1.3. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/2.1.1...2.1.3) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4d6358575b..c41d0a0cf6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + From bbf8864f1478d609fe2eb7184cbd303e0cc9a14b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2020 09:45:31 +0000 Subject: [PATCH 41/65] Bump Microsoft.Build.Traversal from 2.0.34 to 2.0.48 Bumps [Microsoft.Build.Traversal](https://github.com/Microsoft/MSBuildSdks) from 2.0.34 to 2.0.48. - [Release notes](https://github.com/Microsoft/MSBuildSdks/releases) - [Changelog](https://github.com/microsoft/MSBuildSdks/blob/master/RELEASE.md) - [Commits](https://github.com/Microsoft/MSBuildSdks/compare/Microsoft.Build.Traversal.2.0.34...Microsoft.Build.Traversal.2.0.48) Signed-off-by: dependabot-preview[bot] --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 6c793a3f1d..bdb90eb0e9 100644 --- a/global.json +++ b/global.json @@ -5,6 +5,6 @@ "version": "3.1.100" }, "msbuild-sdks": { - "Microsoft.Build.Traversal": "2.0.34" + "Microsoft.Build.Traversal": "2.0.48" } } \ No newline at end of file From e0c94304c79637c86e9304e0471ce37bb139f223 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2020 09:45:31 +0000 Subject: [PATCH 42/65] Bump Humanizer from 2.8.11 to 2.8.26 Bumps [Humanizer](https://github.com/Humanizr/Humanizer) from 2.8.11 to 2.8.26. - [Release notes](https://github.com/Humanizr/Humanizer/releases) - [Changelog](https://github.com/Humanizr/Humanizer/blob/master/release_notes.md) - [Commits](https://github.com/Humanizr/Humanizer/compare/v2.8.11...v2.8.26) Signed-off-by: dependabot-preview[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c41d0a0cf6..8213719c01 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -20,7 +20,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 6b55fa51ff..fd13455c63 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -76,7 +76,7 @@ - + From 8a021e0beb39a897816d8da99983eb9de6e4b419 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 8 Jun 2020 22:35:01 +0900 Subject: [PATCH 43/65] Use save method in test --- .../Beatmaps/IO/ImportBeatmapTest.cs | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 55368f6676..249a8caba9 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -1,11 +1,10 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.IO; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using NUnit.Framework; @@ -15,7 +14,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Tests.Resources; @@ -730,25 +728,17 @@ namespace osu.Game.Tests.Beatmaps.IO await osu.Dependencies.Get().Import(temp); BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; + + var beatmapInfo = setToUpdate.Beatmaps.First(b => b.RulesetID == 0); Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap; BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename)); string oldMd5Hash = beatmapToUpdate.BeatmapInfo.MD5Hash; - using (var stream = new MemoryStream()) - { - using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) - { - beatmapToUpdate.HitObjects.Clear(); - beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); + beatmapToUpdate.HitObjects.Clear(); + beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 }); - new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer); - } - - stream.Seek(0, SeekOrigin.Begin); - - manager.UpdateFile(setToUpdate, fileToUpdate, stream); - } + manager.Save(beatmapInfo, beatmapToUpdate); // Check that the old file reference has been removed Assert.That(manager.QueryBeatmapSet(s => s.ID == setToUpdate.ID).Files.All(f => f.ID != fileToUpdate.ID)); From 2a5e96002548e306ffe0837747029d0f3f62a4f1 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 8 Jun 2020 21:15:51 +0200 Subject: [PATCH 44/65] Move user and skin specific settings to a subclass --- .../Screens/Menu/BasicLogoVisualisation.cs | 229 ++++++++++++++++++ osu.Game/Screens/Menu/LogoVisualisation.cs | 216 +---------------- 2 files changed, 231 insertions(+), 214 deletions(-) create mode 100644 osu.Game/Screens/Menu/BasicLogoVisualisation.cs diff --git a/osu.Game/Screens/Menu/BasicLogoVisualisation.cs b/osu.Game/Screens/Menu/BasicLogoVisualisation.cs new file mode 100644 index 0000000000..ab86c38cb4 --- /dev/null +++ b/osu.Game/Screens/Menu/BasicLogoVisualisation.cs @@ -0,0 +1,229 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK; +using osuTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Utils; + +namespace osu.Game.Screens.Menu +{ + public class BasicLogoVisualisation : Drawable, IHasAccentColour + { + private readonly IBindable beatmap = new Bindable(); + + /// + /// The number of bars to jump each update iteration. + /// + private const int index_change = 5; + + /// + /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. + /// + private const float bar_length = 600; + + /// + /// The number of bars in one rotation of the visualiser. + /// + private const int bars_per_visualiser = 200; + + /// + /// How many times we should stretch around the circumference (overlapping overselves). + /// + private const float visualiser_rounds = 5; + + /// + /// How much should each bar go down each millisecond (based on a full bar). + /// + private const float decay_per_milisecond = 0.0024f; + + /// + /// Number of milliseconds between each amplitude update. + /// + private const float time_between_updates = 50; + + /// + /// The minimum amplitude to show a bar. + /// + private const float amplitude_dead_zone = 1f / bar_length; + + private int indexOffset; + + public Color4 AccentColour { get; set; } + + private readonly float[] frequencyAmplitudes = new float[256]; + + private IShader shader; + private readonly Texture texture; + + public BasicLogoVisualisation() + { + texture = Texture.WhitePixel; + Blending = BlendingParameters.Additive; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders, IBindable beatmap) + { + this.beatmap.BindTo(beatmap); + shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } + + private void updateAmplitudes() + { + var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; + var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; + + float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; + + for (int i = 0; i < bars_per_visualiser; i++) + { + if (track?.IsRunning ?? false) + { + float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); + if (targetAmplitude > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = targetAmplitude; + } + else + { + int index = (i + index_change) % bars_per_visualiser; + if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = frequencyAmplitudes[index]; + } + } + + indexOffset = (indexOffset + index_change) % bars_per_visualiser; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); + delayed.PerformRepeatCatchUpExecutions = false; + } + + protected override void Update() + { + base.Update(); + + float decayFactor = (float)Time.Elapsed * decay_per_milisecond; + + for (int i = 0; i < bars_per_visualiser; i++) + { + //3% of extra bar length to make it a little faster when bar is almost at it's minimum + frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); + if (frequencyAmplitudes[i] < 0) + frequencyAmplitudes[i] = 0; + } + + Invalidate(Invalidation.DrawNode); + } + + protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); + + private class VisualisationDrawNode : DrawNode + { + protected new BasicLogoVisualisation Source => (BasicLogoVisualisation)base.Source; + + private IShader shader; + private Texture texture; + + // Assuming the logo is a circle, we don't need a second dimension. + private float size; + + private Color4 colour; + private float[] audioData; + + private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); + + public VisualisationDrawNode(BasicLogoVisualisation source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + shader = Source.shader; + texture = Source.texture; + size = Source.DrawSize.X; + colour = Source.AccentColour; + audioData = Source.frequencyAmplitudes; + } + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + shader.Bind(); + + Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; + + ColourInfo colourInfo = DrawColourInfo.Colour; + colourInfo.ApplyChild(colour); + + if (audioData != null) + { + for (int j = 0; j < visualiser_rounds; j++) + { + for (int i = 0; i < bars_per_visualiser; i++) + { + if (audioData[i] < amplitude_dead_zone) + continue; + + float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); + float rotationCos = MathF.Cos(rotation); + float rotationSin = MathF.Sin(rotation); + // taking the cos and sin to the 0..1 range + var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; + + var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); + // The distance between the position and the sides of the bar. + var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); + // The distance between the bottom side of the bar and the top side. + var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); + + var rectangle = new Quad( + Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) + ); + + DrawQuad( + texture, + rectangle, + colourInfo, + null, + vertexBatch.AddAction, + // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. + Vector2.Divide(inflation, barSize.Yx)); + } + } + } + + shader.Unbind(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + vertexBatch.Dispose(); + } + } + } +} diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index 0db7f2a2dc..e893ef91bb 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -1,90 +1,24 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osuTK; using osuTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Batches; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.OpenGL.Vertices; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shaders; -using osu.Framework.Graphics.Textures; -using osu.Game.Beatmaps; -using osu.Game.Graphics; using osu.Game.Skinning; using osu.Game.Online.API; using osu.Game.Users; -using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Utils; namespace osu.Game.Screens.Menu { - public class LogoVisualisation : Drawable, IHasAccentColour + public class LogoVisualisation : BasicLogoVisualisation { - private readonly IBindable beatmap = new Bindable(); - - /// - /// The number of bars to jump each update iteration. - /// - private const int index_change = 5; - - /// - /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. - /// - private const float bar_length = 600; - - /// - /// The number of bars in one rotation of the visualiser. - /// - private const int bars_per_visualiser = 200; - - /// - /// How many times we should stretch around the circumference (overlapping overselves). - /// - private const float visualiser_rounds = 5; - - /// - /// How much should each bar go down each millisecond (based on a full bar). - /// - private const float decay_per_milisecond = 0.0024f; - - /// - /// Number of milliseconds between each amplitude update. - /// - private const float time_between_updates = 50; - - /// - /// The minimum amplitude to show a bar. - /// - private const float amplitude_dead_zone = 1f / bar_length; - - private int indexOffset; - - public Color4 AccentColour { get; set; } - - private readonly float[] frequencyAmplitudes = new float[256]; - - private IShader shader; - private readonly Texture texture; - private Bindable user; private Bindable skin; - public LogoVisualisation() - { - texture = Texture.WhitePixel; - Blending = BlendingParameters.Additive; - } - [BackgroundDependencyLoader] - private void load(ShaderManager shaders, IBindable beatmap, IAPIProvider api, SkinManager skinManager) + private void load(IAPIProvider api, SkinManager skinManager) { - this.beatmap.BindTo(beatmap); - shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); user = api.LocalUser.GetBoundCopy(); skin = skinManager.CurrentSkin.GetBoundCopy(); @@ -92,32 +26,6 @@ namespace osu.Game.Screens.Menu skin.BindValueChanged(_ => updateColour(), true); } - private void updateAmplitudes() - { - var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; - var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; - - float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; - - for (int i = 0; i < bars_per_visualiser; i++) - { - if (track?.IsRunning ?? false) - { - float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); - if (targetAmplitude > frequencyAmplitudes[i]) - frequencyAmplitudes[i] = targetAmplitude; - } - else - { - int index = (i + index_change) % bars_per_visualiser; - if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) - frequencyAmplitudes[i] = frequencyAmplitudes[index]; - } - } - - indexOffset = (indexOffset + index_change) % bars_per_visualiser; - } - private void updateColour() { Color4 defaultColour = Color4.White.Opacity(0.2f); @@ -127,125 +35,5 @@ namespace osu.Game.Screens.Menu else AccentColour = defaultColour; } - - protected override void LoadComplete() - { - base.LoadComplete(); - - var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); - delayed.PerformRepeatCatchUpExecutions = false; - } - - protected override void Update() - { - base.Update(); - - float decayFactor = (float)Time.Elapsed * decay_per_milisecond; - - for (int i = 0; i < bars_per_visualiser; i++) - { - //3% of extra bar length to make it a little faster when bar is almost at it's minimum - frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); - if (frequencyAmplitudes[i] < 0) - frequencyAmplitudes[i] = 0; - } - - Invalidate(Invalidation.DrawNode); - } - - protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); - - private class VisualisationDrawNode : DrawNode - { - protected new LogoVisualisation Source => (LogoVisualisation)base.Source; - - private IShader shader; - private Texture texture; - - // Assuming the logo is a circle, we don't need a second dimension. - private float size; - - private Color4 colour; - private float[] audioData; - - private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); - - public VisualisationDrawNode(LogoVisualisation source) - : base(source) - { - } - - public override void ApplyState() - { - base.ApplyState(); - - shader = Source.shader; - texture = Source.texture; - size = Source.DrawSize.X; - colour = Source.AccentColour; - audioData = Source.frequencyAmplitudes; - } - - public override void Draw(Action vertexAction) - { - base.Draw(vertexAction); - - shader.Bind(); - - Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; - - ColourInfo colourInfo = DrawColourInfo.Colour; - colourInfo.ApplyChild(colour); - - if (audioData != null) - { - for (int j = 0; j < visualiser_rounds; j++) - { - for (int i = 0; i < bars_per_visualiser; i++) - { - if (audioData[i] < amplitude_dead_zone) - continue; - - float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); - float rotationCos = MathF.Cos(rotation); - float rotationSin = MathF.Sin(rotation); - // taking the cos and sin to the 0..1 range - var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; - - var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); - // The distance between the position and the sides of the bar. - var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); - // The distance between the bottom side of the bar and the top side. - var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); - - var rectangle = new Quad( - Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) - ); - - DrawQuad( - texture, - rectangle, - colourInfo, - null, - vertexBatch.AddAction, - // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. - Vector2.Divide(inflation, barSize.Yx)); - } - } - } - - shader.Unbind(); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - vertexBatch.Dispose(); - } - } } } From a60bb5feac2eae08be730b5def7e9a3df82c3c1d Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 8 Jun 2020 23:45:40 +0200 Subject: [PATCH 45/65] Rename baseclass, add xmldoc & change access to internal --- .../Screens/Menu/BasicLogoVisualisation.cs | 229 ----------------- osu.Game/Screens/Menu/LogoVisualisation.cs | 233 ++++++++++++++++-- .../Screens/Menu/MenuLogoVisualisation.cs | 39 +++ osu.Game/Screens/Menu/OsuLogo.cs | 4 +- 4 files changed, 254 insertions(+), 251 deletions(-) delete mode 100644 osu.Game/Screens/Menu/BasicLogoVisualisation.cs create mode 100644 osu.Game/Screens/Menu/MenuLogoVisualisation.cs diff --git a/osu.Game/Screens/Menu/BasicLogoVisualisation.cs b/osu.Game/Screens/Menu/BasicLogoVisualisation.cs deleted file mode 100644 index ab86c38cb4..0000000000 --- a/osu.Game/Screens/Menu/BasicLogoVisualisation.cs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osuTK; -using osuTK.Graphics; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Batches; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.OpenGL.Vertices; -using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Shaders; -using osu.Framework.Graphics.Textures; -using osu.Game.Beatmaps; -using osu.Game.Graphics; -using System; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Utils; - -namespace osu.Game.Screens.Menu -{ - public class BasicLogoVisualisation : Drawable, IHasAccentColour - { - private readonly IBindable beatmap = new Bindable(); - - /// - /// The number of bars to jump each update iteration. - /// - private const int index_change = 5; - - /// - /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. - /// - private const float bar_length = 600; - - /// - /// The number of bars in one rotation of the visualiser. - /// - private const int bars_per_visualiser = 200; - - /// - /// How many times we should stretch around the circumference (overlapping overselves). - /// - private const float visualiser_rounds = 5; - - /// - /// How much should each bar go down each millisecond (based on a full bar). - /// - private const float decay_per_milisecond = 0.0024f; - - /// - /// Number of milliseconds between each amplitude update. - /// - private const float time_between_updates = 50; - - /// - /// The minimum amplitude to show a bar. - /// - private const float amplitude_dead_zone = 1f / bar_length; - - private int indexOffset; - - public Color4 AccentColour { get; set; } - - private readonly float[] frequencyAmplitudes = new float[256]; - - private IShader shader; - private readonly Texture texture; - - public BasicLogoVisualisation() - { - texture = Texture.WhitePixel; - Blending = BlendingParameters.Additive; - } - - [BackgroundDependencyLoader] - private void load(ShaderManager shaders, IBindable beatmap) - { - this.beatmap.BindTo(beatmap); - shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); - } - - private void updateAmplitudes() - { - var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; - var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; - - float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; - - for (int i = 0; i < bars_per_visualiser; i++) - { - if (track?.IsRunning ?? false) - { - float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); - if (targetAmplitude > frequencyAmplitudes[i]) - frequencyAmplitudes[i] = targetAmplitude; - } - else - { - int index = (i + index_change) % bars_per_visualiser; - if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) - frequencyAmplitudes[i] = frequencyAmplitudes[index]; - } - } - - indexOffset = (indexOffset + index_change) % bars_per_visualiser; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); - delayed.PerformRepeatCatchUpExecutions = false; - } - - protected override void Update() - { - base.Update(); - - float decayFactor = (float)Time.Elapsed * decay_per_milisecond; - - for (int i = 0; i < bars_per_visualiser; i++) - { - //3% of extra bar length to make it a little faster when bar is almost at it's minimum - frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); - if (frequencyAmplitudes[i] < 0) - frequencyAmplitudes[i] = 0; - } - - Invalidate(Invalidation.DrawNode); - } - - protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); - - private class VisualisationDrawNode : DrawNode - { - protected new BasicLogoVisualisation Source => (BasicLogoVisualisation)base.Source; - - private IShader shader; - private Texture texture; - - // Assuming the logo is a circle, we don't need a second dimension. - private float size; - - private Color4 colour; - private float[] audioData; - - private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); - - public VisualisationDrawNode(BasicLogoVisualisation source) - : base(source) - { - } - - public override void ApplyState() - { - base.ApplyState(); - - shader = Source.shader; - texture = Source.texture; - size = Source.DrawSize.X; - colour = Source.AccentColour; - audioData = Source.frequencyAmplitudes; - } - - public override void Draw(Action vertexAction) - { - base.Draw(vertexAction); - - shader.Bind(); - - Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; - - ColourInfo colourInfo = DrawColourInfo.Colour; - colourInfo.ApplyChild(colour); - - if (audioData != null) - { - for (int j = 0; j < visualiser_rounds; j++) - { - for (int i = 0; i < bars_per_visualiser; i++) - { - if (audioData[i] < amplitude_dead_zone) - continue; - - float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); - float rotationCos = MathF.Cos(rotation); - float rotationSin = MathF.Sin(rotation); - // taking the cos and sin to the 0..1 range - var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; - - var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); - // The distance between the position and the sides of the bar. - var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); - // The distance between the bottom side of the bar and the top side. - var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); - - var rectangle = new Quad( - Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix), - Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) - ); - - DrawQuad( - texture, - rectangle, - colourInfo, - null, - vertexBatch.AddAction, - // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. - Vector2.Divide(inflation, barSize.Yx)); - } - } - } - - shader.Unbind(); - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - vertexBatch.Dispose(); - } - } - } -} diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index e893ef91bb..6a28740d4e 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -1,39 +1,232 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osuTK; using osuTK.Graphics; -using osu.Game.Skinning; -using osu.Game.Online.API; -using osu.Game.Users; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Batches; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Utils; namespace osu.Game.Screens.Menu { - public class LogoVisualisation : BasicLogoVisualisation + /// + /// A visualiser that reacts to music coming from beatmaps. + /// + public class LogoVisualisation : Drawable, IHasAccentColour { - private Bindable user; - private Bindable skin; + private readonly IBindable beatmap = new Bindable(); - [BackgroundDependencyLoader] - private void load(IAPIProvider api, SkinManager skinManager) + /// + /// The number of bars to jump each update iteration. + /// + private const int index_change = 5; + + /// + /// The maximum length of each bar in the visualiser. Will be reduced when kiai is not activated. + /// + private const float bar_length = 600; + + /// + /// The number of bars in one rotation of the visualiser. + /// + private const int bars_per_visualiser = 200; + + /// + /// How many times we should stretch around the circumference (overlapping overselves). + /// + private const float visualiser_rounds = 5; + + /// + /// How much should each bar go down each millisecond (based on a full bar). + /// + private const float decay_per_milisecond = 0.0024f; + + /// + /// Number of milliseconds between each amplitude update. + /// + private const float time_between_updates = 50; + + /// + /// The minimum amplitude to show a bar. + /// + private const float amplitude_dead_zone = 1f / bar_length; + + private int indexOffset; + + public Color4 AccentColour { get; set; } + + private readonly float[] frequencyAmplitudes = new float[256]; + + private IShader shader; + private readonly Texture texture; + + public LogoVisualisation() { - user = api.LocalUser.GetBoundCopy(); - skin = skinManager.CurrentSkin.GetBoundCopy(); - - user.ValueChanged += _ => updateColour(); - skin.BindValueChanged(_ => updateColour(), true); + texture = Texture.WhitePixel; + Blending = BlendingParameters.Additive; } - private void updateColour() + [BackgroundDependencyLoader] + private void load(ShaderManager shaders, IBindable beatmap) { - Color4 defaultColour = Color4.White.Opacity(0.2f); + this.beatmap.BindTo(beatmap); + shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } - if (user.Value?.IsSupporter ?? false) - AccentColour = skin.Value.GetConfig(GlobalSkinColours.MenuGlow)?.Value ?? defaultColour; - else - AccentColour = defaultColour; + private void updateAmplitudes() + { + var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null; + var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null; + + float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes; + + for (int i = 0; i < bars_per_visualiser; i++) + { + if (track?.IsRunning ?? false) + { + float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f); + if (targetAmplitude > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = targetAmplitude; + } + else + { + int index = (i + index_change) % bars_per_visualiser; + if (frequencyAmplitudes[index] > frequencyAmplitudes[i]) + frequencyAmplitudes[i] = frequencyAmplitudes[index]; + } + } + + indexOffset = (indexOffset + index_change) % bars_per_visualiser; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true); + delayed.PerformRepeatCatchUpExecutions = false; + } + + protected override void Update() + { + base.Update(); + + float decayFactor = (float)Time.Elapsed * decay_per_milisecond; + + for (int i = 0; i < bars_per_visualiser; i++) + { + //3% of extra bar length to make it a little faster when bar is almost at it's minimum + frequencyAmplitudes[i] -= decayFactor * (frequencyAmplitudes[i] + 0.03f); + if (frequencyAmplitudes[i] < 0) + frequencyAmplitudes[i] = 0; + } + + Invalidate(Invalidation.DrawNode); + } + + protected override DrawNode CreateDrawNode() => new VisualisationDrawNode(this); + + private class VisualisationDrawNode : DrawNode + { + protected new LogoVisualisation Source => (LogoVisualisation)base.Source; + + private IShader shader; + private Texture texture; + + // Assuming the logo is a circle, we don't need a second dimension. + private float size; + + private Color4 colour; + private float[] audioData; + + private readonly QuadBatch vertexBatch = new QuadBatch(100, 10); + + public VisualisationDrawNode(LogoVisualisation source) + : base(source) + { + } + + public override void ApplyState() + { + base.ApplyState(); + + shader = Source.shader; + texture = Source.texture; + size = Source.DrawSize.X; + colour = Source.AccentColour; + audioData = Source.frequencyAmplitudes; + } + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + shader.Bind(); + + Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; + + ColourInfo colourInfo = DrawColourInfo.Colour; + colourInfo.ApplyChild(colour); + + if (audioData != null) + { + for (int j = 0; j < visualiser_rounds; j++) + { + for (int i = 0; i < bars_per_visualiser; i++) + { + if (audioData[i] < amplitude_dead_zone) + continue; + + float rotation = MathUtils.DegreesToRadians(i / (float)bars_per_visualiser * 360 + j * 360 / visualiser_rounds); + float rotationCos = MathF.Cos(rotation); + float rotationSin = MathF.Sin(rotation); + // taking the cos and sin to the 0..1 range + var barPosition = new Vector2(rotationCos / 2 + 0.5f, rotationSin / 2 + 0.5f) * size; + + var barSize = new Vector2(size * MathF.Sqrt(2 * (1 - MathF.Cos(MathUtils.DegreesToRadians(360f / bars_per_visualiser)))) / 2f, bar_length * audioData[i]); + // The distance between the position and the sides of the bar. + var bottomOffset = new Vector2(-rotationSin * barSize.X / 2, rotationCos * barSize.X / 2); + // The distance between the bottom side of the bar and the top side. + var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y); + + var rectangle = new Quad( + Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix), + Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix) + ); + + DrawQuad( + texture, + rectangle, + colourInfo, + null, + vertexBatch.AddAction, + // barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that. + Vector2.Divide(inflation, barSize.Yx)); + } + } + } + + shader.Unbind(); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + vertexBatch.Dispose(); + } } } } diff --git a/osu.Game/Screens/Menu/MenuLogoVisualisation.cs b/osu.Game/Screens/Menu/MenuLogoVisualisation.cs new file mode 100644 index 0000000000..5eb3f1efa0 --- /dev/null +++ b/osu.Game/Screens/Menu/MenuLogoVisualisation.cs @@ -0,0 +1,39 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osuTK.Graphics; +using osu.Game.Skinning; +using osu.Game.Online.API; +using osu.Game.Users; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; + +namespace osu.Game.Screens.Menu +{ + internal class MenuLogoVisualisation : LogoVisualisation + { + private Bindable user; + private Bindable skin; + + [BackgroundDependencyLoader] + private void load(IAPIProvider api, SkinManager skinManager) + { + user = api.LocalUser.GetBoundCopy(); + skin = skinManager.CurrentSkin.GetBoundCopy(); + + user.ValueChanged += _ => updateColour(); + skin.BindValueChanged(_ => updateColour(), true); + } + + private void updateColour() + { + Color4 defaultColour = Color4.White.Opacity(0.2f); + + if (user.Value?.IsSupporter ?? false) + AccentColour = skin.Value.GetConfig(GlobalSkinColours.MenuGlow)?.Value ?? defaultColour; + else + AccentColour = defaultColour; + } + } +} diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 800520100e..9cadfd7df6 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Menu private readonly Container logoBeatContainer; private readonly Container logoAmplitudeContainer; private readonly Container logoHoverContainer; - private readonly LogoVisualisation visualizer; + private readonly MenuLogoVisualisation visualizer; private readonly IntroSequence intro; @@ -139,7 +139,7 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both, Children = new Drawable[] { - visualizer = new LogoVisualisation + visualizer = new MenuLogoVisualisation { RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, From 44dd7d65bee35d52cce4de57ab79080decb3dce9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Jun 2020 18:21:37 +0900 Subject: [PATCH 46/65] Fix duplicate scores showing --- osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs | 2 +- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs b/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs index 50b62cd6ed..8eb2952159 100644 --- a/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs +++ b/osu.Game/Online/API/Requests/SubmitRoomScoreRequest.cs @@ -8,7 +8,7 @@ using osu.Game.Scoring; namespace osu.Game.Online.API.Requests { - public class SubmitRoomScoreRequest : APIRequest + public class SubmitRoomScoreRequest : APIRequest { private readonly int scoreId; private readonly int roomId; diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index fbe9e3480f..cf0197d26b 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -97,22 +97,18 @@ namespace osu.Game.Screens.Multi.Play } protected override ScoreInfo CreateScore() - { - submitScore(); - return base.CreateScore(); - } - - private void submitScore() { var score = base.CreateScore(); - score.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore()); Debug.Assert(token != null); var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score); + request.Success += s => score.OnlineScoreID = s.ID; request.Failure += e => Logger.Error(e, "Failed to submit score"); api.Queue(request); + + return score; } protected override void Dispose(bool isDisposing) From 4fd5ff61eb1b6a01543689c04b2f84daf9160dd0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Jun 2020 18:53:55 +0900 Subject: [PATCH 47/65] Add loading spinner --- .../TestSceneTimeshiftResultsScreen.cs | 67 +++++++++++++++---- .../Multi/Ranking/TimeshiftResultsScreen.cs | 29 +++++++- 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 8559e7e2f4..66ebf9abda 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using NUnit.Framework; using osu.Game.Online.API; using osu.Game.Online.API.Requests; @@ -18,19 +19,52 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneTimeshiftResultsScreen : ScreenTestScene { + private bool roomsReceived; + + [SetUp] + public void Setup() => Schedule(() => + { + roomsReceived = false; + bindHandler(); + }); + [Test] public void TestShowResultsWithScore() { createResults(new TestScoreInfo(new OsuRuleset().RulesetInfo)); + AddWaitStep("wait for display", 5); } [Test] public void TestShowResultsNullScore() { createResults(null); + AddWaitStep("wait for display", 5); + } + + [Test] + public void TestShowResultsNullScoreWithDelay() + { + AddStep("bind delayed handler", () => bindHandler(3000)); + createResults(null); + AddUntilStep("wait for rooms to be received", () => roomsReceived); + AddWaitStep("wait for display", 5); } private void createResults(ScoreInfo score) + { + AddStep("load results", () => + { + LoadScreen(new TimeshiftResultsScreen(score, 1, new PlaylistItem + { + Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Ruleset = { Value = new OsuRuleset().RulesetInfo } + })); + }); + + } + + private void bindHandler(double delay = 0) { var roomScores = new List(); @@ -61,26 +95,31 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } - AddStep("bind request handler", () => ((DummyAPIAccess)API).HandleRequest = request => + ((DummyAPIAccess)API).HandleRequest = request => { switch (request) { case GetRoomPlaylistScoresRequest r: - r.TriggerSuccess(new RoomPlaylistScores { Scores = roomScores }); + if (delay == 0) + success(); + else + { + Task.Run(async () => + { + await Task.Delay(TimeSpan.FromMilliseconds(delay)); + Schedule(success); + }); + } + + void success() + { + r.TriggerSuccess(new RoomPlaylistScores { Scores = roomScores }); + roomsReceived = true; + } + break; } - }); - - AddStep("load results", () => - { - LoadScreen(new TimeshiftResultsScreen(score, 1, new PlaylistItem - { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, - Ruleset = { Value = new OsuRuleset().RulesetInfo } - })); - }); - - AddWaitStep("wait for display", 10); + }; } } } diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index d95cee2ab8..5cafc974f1 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -4,6 +4,10 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; @@ -17,6 +21,8 @@ namespace osu.Game.Screens.Multi.Ranking private readonly int roomId; private readonly PlaylistItem playlistItem; + private LoadingSpinner loadingLayer; + public TimeshiftResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry = true) : base(score, allowRetry) { @@ -24,10 +30,31 @@ namespace osu.Game.Screens.Multi.Ranking this.playlistItem = playlistItem; } + [BackgroundDependencyLoader] + private void load() + { + AddInternal(loadingLayer = new LoadingLayer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + X = -10, + State = { Value = Score == null ? Visibility.Visible : Visibility.Hidden }, + Padding = new MarginPadding { Bottom = TwoLayerButton.SIZE_EXTENDED.Y } + }); + } + protected override APIRequest FetchScores(Action> scoresCallback) { var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); - req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); + + req.Success += r => + { + scoresCallback?.Invoke(r.Scores.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); + loadingLayer.Hide(); + }; + + req.Failure += _ => loadingLayer.Hide(); + return req; } } From 05b1edb9d88135667cf9893a414fa08e4cc4dad6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Jun 2020 19:01:02 +0900 Subject: [PATCH 48/65] Fix incorrect beatmap showing --- .../Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs | 1 - .../Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 66ebf9abda..9fc7c336cb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -61,7 +61,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Ruleset = { Value = new OsuRuleset().RulesetInfo } })); }); - } private void bindHandler(double delay = 0) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 81d5d113ae..b06ef8ae83 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -4,11 +4,9 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -52,9 +50,9 @@ namespace osu.Game.Screens.Ranking.Expanded } [BackgroundDependencyLoader] - private void load(Bindable working) + private void load() { - var beatmap = working.Value.BeatmapInfo; + var beatmap = score.Beatmap; var metadata = beatmap.Metadata; var creator = metadata.Author?.Username; From ab10732a788c8dbe09d658e19f35c2707c70c8cd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 9 Jun 2020 22:13:48 +0900 Subject: [PATCH 49/65] Remove usages of null-forgiving operator --- osu.Game.Tests/Chat/MessageFormatterTests.cs | 15 ++++++++++----- .../Visual/UserInterface/TestSceneOsuIcon.cs | 8 +++++++- osu.Game/Online/API/APIAccess.cs | 3 ++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs index d1a859c84b..600c820ce1 100644 --- a/osu.Game.Tests/Chat/MessageFormatterTests.cs +++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs @@ -428,23 +428,28 @@ namespace osu.Game.Tests.Chat Assert.AreEqual(5, result.Links.Count); Link f = result.Links.Find(l => l.Url == "https://osu.ppy.sh/wiki/wiki links"); - Assert.AreEqual(44, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(44, f.Index); Assert.AreEqual(10, f.Length); f = result.Links.Find(l => l.Url == "http://www.simple-test.com"); - Assert.AreEqual(10, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(10, f.Index); Assert.AreEqual(11, f.Length); f = result.Links.Find(l => l.Url == "http://google.com"); - Assert.AreEqual(97, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(97, f.Index); Assert.AreEqual(4, f.Length); f = result.Links.Find(l => l.Url == "https://osu.ppy.sh"); - Assert.AreEqual(78, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(78, f.Index); Assert.AreEqual(18, f.Length); f = result.Links.Find(l => l.Url == "\uD83D\uDE12"); - Assert.AreEqual(101, f!.Index); + Assert.That(f, Is.Not.Null); + Assert.AreEqual(101, f.Index); Assert.AreEqual(3, f.Length); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs index 246eb119e8..c5374d50ab 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using System.Reflection; using NUnit.Framework; using osu.Framework.Extensions.IEnumerableExtensions; @@ -45,7 +46,12 @@ namespace osu.Game.Tests.Visual.UserInterface }); foreach (var p in typeof(OsuIcon).GetProperties(BindingFlags.Public | BindingFlags.Static)) - flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)p.GetValue(null)!)); + { + var propValue = p.GetValue(null); + Debug.Assert(propValue != null); + + flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)propValue)); + } AddStep("toggle shadows", () => flow.Children.ForEach(i => i.SpriteIcon.Shadow = !i.SpriteIcon.Shadow)); AddStep("change icons", () => flow.Children.ForEach(i => i.SpriteIcon.Icon = new IconUsage((char)(i.SpriteIcon.Icon.Icon + 1)))); diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index f9e2da9af8..4ea5c192fe 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Newtonsoft.Json.Linq; using osu.Framework.Bindables; using osu.Framework.Extensions.ExceptionExtensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Configuration; @@ -250,7 +251,7 @@ namespace osu.Game.Online.API { try { - return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true)!.ToObject(); + return JObject.Parse(req.GetResponseString()).SelectToken("form_error", true).AsNonNull().ToObject(); } catch { From 7274213cce3d5a1776bcc715d0f90e73b235a808 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jun 2020 23:30:42 +0900 Subject: [PATCH 50/65] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 07be3ab0d2..596e5bfa8b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8213719c01..1d3bafbfd6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index fd13455c63..ad7850599b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 7dc19220e51947f92b9c1dfe381d96098ffe637e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jun 2020 23:38:54 +0900 Subject: [PATCH 51/65] Apply new resharper formatting fixes --- osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs | 11 ++++++----- .../Screens/Gameplay/Components/TeamScoreDisplay.cs | 5 ++++- osu.Game/Rulesets/Mods/ModEasy.cs | 4 +++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs index f5b20fd1c5..a69646507a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs @@ -61,7 +61,9 @@ namespace osu.Game.Rulesets.Osu.Tests private DrawableSlider slider; [SetUpSteps] - public override void SetUpSteps() { } + public override void SetUpSteps() + { + } [TestCase(0)] [TestCase(1)] @@ -132,10 +134,9 @@ namespace osu.Game.Rulesets.Osu.Tests checkPositionChange(16600, sliderRepeat, positionDecreased); } - private void retrieveDrawableSlider(int index) => AddStep($"retrieve {(index + 1).ToOrdinalWords()} slider", () => - { - slider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(index); - }); + private void retrieveDrawableSlider(int index) => + AddStep($"retrieve {(index + 1).ToOrdinalWords()} slider", () => + slider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(index)); private void ensureSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionIncreased); private void ensureNoSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionRemainsSame); diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs index 3e60a03f92..da55ba53ea 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TeamScoreDisplay.cs @@ -21,7 +21,10 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components private TeamDisplay teamDisplay; - public bool ShowScore { set => teamDisplay.ShowScore = value; } + public bool ShowScore + { + set => teamDisplay.ShowScore = value; + } public TeamScoreDisplay(TeamColour teamColour) { diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index 7cf9656810..c6f3930029 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -35,7 +35,9 @@ namespace osu.Game.Rulesets.Mods private BindableNumber health; - public void ReadFromDifficulty(BeatmapDifficulty difficulty) { } + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } public void ApplyToDifficulty(BeatmapDifficulty difficulty) { From 880a1272288d04cf7eea92b94b39ca7037375e48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jun 2020 00:08:48 +0900 Subject: [PATCH 52/65] Use async overload --- osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index f145d90356..0151678db3 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Beatmaps.IO // arbitrary write to non-hashed file using (var sw = new FileInfo(Directory.GetFiles(extractedFolder, "*.mp3").First()).AppendText()) - sw.WriteLine("text"); + await sw.WriteLineAsync("text"); using (var zip = ZipArchive.Create()) { From 3ae1df07b0c13acefc6a700f07f7a9b74da4a102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jun 2020 00:09:29 +0900 Subject: [PATCH 53/65] Fix a couple more new formatting issues --- .../Screens/Gameplay/Components/TeamDisplay.cs | 5 ++++- osu.Game/Rulesets/Mods/ModHardRock.cs | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TeamDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TeamDisplay.cs index 29908e8e7c..b01c93ae03 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/TeamDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TeamDisplay.cs @@ -14,7 +14,10 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components { private readonly TeamScore score; - public bool ShowScore { set => score.FadeTo(value ? 1 : 0, 200); } + public bool ShowScore + { + set => score.FadeTo(value ? 1 : 0, 200); + } public TeamDisplay(TournamentTeam team, TeamColour colour, Bindable currentTeamScore, int pointsToWin) : base(team) diff --git a/osu.Game/Rulesets/Mods/ModHardRock.cs b/osu.Game/Rulesets/Mods/ModHardRock.cs index 58c9a58408..0e589735c1 100644 --- a/osu.Game/Rulesets/Mods/ModHardRock.cs +++ b/osu.Game/Rulesets/Mods/ModHardRock.cs @@ -17,7 +17,9 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Everything just got a bit harder..."; public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) }; - public void ReadFromDifficulty(BeatmapDifficulty difficulty) { } + public void ReadFromDifficulty(BeatmapDifficulty difficulty) + { + } public void ApplyToDifficulty(BeatmapDifficulty difficulty) { From e57a2294743e216415aeffa3cfdc12514721dd49 Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 9 Jun 2020 20:22:30 +0200 Subject: [PATCH 54/65] Move all the graphics related code to TournamentGame --- osu.Game.Tournament/TournamentGame.cs | 97 +++++++++++++++++++++-- osu.Game.Tournament/TournamentGameBase.cs | 94 +--------------------- 2 files changed, 92 insertions(+), 99 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 78bb66d553..8a0190b902 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -1,11 +1,19 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Drawing; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Colour; using osu.Game.Graphics.Cursor; using osu.Game.Tournament.Models; +using osu.Game.Graphics; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tournament @@ -21,17 +29,94 @@ namespace osu.Game.Tournament public static readonly Color4 ELEMENT_FOREGROUND_COLOUR = Color4Extensions.FromHex("#000"); public static readonly Color4 TEXT_COLOUR = Color4Extensions.FromHex("#fff"); + private Drawable heightWarning; + private Bindable windowSize; + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager frameworkConfig) + { + windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); + windowSize.BindValueChanged(size => ScheduleAfterChildren(() => + { + var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; + + heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; + }), true); + + AddRange(new[] + { + new Container + { + CornerRadius = 10, + Depth = float.MinValue, + Position = new Vector2(5), + Masking = true, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Children = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.2f), + RelativeSizeAxes = Axes.Both, + }, + new TourneyButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Padding = new MarginPadding + { + Top = 10, + Left = 10, + }, + Margin = new MarginPadding + { + Right = 10, + Bottom = 10, + }, + Action = SaveChanges, + }, + } + }, + heightWarning = new Container + { + Masking = true, + CornerRadius = 5, + Depth = float.MinValue, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Red, + RelativeSizeAxes = Axes.Both, + }, + new TournamentSpriteText + { + Text = "Please make the window wider", + Font = OsuFont.Torus.With(weight: FontWeight.Bold), + Colour = Color4.White, + Padding = new MarginPadding(20) + } + } + }, + new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = new TournamentSceneManager() + } + }); + } protected override void LoadComplete() { base.LoadComplete(); - Add(new OsuContextMenuContainer - { - RelativeSizeAxes = Axes.Both, - Child = new TournamentSceneManager() - }); - + MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display // we don't want to show the menu cursor as it would appear on stream output. MenuCursorContainer.Cursor.Alpha = 0; } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 85db9e61fb..cc7bb863ed 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -2,28 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Drawing; using System.IO; using System.Linq; using Newtonsoft.Json; using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; -using osu.Game.Graphics; using osu.Game.Online.API.Requests; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; using osu.Game.Users; -using osuTK; -using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Tournament @@ -40,19 +31,15 @@ namespace osu.Game.Tournament private TournamentStorage tournamentStorage; private DependencyContainer dependencies; - - private Bindable windowSize; private FileBasedIPC ipc; - private Drawable heightWarning; - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { return dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); } [BackgroundDependencyLoader] - private void load(Storage storage, FrameworkConfigManager frameworkConfig) + private void load(Storage storage) { Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly)); @@ -62,83 +49,12 @@ namespace osu.Game.Tournament this.storage = storage; - windowSize = frameworkConfig.GetBindable(FrameworkSetting.WindowedSize); - windowSize.BindValueChanged(size => ScheduleAfterChildren(() => - { - var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; - - heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; - }), true); - readBracket(); ladder.CurrentMatch.Value = ladder.Matches.FirstOrDefault(p => p.Current.Value); dependencies.CacheAs(ipc = new FileBasedIPC()); Add(ipc); - - AddRange(new[] - { - new Container - { - CornerRadius = 10, - Depth = float.MinValue, - Position = new Vector2(5), - Masking = true, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Children = new Drawable[] - { - new Box - { - Colour = OsuColour.Gray(0.2f), - RelativeSizeAxes = Axes.Both, - }, - new TourneyButton - { - Text = "Save Changes", - Width = 140, - Height = 50, - Padding = new MarginPadding - { - Top = 10, - Left = 10, - }, - Margin = new MarginPadding - { - Right = 10, - Bottom = 10, - }, - Action = SaveChanges, - }, - } - }, - heightWarning = new Container - { - Masking = true, - CornerRadius = 5, - Depth = float.MinValue, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - Colour = Color4.Red, - RelativeSizeAxes = Axes.Both, - }, - new TournamentSpriteText - { - Text = "Please make the window wider", - Font = OsuFont.Torus.With(weight: FontWeight.Bold), - Colour = Color4.White, - Padding = new MarginPadding(20) - } - } - }, - }); } private void readBracket() @@ -313,14 +229,6 @@ namespace osu.Game.Tournament API.Queue(req); } - protected override void LoadComplete() - { - MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display - MenuCursorContainer.Cursor.Alpha = 0; - - base.LoadComplete(); - } - protected virtual void SaveChanges() { foreach (var r in ladder.Rounds) From af05ee67cbe67e0577a9cdb8bad11aa01dbfd22a Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 9 Jun 2020 20:30:15 +0200 Subject: [PATCH 55/65] move base.loadcomplete to the bottom --- osu.Game.Tournament/TournamentGame.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 8a0190b902..3392440902 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -114,11 +114,12 @@ namespace osu.Game.Tournament protected override void LoadComplete() { - base.LoadComplete(); - MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display + // we don't want to show the menu cursor as it would appear on stream output. MenuCursorContainer.Cursor.Alpha = 0; + + base.LoadComplete(); } } } From c9b4fa92f57b6c6023cbedb9c43aa846b1c7b855 Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 9 Jun 2020 20:40:54 +0200 Subject: [PATCH 56/65] Hide in-game cursor manually in the testbrowser --- osu.Game.Tournament.Tests/TournamentTestBrowser.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs index f7ad757926..3bc719be7c 100644 --- a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs +++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs @@ -19,6 +19,9 @@ namespace osu.Game.Tournament.Tests Depth = 10 }, AddInternal); + MenuCursorContainer.Cursor.AlwaysPresent = true; + MenuCursorContainer.Cursor.Alpha = 0; + // Have to construct this here, rather than in the constructor, because // we depend on some dependencies to be loaded within OsuGameBase.load(). Add(new TestBrowser()); From aacacd75f08f289fa77cc5d9be211145b8378b7d Mon Sep 17 00:00:00 2001 From: Shivam Date: Tue, 9 Jun 2020 21:14:05 +0200 Subject: [PATCH 57/65] Remove abstract from the class --- osu.Game.Tournament/TournamentGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index cc7bb863ed..bb8c134ecb 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -20,7 +20,7 @@ using osuTK.Input; namespace osu.Game.Tournament { [Cached(typeof(TournamentGameBase))] - public abstract class TournamentGameBase : OsuGameBase + public class TournamentGameBase : OsuGameBase { private const string bracket_filename = "bracket.json"; From 0f39558da2aa09555c0a50e5bb74b32325d07a16 Mon Sep 17 00:00:00 2001 From: Shivam Date: Wed, 10 Jun 2020 08:04:34 +0200 Subject: [PATCH 58/65] Apply review comment --- osu.Game.Tournament.Tests/TournamentTestBrowser.cs | 3 --- osu.Game.Tournament/TournamentGame.cs | 10 ---------- osu.Game.Tournament/TournamentGameBase.cs | 9 +++++++++ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs index 3bc719be7c..f7ad757926 100644 --- a/osu.Game.Tournament.Tests/TournamentTestBrowser.cs +++ b/osu.Game.Tournament.Tests/TournamentTestBrowser.cs @@ -19,9 +19,6 @@ namespace osu.Game.Tournament.Tests Depth = 10 }, AddInternal); - MenuCursorContainer.Cursor.AlwaysPresent = true; - MenuCursorContainer.Cursor.Alpha = 0; - // Have to construct this here, rather than in the constructor, because // we depend on some dependencies to be loaded within OsuGameBase.load(). Add(new TestBrowser()); diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 3392440902..7b1a174c1e 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -111,15 +111,5 @@ namespace osu.Game.Tournament } }); } - - protected override void LoadComplete() - { - MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display - - // we don't want to show the menu cursor as it would appear on stream output. - MenuCursorContainer.Cursor.Alpha = 0; - - base.LoadComplete(); - } } } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index bb8c134ecb..0160065cc4 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -228,6 +228,15 @@ namespace osu.Game.Tournament API.Queue(req); } + protected override void LoadComplete() + { + MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display + + // we don't want to show the menu cursor as it would appear on stream output. + MenuCursorContainer.Cursor.Alpha = 0; + + base.LoadComplete(); + } protected virtual void SaveChanges() { From a43e1a0ae345722b922cd7b3bb0b533969657103 Mon Sep 17 00:00:00 2001 From: Shivam Date: Wed, 10 Jun 2020 08:41:13 +0200 Subject: [PATCH 59/65] Remove whitespace --- osu.Game.Tournament/TournamentGameBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 0160065cc4..d17b93bf5d 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -228,6 +228,7 @@ namespace osu.Game.Tournament API.Queue(req); } + protected override void LoadComplete() { MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display From b89dcb6a77de715a84faa28fd1b91fc37b167e5b Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Thu, 11 Jun 2020 13:02:47 +0930 Subject: [PATCH 60/65] Fix cursor not hiding with SDL2 backend --- osu.Desktop/OsuGameDesktop.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 5f74883803..bca30f3f9e 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -122,14 +122,22 @@ namespace osu.Desktop { base.SetHost(host); - if (host.Window is DesktopGameWindow desktopWindow) + switch (host.Window) { - desktopWindow.CursorState |= CursorState.Hidden; + // Legacy osuTK DesktopGameWindow + case DesktopGameWindow desktopGameWindow: + desktopGameWindow.CursorState |= CursorState.Hidden; + desktopGameWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); + desktopGameWindow.Title = Name; + desktopGameWindow.FileDrop += fileDrop; + break; - desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); - desktopWindow.Title = Name; - - desktopWindow.FileDrop += fileDrop; + // SDL2 DesktopWindow + case DesktopWindow desktopWindow: + desktopWindow.CursorState.Value |= CursorState.Hidden; + desktopWindow.Title = Name; + desktopWindow.FileDrop += fileDrop; + break; } } From 702bd2b65d4c4546a57d83b4decaee64304013af Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Jun 2020 13:41:53 +0900 Subject: [PATCH 61/65] Fix potential nullref in test --- .../SongSelect/TestScenePlaySongSelect.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index a7e2dbeccb..f7d66ca5cf 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -38,13 +38,9 @@ namespace osu.Game.Tests.Visual.SongSelect public class TestScenePlaySongSelect : ScreenTestScene { private BeatmapManager manager; - private RulesetStore rulesets; - private MusicController music; - private WorkingBeatmap defaultBeatmap; - private TestSongSelect songSelect; [BackgroundDependencyLoader] @@ -308,15 +304,13 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap); - var sortMode = config.GetBindable(OsuSetting.SongSelectSortingMode); - - AddStep(@"Sort by Artist", delegate { sortMode.Value = SortMode.Artist; }); - AddStep(@"Sort by Title", delegate { sortMode.Value = SortMode.Title; }); - AddStep(@"Sort by Author", delegate { sortMode.Value = SortMode.Author; }); - AddStep(@"Sort by DateAdded", delegate { sortMode.Value = SortMode.DateAdded; }); - AddStep(@"Sort by BPM", delegate { sortMode.Value = SortMode.BPM; }); - AddStep(@"Sort by Length", delegate { sortMode.Value = SortMode.Length; }); - AddStep(@"Sort by Difficulty", delegate { sortMode.Value = SortMode.Difficulty; }); + AddStep(@"Sort by Artist", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Artist)); + AddStep(@"Sort by Title", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Title)); + AddStep(@"Sort by Author", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Author)); + AddStep(@"Sort by DateAdded", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.DateAdded)); + AddStep(@"Sort by BPM", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.BPM)); + AddStep(@"Sort by Length", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Length)); + AddStep(@"Sort by Difficulty", () => config.Set(OsuSetting.SongSelectSortingMode, SortMode.Difficulty)); } [Test] From 7b012f1def0eb2c0e8e1d0af48ff86184224a802 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Jun 2020 14:55:49 +0900 Subject: [PATCH 62/65] Fix test failures --- .../Background/TestSceneUserDimBackgrounds.cs | 12 ++++++-- .../TestSceneExpandedPanelMiddleContent.cs | 30 +++++++++---------- .../Expanded/ExpandedPanelMiddleContent.cs | 2 +- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index d601f40afe..19294d12fc 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens; @@ -27,6 +28,7 @@ using osu.Game.Screens.Play; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; +using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Resources; using osu.Game.Users; using osuTK; @@ -186,9 +188,15 @@ namespace osu.Game.Tests.Visual.Background public void TestTransition() { performFullSetup(); + FadeAccessibleResults results = null; - AddStep("Transition to Results", () => player.Push(results = - new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } }))); + + AddStep("Transition to Results", () => player.Push(results = new FadeAccessibleResults(new ScoreInfo + { + User = new User { Username = "osu!" }, + Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo + }))); + AddUntilStep("Wait for results is current", () => results.IsCurrentScreen()); AddUntilStep("Screen is undimmed, original background retained", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect()); diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs index 69511b85c0..7be44a62de 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneExpandedPanelMiddleContent.cs @@ -4,7 +4,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -33,7 +32,10 @@ namespace osu.Game.Tests.Visual.Ranking { var author = new User { Username = "mapper_name" }; - AddStep("show example score", () => showPanel(createTestBeatmap(author), new TestScoreInfo(new OsuRuleset().RulesetInfo))); + AddStep("show example score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo) + { + Beatmap = createTestBeatmap(author) + })); AddAssert("mapper name present", () => this.ChildrenOfType().Any(spriteText => spriteText.Text == "mapper_name")); } @@ -41,38 +43,34 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestMapWithUnknownMapper() { - AddStep("show example score", () => showPanel(createTestBeatmap(null), new TestScoreInfo(new OsuRuleset().RulesetInfo))); + AddStep("show example score", () => showPanel(new TestScoreInfo(new OsuRuleset().RulesetInfo) + { + Beatmap = createTestBeatmap(null) + })); AddAssert("mapped by text not present", () => this.ChildrenOfType().All(spriteText => !containsAny(spriteText.Text, "mapped", "by"))); } - private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score) - { - Child = new ExpandedPanelMiddleContentContainer(workingBeatmap, score); - } + private void showPanel(ScoreInfo score) => Child = new ExpandedPanelMiddleContentContainer(score); - private WorkingBeatmap createTestBeatmap(User author) + private BeatmapInfo createTestBeatmap(User author) { - var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)); + var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0)).BeatmapInfo; + beatmap.Metadata.Author = author; beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title"; beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist"; - return new TestWorkingBeatmap(beatmap); + return beatmap; } private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains); private class ExpandedPanelMiddleContentContainer : Container { - [Cached] - private Bindable workingBeatmap { get; set; } - - public ExpandedPanelMiddleContentContainer(WorkingBeatmap beatmap, ScoreInfo score) + public ExpandedPanelMiddleContentContainer(ScoreInfo score) { - workingBeatmap = new Bindable(beatmap); - Anchor = Anchor.Centre; Origin = Anchor.Centre; Size = new Vector2(ScorePanel.EXPANDED_WIDTH, 700); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index b06ef8ae83..01502c0913 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Ranking.Expanded private void load() { var beatmap = score.Beatmap; - var metadata = beatmap.Metadata; + var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata; var creator = metadata.Author?.Username; var topStatistics = new List From b7c1cfbe6306b262db5eb1880c74fe105147df78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 11 Jun 2020 15:07:14 +0900 Subject: [PATCH 63/65] Adjust display to avoid overlaps --- osu.Game/Screens/Multi/Components/OverlinedDisplay.cs | 2 +- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Components/OverlinedDisplay.cs b/osu.Game/Screens/Multi/Components/OverlinedDisplay.cs index 71cabd8b50..8d8d4cc404 100644 --- a/osu.Game/Screens/Multi/Components/OverlinedDisplay.cs +++ b/osu.Game/Screens/Multi/Components/OverlinedDisplay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Multi.Components }, new Drawable[] { - Content = new Container { Margin = new MarginPadding { Top = 5 } } + Content = new Container { Padding = new MarginPadding { Top = 5 } } } } }; diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 9296fe81bd..f837a407a5 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -130,6 +130,7 @@ namespace osu.Game.Screens.Multi.Match SelectedItem = { BindTarget = SelectedItem } } }, + null, new Drawable[] { new TriangleButton @@ -139,6 +140,12 @@ namespace osu.Game.Screens.Multi.Match Action = showBeatmapResults } } + }, + RowDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 5), + new Dimension(GridSizeMode.AutoSize) } } }, From fca6a6d69f3724091c2f72b1f4ea7034614633d7 Mon Sep 17 00:00:00 2001 From: Shane Woolcock Date: Fri, 12 Jun 2020 09:46:21 +0930 Subject: [PATCH 64/65] Implement file drop with DragDrop event --- osu.Desktop/OsuGameDesktop.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index bca30f3f9e..cd31df316a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -10,7 +10,6 @@ using Microsoft.Win32; using osu.Desktop.Overlays; using osu.Framework.Platform; using osu.Game; -using osuTK.Input; using osu.Desktop.Updater; using osu.Framework; using osu.Framework.Logging; @@ -129,22 +128,20 @@ namespace osu.Desktop desktopGameWindow.CursorState |= CursorState.Hidden; desktopGameWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); desktopGameWindow.Title = Name; - desktopGameWindow.FileDrop += fileDrop; + desktopGameWindow.FileDrop += (_, e) => fileDrop(e.FileNames); break; // SDL2 DesktopWindow case DesktopWindow desktopWindow: desktopWindow.CursorState.Value |= CursorState.Hidden; desktopWindow.Title = Name; - desktopWindow.FileDrop += fileDrop; + desktopWindow.DragDrop += f => fileDrop(new[] { f }); break; } } - private void fileDrop(object sender, FileDropEventArgs e) + private void fileDrop(string[] filePaths) { - var filePaths = e.FileNames; - var firstExtension = Path.GetExtension(filePaths.First()); if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; From a48e36fd31d5e4850a3f132883173a794a457c25 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 12 Jun 2020 12:58:33 +0900 Subject: [PATCH 65/65] Fix dotnet publish with runtime specification not working --- osu.Desktop/osu.Desktop.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index c34e1e1221..7a99c70999 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -30,6 +30,10 @@ + + + +