From ee591829899d2fd64938bd9ac406e88ad72b9082 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 May 2020 18:12:19 +0900 Subject: [PATCH 01/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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/78] 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 40e64eed475b7c09de60643671035e5cd0ec9967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jun 2020 23:15:14 +0200 Subject: [PATCH 16/78] Add contributing guidelines --- CONTRIBUTING.md | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..441521f9ef --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,121 @@ +# Contributing Guidelines + +Thank you for showing interest in the development of osu!lazer! We aim to provide a good collaborating environment for everyone involved, and as such have decided to list some of the most important things to keep in mind in the process. The guidelines below have been chosen based on past experience. + +These are not "official rules" *per se*, but following them will help everyone deal with things in the most efficient manner. + +## Table of contents + +1. [I would like to submit an issue!](#i-would-like-to-submit-an-issue) +2. [I would like to submit a pull request!](#i-would-like-to-submit-a-pull-request) + +## I would like to submit an issue! + +When it comes to issues, bug reports and feature suggestions are welcomed, although please keep in mind that at any point in time, hundreds of issues are open, which vary in severity and the amount of time needed to address them. As such it's not uncommon for issues to remain unresolved for a long time or even closed outright if they are deemed not important enough to fix in the foreseeable future. Issues that are required to "go live" or otherwise achieve parity with stable are prioritised the most. + +* **Before submitting an issue, try searching existing issues first.** + + For housekeeping purposes, we close issues that overlap with or duplicate other pre-existing issues - you can help us not have to do that by searching existing issues yourself first. The issue search box, as well as the issue tag system, are tools you can use to check if an issue has been reported before. + +* **When submitting a bug report, please try to include as much detail as possible.** + + Bugs are not equal - some of them will be reproducible every time on pretty much all hardware, while others will be hard to track down due to being specific to particular hardware or even somewhat random in nature. As such, providing as much detail as possible when reporting a bug is hugely appreciated. A good starting set of information contains of: + + * the in-game logs, which are located at: + * `%AppData%/osu/logs` (on Windows), + * `~/.local/share/osu/logs` (on Linux and macOS), + * `Android/Data/sh.ppy.osulazer/logs` (on Android), + * on iOS they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes, + * your system specifications (including the operating system and platform you are playing on), + * a reproduction scenario (list of steps you have performed leading up to the occurrence of the bug), + * a video or picture of the bug, if at all possible. + +* **Provide more information when asked to do so.** + + Sometimes when a bug is more elusive or complicated, none of the information listed above will pinpoint a concrete cause of the problem. In this case we will most likely ask you for additional info, such as a Windows Event Log dump or a copy of your local lazer database (`client.db`). Providing that information is beneficial to both parties - we can track down the problem better, and hopefully fix it for you at some point once we know where it is! + +* **When submitting a feature proposal, please describe it in the most understandable way you can.** + + Communicating your idea for a feature can often be hard, and we would like to avoid any misunderstandings. As such, please try to explain your idea in a short, but understandable manner - it's best to avoid jargon or terms and references that could be considered obscure. A mock-up picture (doesn't have to be good!) of the feature can also go a long way in explaining. + +* **Refrain from posting "+1" comments.** + + If an issue has already been created, saying that you also experience it without providing any additional details doesn't really help us in any way. To express support for a proposal or indicate that you are also affected by a particular bug, you can use comment reactions instead. + +* **Refrain from asking if an issue has been resolved yet.** + + As mentioned above, the issue tracker has hundreds of issues open at any given time. Currently the game is being worked on by two members of the core team, and a handful of outside contributors who offer their free time to help out. As such, it can happen that an issue gets placed on the backburner due to being less important; generally posting a comment demanding its resolution some months or years after it is reported is not very likely to increase its priority. + +* **Avoid long discussions about non-development topics.** + + GitHub is mostly a developer space, and as such isn't really fit for lengthened discussions about gameplay mechanics (which might not even be in any way confirmed for the final release) and similar non-technical matters. Such matters are probably best addressed at the osu! forums. + +## I would like to submit a pull request! + +We also welcome pull requests from unaffiliated contributors. The issue tracker should provide plenty of issues that you can work on; we also mark issues that we think would be good for newcomers with the [`good-first-issue`](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue) label. + +However, do keep in mind that the core team is committed to bringing osu!lazer up to par with stable first and foremost, so depending on what your contribution concerns, it might not be merged and released right away. Our approach to managing issues and their priorities is described [in the wiki](https://github.com/ppy/osu/wiki/Project-management). + +Here are some key things to note before jumping in: + +* **Make sure you are comfortable with C\# and your development environment.** + + While we are accepting of all kinds of contributions, we also have a certain quality standard we'd like to uphold and limited time to review your code. Therefore, we would like to avoid providing entry-level advice, and as such if you're not very familiar with C\# as a programming language, we'd recommend that you start off with a few personal projects to get acquainted with the language's syntax, toolchain and principles of object-oriented programming first. + +* **Make sure you are familiar with git and the pull request workflow.** + + [git](https://git-scm.com/) is a distributed version control system that might not be very intuitive at the beginning if you're not familiar with version control. In particular, projects using git have a particular workflow for submitting code changes, which is called the pull request workflow. + + To make things run more smoothly, we recommend that you look up some online resources to familiarise yourself with the git vocabulary and commands, and practice working with forks and submitting pull requests at your own pace. A high-level overview of the process can be found in [this article by GitHub](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests). + +* **Make sure to submit pull requests off of a topic branch.** + + As described in the article linked in the previous point, topic branches help you parallelise your work and separate it from the main `master` branch, and additionally are easier for maintainers to work with. Working with multiple `master` branches across many remotes is difficult to keep track of, and it's easy to make a mistake and push to the wrong `master` branch by accident. + +* **Refrain from making changes through the GitHub web interface.** + + Even though GitHub provides an option to edit code or replace files in the repository using the web interface, we strongly discourage using it in most scenarios. Editing files this way is inefficient and likely to introduce whitespace or file encoding changes that make it more difficult to review the code. + + Code written through the web interface will also very likely be questioned outright by the reviewers, as it is likely that it has not been properly tested or that it will fail continuous integration checks. We strongly encourage using an IDE like [Visual Studio](https://visualstudio.microsoft.com/), [Visual Studio Code](https://code.visualstudio.com/) or [JetBrains Rider](https://www.jetbrains.com/rider/) instead. + +* **Add tests for your code whenever possible.** + + Automated tests are an essential part of a quality and reliable codebase. They help to make the code more maintainable by ensuring it is safe to reorganise (or refactor) the code in various ways, and also prevent regressions - bugs that resurface after having been fixed at some point in the past. If it is viable, please put in the time to add tests, so that the changes you make can last for a (hopefully) very long time. + +* **Run tests before opening a pull request.** + + Tying into the previous point, sometimes changes in one part of the codebase can result in unpredictable changes in behaviour in other pieces of the code. This is why it is best to always try to run tests before opening a PR. + + Continuous integration will always run the tests for you (and us), too, but it is best not to rely on it, as there might be many builds queued at any time. Running tests on your own will help you be more certain that at the point of clicking the "Create pull request" button, your changes are as ready as can be. + +* **Run code style analysis before opening a pull request.** + + As part of continuous integration, we also run code style analysis, which is supposed to make sure that your code is formatted the same way as all the pre-existing code in the repository. The reason we enforce a particular code style everywhere is to make sure the codebase is consistent in that regard - having one whitespace convention in one place and another one elsewhere causes disorganisation. + +* **Make sure to keep the *Allow edits from maintainers* check box checked.** + + To speed up the merging process, collaborators and team members will sometimes want to push changes to your branch themselves, to make minor code style adjustments or to otherwise refactor the code without having to describe how they'd like the code to look like in painstaking detail. Having the *Allow edits from maintainers* check box checked lets them do that; without it they are forced to report issues back to you and wait for you to address them. + +* **Refrain from continually merging the master branch back to the PR.** + + Unless there are merge conflicts that need resolution, there is no need to keep merging `master` back to a branch over and over again. One of the maintainers will merge `master` themselves before merging the PR itself anyway, and continual merge commits can cause CI to get overwhelmed due to queueing up too many builds. + +* **Refrain from force-pushing to the PR branch.** + + Force-pushing should be avoided, as it can lead to accidentally overwriting a maintainer's changes or CI building wrong commits. We value all history in the project, so there is no need to squash or amend commits in most cases. + + The cases in which force-pushing is warranted are very rare (such as accidentally leaking sensitive info in one of the files committed, adding unrelated files, or mis-merging a dependent PR). + +* **Be patient when waiting for the code to be reviewed and merged.** + + As much as we'd like to review all contributions as fast as possible, our time is limited, as team members have to work on their own tasks in addition to reviewing code. As such, work needs to be prioritised, and it can unfortunately take weeks or months for your PR to be merged, depending on how important it is deemed to be. + +* **Don't mistake criticism of code for criticism of your person.** + + As mentioned before, we are highly committed to quality when it comes to the lazer project. This means that contributions from less experienced community members can take multiple rounds of review to get to a mergeable state. We try our utmost best to never conflate a person with the code they authored, and to keep the discussion focused on the code at all times. Please consider our comments and requests a learning experience, and don't treat it as a personal attack. + +* **Feel free to reach out for help.** + + If you're uncertain about some part of the codebase or some inner workings of the game and framework, please reach out either by leaving a comment in the relevant issue or PR thread, or by posting a message in the [development Discord server](https://discord.gg/ppy). We will try to help you as much as we can. + + When it comes to which form of communication is best, GitHub generally lends better to longer-form discussions, while Discord is better for snappy call-and-response answers. Use your best discretion when deciding, and try to keep a single discussion in one place instead of moving back and forth. From 13622eff1f12e8338f96ff5005ccbc2a9e12b8f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 12:54:07 +0900 Subject: [PATCH 17/78] 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 18/78] 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 74875f9b629715a1e5a13e65704af277fb2c8c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jun 2020 06:47:10 +0200 Subject: [PATCH 19/78] Apply review suggestions & other cleanups --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 441521f9ef..331534ad73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,21 +11,21 @@ These are not "official rules" *per se*, but following them will help everyone d ## I would like to submit an issue! -When it comes to issues, bug reports and feature suggestions are welcomed, although please keep in mind that at any point in time, hundreds of issues are open, which vary in severity and the amount of time needed to address them. As such it's not uncommon for issues to remain unresolved for a long time or even closed outright if they are deemed not important enough to fix in the foreseeable future. Issues that are required to "go live" or otherwise achieve parity with stable are prioritised the most. +Issues, bug reports and feature suggestions are welcomed, though please keep in mind that at any point in time, hundreds of issues are open, which vary in severity and the amount of time needed to address them. As such it's not uncommon for issues to remain unresolved for a long time or even closed outright if they are deemed not important enough to fix in the foreseeable future. Issues that are required to "go live" or otherwise achieve parity with stable are prioritised the most. * **Before submitting an issue, try searching existing issues first.** - For housekeeping purposes, we close issues that overlap with or duplicate other pre-existing issues - you can help us not have to do that by searching existing issues yourself first. The issue search box, as well as the issue tag system, are tools you can use to check if an issue has been reported before. + For housekeeping purposes, we close issues that overlap with or duplicate other pre-existing issues - you can help us not to have to do that by searching existing issues yourself first. The issue search box, as well as the issue tag system, are tools you can use to check if an issue has been reported before. * **When submitting a bug report, please try to include as much detail as possible.** - Bugs are not equal - some of them will be reproducible every time on pretty much all hardware, while others will be hard to track down due to being specific to particular hardware or even somewhat random in nature. As such, providing as much detail as possible when reporting a bug is hugely appreciated. A good starting set of information contains of: + Bugs are not equal - some of them will be reproducible every time on pretty much all hardware, while others will be hard to track down due to being specific to particular hardware or even somewhat random in nature. As such, providing as much detail as possible when reporting a bug is hugely appreciated. A good starting set of information consists of: * the in-game logs, which are located at: * `%AppData%/osu/logs` (on Windows), * `~/.local/share/osu/logs` (on Linux and macOS), * `Android/Data/sh.ppy.osulazer/logs` (on Android), - * on iOS they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes, + * on iOS they can be obtained by connecting your device to your desktop and [copying the `logs` directory from the app's own document storage using iTunes](https://support.apple.com/en-us/HT201301#copy-to-computer), * your system specifications (including the operating system and platform you are playing on), * a reproduction scenario (list of steps you have performed leading up to the occurrence of the bug), * a video or picture of the bug, if at all possible. From 5f1d44a2bef173fc2745e0e1212672db4e3b5d86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 15:52:47 +0900 Subject: [PATCH 20/78] 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 21/78] 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 22/78] 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 23/78] 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 24/78] 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 25/78] 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 c155ab83399a51bdab44a48d5805463c8520318a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 18:03:10 +0900 Subject: [PATCH 26/78] Check filenames and timestamps before reusing an already imported model --- osu.Game/Beatmaps/BeatmapManager.cs | 4 ++-- osu.Game/Database/ArchiveModelManager.cs | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f626b45e42..e7cef13c68 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -258,9 +258,9 @@ namespace osu.Game.Beatmaps /// The first result for the provided query, or null if no results were found. public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query); - protected override bool CanUndelete(BeatmapSetInfo existing, BeatmapSetInfo import) + protected override bool CanReuseExisting(BeatmapSetInfo existing, BeatmapSetInfo import) { - if (!base.CanUndelete(existing, import)) + if (!base.CanReuseExisting(existing, import)) return false; var existingIds = existing.Beatmaps.Select(b => b.OnlineBeatmapID).OrderBy(i => i); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ae55a7b14a..4d7d3e96e6 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -332,7 +332,7 @@ namespace osu.Game.Database if (existing != null) { - if (CanUndelete(existing, item)) + if (CanReuseExisting(existing, item)) { Undelete(existing); LogForModel(item, $"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import."); @@ -660,13 +660,29 @@ namespace osu.Game.Database protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash); /// - /// After an existing is found during an import process, the default behaviour is to restore the existing + /// After an existing is found during an import process, the default behaviour is to use/restore the existing /// item and skip the import. This method allows changing that behaviour. /// /// The existing model. /// The newly imported model. /// Whether the existing model should be restored and used. Returning false will delete the existing and force a re-import. - protected virtual bool CanUndelete(TModel existing, TModel import) => true; + protected virtual bool CanReuseExisting(TModel existing, TModel import) => + getFilenames(existing.Files).SequenceEqual(getFilenames(import.Files)) && + // poor-man's (cheap) equality comparison, avoiding hashing unnecessarily. + // can switch to full hash checks on a per-case basis (or for all) if we decide this is not a performance issue. + getTimestamps(existing.Files).SequenceEqual(getTimestamps(import.Files)); + + private IEnumerable getFilenames(List files) + { + foreach (var f in files.OrderBy(f => f.Filename)) + yield return f.Filename; + } + + private IEnumerable getTimestamps(List files) + { + foreach (var f in files.OrderBy(f => f.Filename)) + yield return File.GetLastWriteTimeUtc(Files.Storage.GetFullPath(f.FileInfo.StoragePath)).ToFileTime(); + } private DbSet queryModel() => ContextFactory.Get().Set(); From 012933545eccd4510dc3c0a70b040610dad0bf8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 18:30:27 +0900 Subject: [PATCH 27/78] Add test coverage --- .../Beatmaps/IO/ImportBeatmapTest.cs | 161 ++++++++++++++++++ osu.Game/Database/ArchiveModelManager.cs | 2 +- 2 files changed, 162 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 5eb11a3264..12c9c92e90 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -12,6 +12,7 @@ using NUnit.Framework; using osu.Framework.Platform; using osu.Game.IPC; using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; @@ -22,6 +23,7 @@ using SharpCompress.Archives; using SharpCompress.Archives.Zip; using SharpCompress.Common; using SharpCompress.Writers.Zip; +using FileInfo = System.IO.FileInfo; namespace osu.Game.Tests.Beatmaps.IO { @@ -93,6 +95,165 @@ namespace osu.Game.Tests.Beatmaps.IO } } + [Test] + public async Task TestImportThenImportWithReZip() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithNewerTimestamp))) + { + try + { + var osu = loadOsu(host); + + var temp = TestResources.GetTestBeatmapForImport(); + + string extractedFolder = $"{temp}_extracted"; + Directory.CreateDirectory(extractedFolder); + + try + { + var imported = await LoadOszIntoOsu(osu); + + string hashBefore = hashFile(temp); + + using (var zip = ZipArchive.Open(temp)) + zip.WriteToDirectory(extractedFolder); + + using (var zip = ZipArchive.Create()) + { + zip.AddAllFromDirectory(extractedFolder); + zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); + } + + // zip files differ because different compression or encoder. + Assert.AreNotEqual(hashBefore, hashFile(temp)); + + var importedSecondTime = await osu.Dependencies.Get().Import(temp); + + ensureLoaded(osu); + + // but contents doesn't, so existing should still be used. + Assert.IsTrue(imported.ID == importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID); + } + finally + { + Directory.Delete(extractedFolder, true); + } + } + finally + { + host.Exit(); + } + } + } + + private string hashFile(string filename) + { + using (var s = File.OpenRead(filename)) + return s.ComputeMD5Hash(); + } + + [Test] + public async Task TestImportThenImportWithNewerTimestamp() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithNewerTimestamp))) + { + try + { + var osu = loadOsu(host); + + var temp = TestResources.GetTestBeatmapForImport(); + + string extractedFolder = $"{temp}_extracted"; + Directory.CreateDirectory(extractedFolder); + + try + { + var imported = await LoadOszIntoOsu(osu); + + using (var zip = ZipArchive.Open(temp)) + zip.WriteToDirectory(extractedFolder); + + // change timestamp + new FileInfo(Directory.GetFiles(extractedFolder).First()).LastWriteTime = DateTime.Now; + + using (var zip = ZipArchive.Create()) + { + zip.AddAllFromDirectory(extractedFolder); + zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); + } + + var importedSecondTime = await osu.Dependencies.Get().Import(temp); + + ensureLoaded(osu); + + // check the newly "imported" beatmap is not the original. + Assert.IsTrue(imported.ID != importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID); + } + finally + { + Directory.Delete(extractedFolder, true); + } + } + finally + { + host.Exit(); + } + } + } + + [Test] + public async Task TestImportThenImportWithDifferentFilename() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithDifferentFilename))) + { + try + { + var osu = loadOsu(host); + + var temp = TestResources.GetTestBeatmapForImport(); + + string extractedFolder = $"{temp}_extracted"; + Directory.CreateDirectory(extractedFolder); + + try + { + var imported = await LoadOszIntoOsu(osu); + + using (var zip = ZipArchive.Open(temp)) + zip.WriteToDirectory(extractedFolder); + + // change filename + var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First()); + firstFile.MoveTo(Path.Combine(firstFile.DirectoryName, $"{firstFile.Name}-changed{firstFile.Extension}")); + + using (var zip = ZipArchive.Create()) + { + zip.AddAllFromDirectory(extractedFolder); + zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate)); + } + + var importedSecondTime = await osu.Dependencies.Get().Import(temp); + + ensureLoaded(osu); + + // check the newly "imported" beatmap is not the original. + Assert.IsTrue(imported.ID != importedSecondTime.ID); + Assert.IsTrue(imported.Beatmaps.First().ID != importedSecondTime.Beatmaps.First().ID); + } + finally + { + Directory.Delete(extractedFolder, true); + } + } + finally + { + host.Exit(); + } + } + } + [Test] public async Task TestImportCorruptThenImport() { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 4d7d3e96e6..5ca9423de2 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -276,7 +276,7 @@ namespace osu.Game.Database // for now, concatenate all .osu files in the set to create a unique hash. MemoryStream hashable = new MemoryStream(); - foreach (TFileModel file in item.Files.Where(f => HashableFileTypes.Any(f.Filename.EndsWith))) + foreach (TFileModel file in item.Files.Where(f => HashableFileTypes.Any(f.Filename.EndsWith)).OrderBy(f => f.Filename)) { using (Stream s = Files.Store.GetStream(file.FileInfo.StoragePath)) s.CopyTo(hashable); From 6133c7d74784d0f848add452953ebf56aa39c0a2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Jun 2020 18:51:02 +0900 Subject: [PATCH 28/78] 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 25160dc220d9f2f0bde4125f0bafd7446e3ad354 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 19:15:52 +0900 Subject: [PATCH 29/78] Fix test name --- 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 12c9c92e90..12f06059f7 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Beatmaps.IO [Test] public async Task TestImportThenImportWithReZip() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithNewerTimestamp))) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithReZip))) { try { From 5ed3cd205f068d572caddc0ae01aa7ab8a580a6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 22:35:01 +0900 Subject: [PATCH 30/78] Simplify reuse check using FileInfo IDs --- .../Beatmaps/IO/ImportBeatmapTest.cs | 9 +++++---- osu.Game/Database/ArchiveModelManager.cs | 20 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 12f06059f7..9b34eece5f 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -154,9 +154,9 @@ namespace osu.Game.Tests.Beatmaps.IO } [Test] - public async Task TestImportThenImportWithNewerTimestamp() + public async Task TestImportThenImportWithChangedFile() { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithNewerTimestamp))) + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportWithChangedFile))) { try { @@ -174,8 +174,9 @@ namespace osu.Game.Tests.Beatmaps.IO using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(extractedFolder); - // change timestamp - new FileInfo(Directory.GetFiles(extractedFolder).First()).LastWriteTime = DateTime.Now; + // arbitrary write to non-hashed file + using (var sw = new FileInfo(Directory.GetFiles(extractedFolder, "*.mp3").First()).AppendText()) + sw.WriteLine("text"); using (var zip = ZipArchive.Create()) { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 5ca9423de2..0fe8dd1268 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -667,10 +667,16 @@ namespace osu.Game.Database /// The newly imported model. /// Whether the existing model should be restored and used. Returning false will delete the existing and force a re-import. protected virtual bool CanReuseExisting(TModel existing, TModel import) => - getFilenames(existing.Files).SequenceEqual(getFilenames(import.Files)) && - // poor-man's (cheap) equality comparison, avoiding hashing unnecessarily. - // can switch to full hash checks on a per-case basis (or for all) if we decide this is not a performance issue. - getTimestamps(existing.Files).SequenceEqual(getTimestamps(import.Files)); + // for the best or worst, we copy and import files of a new import before checking whether + // it is a duplicate. so to check if anything has changed, we can just compare all FileInfo IDs. + getIDs(existing.Files).SequenceEqual(getIDs(import.Files)) && + getFilenames(existing.Files).SequenceEqual(getFilenames(import.Files)); + + private IEnumerable getIDs(List files) + { + foreach (var f in files.OrderBy(f => f.Filename)) + yield return f.FileInfo.ID; + } private IEnumerable getFilenames(List files) { @@ -678,12 +684,6 @@ namespace osu.Game.Database yield return f.Filename; } - private IEnumerable getTimestamps(List files) - { - foreach (var f in files.OrderBy(f => f.Filename)) - yield return File.GetLastWriteTimeUtc(Files.Storage.GetFullPath(f.FileInfo.StoragePath)).ToFileTime(); - } - private DbSet queryModel() => ContextFactory.Get().Set(); protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; From 66ec2afe5cdcd9eb77adf5015966e5dcb652c1d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jun 2020 23:38:40 +0900 Subject: [PATCH 31/78] Remove broken import test --- .../Beatmaps/IO/ImportBeatmapTest.cs | 33 +------------------ 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 9b34eece5f..546bf758c1 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -1,4 +1,4 @@ -// 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; @@ -374,37 +374,6 @@ namespace osu.Game.Tests.Beatmaps.IO } } - [Test] - public async Task TestImportThenImportDifferentHash() - { - // unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here. - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportDifferentHash))) - { - try - { - var osu = loadOsu(host); - var manager = osu.Dependencies.Get(); - - var imported = await LoadOszIntoOsu(osu); - - imported.Hash += "-changed"; - manager.Update(imported); - - var importedSecondTime = await LoadOszIntoOsu(osu); - - Assert.IsTrue(imported.ID != importedSecondTime.ID); - Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID); - - // only one beatmap will exist as the online set ID matched, causing purging of the first import. - checkBeatmapSetCount(osu, 1); - } - finally - { - host.Exit(); - } - } - } - [Test] public async Task TestImportThenDeleteThenImport() { From c2fd2b861642a6fcac8412891d0365104c6ed6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jun 2020 23:20:43 +0200 Subject: [PATCH 32/78] Add notes about draft PRs & pushing --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 331534ad73..9666f249e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -92,6 +92,16 @@ Here are some key things to note before jumping in: As part of continuous integration, we also run code style analysis, which is supposed to make sure that your code is formatted the same way as all the pre-existing code in the repository. The reason we enforce a particular code style everywhere is to make sure the codebase is consistent in that regard - having one whitespace convention in one place and another one elsewhere causes disorganisation. +* **Make sure that the pull request is complete before opening it.** + + Whether it's fixing a bug or implementing new functionality, it's best that you make sure that the change you want to submit as a pull request is as complete as it can be before clicking the *Create pull request* button. Having to track if a pull request is ready for review or not places additional burden on reviewers. + + Draft pull requests are an option, but use them sparingly and within reason. They are best suited to discuss code changes that cannot be easily described in natural language or have a potential large impact on the future direction of the project. When in doubt, don't open drafts unless a maintainer asks you to do so. + +* **Only push code when it's ready.** + + As an extension of the above, when making changes to an already-open PR, please try to only push changes you are reasonably certain of. Pushing after every commit causes the continuous integration build queue to grow in size, slowing down work and taking up time that could be spent verifying other changes. + * **Make sure to keep the *Allow edits from maintainers* check box checked.** To speed up the merging process, collaborators and team members will sometimes want to push changes to your branch themselves, to make minor code style adjustments or to otherwise refactor the code without having to describe how they'd like the code to look like in painstaking detail. Having the *Allow edits from maintainers* check box checked lets them do that; without it they are forced to report issues back to you and wait for you to address them. From ddf5282d0e24d798a157d2c9704f8b30a7944731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jun 2020 23:33:49 +0200 Subject: [PATCH 33/78] Move items from README.md to contributing guidelines --- CONTRIBUTING.md | 8 +++++++- README.md | 6 ------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9666f249e2..6c327f01b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,7 +52,7 @@ Issues, bug reports and feature suggestions are welcomed, though please keep in ## I would like to submit a pull request! -We also welcome pull requests from unaffiliated contributors. The issue tracker should provide plenty of issues that you can work on; we also mark issues that we think would be good for newcomers with the [`good-first-issue`](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue) label. +We also welcome pull requests from unaffiliated contributors. The [issue tracker](https://github.com/ppy/osu/issues) should provide plenty of issues that you can work on; we also mark issues that we think would be good for newcomers with the [`good-first-issue`](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue) label. However, do keep in mind that the core team is committed to bringing osu!lazer up to par with stable first and foremost, so depending on what your contribution concerns, it might not be merged and released right away. Our approach to managing issues and their priorities is described [in the wiki](https://github.com/ppy/osu/wiki/Project-management). @@ -62,12 +62,18 @@ Here are some key things to note before jumping in: While we are accepting of all kinds of contributions, we also have a certain quality standard we'd like to uphold and limited time to review your code. Therefore, we would like to avoid providing entry-level advice, and as such if you're not very familiar with C\# as a programming language, we'd recommend that you start off with a few personal projects to get acquainted with the language's syntax, toolchain and principles of object-oriented programming first. + In addition, please take the time to take a look at and get acquainted with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. + * **Make sure you are familiar with git and the pull request workflow.** [git](https://git-scm.com/) is a distributed version control system that might not be very intuitive at the beginning if you're not familiar with version control. In particular, projects using git have a particular workflow for submitting code changes, which is called the pull request workflow. To make things run more smoothly, we recommend that you look up some online resources to familiarise yourself with the git vocabulary and commands, and practice working with forks and submitting pull requests at your own pace. A high-level overview of the process can be found in [this article by GitHub](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests). +* **Double-check designs before starting work on new functionality.** + + When implementing new features, keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention of having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time to ensure no effort is wasted. + * **Make sure to submit pull requests off of a topic branch.** As described in the article linked in the previous point, topic branches help you parallelise your work and separate it from the main `master` branch, and additionally are easier for maintainers to work with. Working with multiple `master` branches across many remotes is difficult to keep track of, and it's easy to make a mistake and push to the wrong `master` branch by accident. diff --git a/README.md b/README.md index 336bf33f7e..9e1cc20c8b 100644 --- a/README.md +++ b/README.md @@ -93,12 +93,6 @@ JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it ## Contributing -We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention of having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time to ensure no effort is wasted. - -If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aopen+label%3Agood-first-issue+sort%3Aupdated-desc) label). - -Before starting, please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**. - Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured, with any libraries we are using, or with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as painless as possible. For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. From af3daaaeafd294a740d9586ee56e13858b75b5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jun 2020 23:39:29 +0200 Subject: [PATCH 34/78] Add reference to contributing guidelines in README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9e1cc20c8b..dc3ee63844 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it ## Contributing +When it comes to contributing to the project, the two main things you can do to help out are reporting issues and submitting pull requests. Based on past experiences, we have prepared a [list of contributing guidelines](CONTRIBUTING.md) that should hopefully ease you into our collaboration process and answer the most frequently-asked questions. + Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured, with any libraries we are using, or with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as painless as possible. For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. From 9920911390833ffb35fddffaaa62803ebf92ecd1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jun 2020 17:20:08 +0900 Subject: [PATCH 35/78] 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 5d7bb8cb4e9e44eeb8a504f7d8e43f9046003aca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 4 Jun 2020 21:33:38 +0900 Subject: [PATCH 36/78] Change format of date on score panel --- osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index fd8ac33aef..81d5d113ae 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -211,7 +211,7 @@ namespace osu.Game.Screens.Ranking.Expanded Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold), - Text = $"Played on {score.Date.ToLocalTime():g}" + Text = $"Played on {score.Date.ToLocalTime():d MMMM yyyy HH:mm}" } } }; From afcefe01bf74177240c59e70b3ea2b87745a4223 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 4 Jun 2020 21:48:55 +0900 Subject: [PATCH 37/78] Fix score panel not receiving input in some places --- osu.Game/Screens/Ranking/ScorePanel.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index a99b48e8f0..65fb901c89 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -243,5 +243,10 @@ namespace osu.Game.Screens.Ranking return true; } + + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + => base.ReceivePositionalInputAt(screenSpacePos) + || topLayerContainer.ReceivePositionalInputAt(screenSpacePos) + || middleLayerContainer.ReceivePositionalInputAt(screenSpacePos); } } From 9c1542f8979637fcea83b327b5252f2fae8933a1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 4 Jun 2020 22:17:00 +0900 Subject: [PATCH 38/78] Fix crash when pressing clear button twice --- .../Settings/TestSceneKeyBindingPanel.cs | 45 ++++++++++++++++++- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 5 ++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 745820696a..3d335995ac 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -1,13 +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.Diagnostics; +using System.Linq; using NUnit.Framework; +using osu.Framework.Testing; +using osu.Framework.Threading; using osu.Game.Overlays; +using osu.Game.Overlays.KeyBinding; +using osuTK.Input; namespace osu.Game.Tests.Visual.Settings { [TestFixture] - public class TestSceneKeyBindingPanel : OsuTestScene + public class TestSceneKeyBindingPanel : OsuManualInputManagerTestScene { private readonly KeyBindingPanel panel; @@ -21,5 +27,42 @@ namespace osu.Game.Tests.Visual.Settings base.LoadComplete(); panel.Show(); } + + [Test] + public void TestClickTwiceOnClearButton() + { + KeyBindingRow firstRow = null; + + AddStep("click first row", () => + { + firstRow = panel.ChildrenOfType().First(); + InputManager.MoveMouseTo(firstRow); + InputManager.Click(MouseButton.Left); + }); + + AddStep("schedule button clicks", () => + { + var clearButton = firstRow.ChildrenOfType().Single(); + + InputManager.MoveMouseTo(clearButton); + + int buttonClicks = 0; + ScheduledDelegate clickDelegate = null; + + clickDelegate = Scheduler.AddDelayed(() => + { + InputManager.PressButton(MouseButton.Left); + InputManager.ReleaseButton(MouseButton.Left); + + if (++buttonClicks == 2) + { + // ReSharper disable once AccessToModifiedClosure + Debug.Assert(clickDelegate != null); + // ReSharper disable once AccessToModifiedClosure + clickDelegate.Cancel(); + } + }, 0, true); + }); + } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 01d5991d3e..eafb4572ca 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -274,6 +274,9 @@ namespace osu.Game.Overlays.KeyBinding private void clear() { + if (bindTarget == null) + return; + bindTarget.UpdateKeyCombination(InputKey.None); finalise(); } @@ -333,7 +336,7 @@ namespace osu.Game.Overlays.KeyBinding } } - private class ClearButton : TriangleButton + public class ClearButton : TriangleButton { public ClearButton() { 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 39/78] 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 40/78] 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 41/78] 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 42/78] 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 43/78] 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 44/78] 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 45/78] 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 46/78] 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 47/78] 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 48/78] 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 49/78] 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 50/78] 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 51/78] 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 52/78] 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 53/78] 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 54/78] 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 55/78] 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 56/78] 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 57/78] 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 58/78] 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 59/78] 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 60/78] 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 61/78] 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 62/78] 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 63/78] 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 64/78] 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 65/78] 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 66/78] 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 67/78] 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 68/78] 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 69/78] 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 70/78] 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 71/78] 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 72/78] 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 73/78] 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 74/78] 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 75/78] 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 76/78] 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 77/78] 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 78/78] 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 @@ + + + +