From aec3753863db310227c684e1686eb57c5bc68585 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 3 Jan 2019 13:57:56 +0900 Subject: [PATCH 001/167] Fix imported replays having excess statistics --- .../Scoring/CatchScoreProcessor.cs | 2 +- .../Scoring/ManiaScoreProcessor.cs | 2 +- .../Scoring/OsuScoreProcessor.cs | 2 +- .../Scoring/TaikoScoreProcessor.cs | 2 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 32 ++++++++++++------- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 57f4355d6a..54c572bcab 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -43,6 +43,6 @@ namespace osu.Game.Rulesets.Catch.Scoring Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness; } - protected override HitWindows CreateHitWindows() => new CatchHitWindows(); + public override HitWindows CreateHitWindows() => new CatchHitWindows(); } } diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 20a665c314..a053a6be97 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -159,6 +159,6 @@ namespace osu.Game.Rulesets.Mania.Scoring } } - protected override HitWindows CreateHitWindows() => new ManiaHitWindows(); + public override HitWindows CreateHitWindows() => new ManiaHitWindows(); } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index a24efe4a1e..1a63ff75c4 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -74,6 +74,6 @@ namespace osu.Game.Rulesets.Osu.Scoring protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement); - protected override HitWindows CreateHitWindows() => new OsuHitWindows(); + public override HitWindows CreateHitWindows() => new OsuHitWindows(); } } diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index 87481c800d..900a2c8148 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -67,6 +67,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring Health.Value = 0; } - protected override HitWindows CreateHitWindows() => new TaikoHitWindows(); + public override HitWindows CreateHitWindows() => new TaikoHitWindows(); } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 4b3012192d..576937f94e 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Scoring /// /// Create a for this processor. /// - protected virtual HitWindows CreateHitWindows() => new HitWindows(); + public virtual HitWindows CreateHitWindows() => new HitWindows(); /// /// The current rank. diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index 3184f776a7..1009e7065e 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -57,12 +57,20 @@ namespace osu.Game.Scoring.Legacy var countKatu = (int)sr.ReadUInt16(); var countMiss = (int)sr.ReadUInt16(); - score.ScoreInfo.Statistics[HitResult.Great] = count300; - score.ScoreInfo.Statistics[HitResult.Good] = count100; - score.ScoreInfo.Statistics[HitResult.Meh] = count50; - score.ScoreInfo.Statistics[HitResult.Perfect] = countGeki; - score.ScoreInfo.Statistics[HitResult.Ok] = countKatu; - score.ScoreInfo.Statistics[HitResult.Miss] = countMiss; + var windows = currentRuleset.CreateRulesetContainerWith(workingBeatmap).CreateScoreProcessor().CreateHitWindows(); + + if (windows.IsHitResultAllowed(HitResult.Great)) + score.ScoreInfo.Statistics[HitResult.Great] = count300; + if (windows.IsHitResultAllowed(HitResult.Good)) + score.ScoreInfo.Statistics[HitResult.Good] = count100; + if (windows.IsHitResultAllowed(HitResult.Meh)) + score.ScoreInfo.Statistics[HitResult.Meh] = count50; + if (windows.IsHitResultAllowed(HitResult.Perfect)) + score.ScoreInfo.Statistics[HitResult.Perfect] = countGeki; + if (windows.IsHitResultAllowed(HitResult.Ok)) + score.ScoreInfo.Statistics[HitResult.Ok] = countKatu; + if (windows.IsHitResultAllowed(HitResult.Miss)) + score.ScoreInfo.Statistics[HitResult.Miss] = countMiss; score.ScoreInfo.TotalScore = sr.ReadInt32(); score.ScoreInfo.MaxCombo = sr.ReadUInt16(); @@ -116,12 +124,12 @@ namespace osu.Game.Scoring.Legacy private void calculateAccuracy(ScoreInfo score) { - int countMiss = score.Statistics[HitResult.Miss]; - int count50 = score.Statistics[HitResult.Meh]; - int count100 = score.Statistics[HitResult.Good]; - int count300 = score.Statistics[HitResult.Great]; - int countGeki = score.Statistics[HitResult.Perfect]; - int countKatu = score.Statistics[HitResult.Ok]; + score.Statistics.TryGetValue(HitResult.Miss, out int countMiss); + score.Statistics.TryGetValue(HitResult.Meh, out int count50); + score.Statistics.TryGetValue(HitResult.Good, out int count100); + score.Statistics.TryGetValue(HitResult.Great, out int count300); + score.Statistics.TryGetValue(HitResult.Perfect, out int countGeki); + score.Statistics.TryGetValue(HitResult.Ok, out int countKatu); switch (score.Ruleset.ID) { From b62630ecd22c86a632584946e4ce189f257a032b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 17 Jan 2019 21:10:34 +0900 Subject: [PATCH 002/167] Centralise download tracking logic so we can show it in multiple scenarios --- .../BeatmapSet/Buttons/DownloadButton.cs | 70 +++++++++-------- osu.Game/Overlays/Direct/DirectPanel.cs | 60 +-------------- .../Overlays/Direct/DownloadProgressBar.cs | 70 +++++++++++++++++ .../Direct/DownloadTrackingComponent.cs | 77 +++++++++++++++++++ 4 files changed, 188 insertions(+), 89 deletions(-) create mode 100644 osu.Game/Overlays/Direct/DownloadProgressBar.cs create mode 100644 osu.Game/Overlays/Direct/DownloadTrackingComponent.cs diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index 8406dada44..9d9f71a7fa 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; +using osu.Game.Overlays.Direct; using osu.Game.Users; using osuTK; using osuTK.Graphics; @@ -28,44 +29,53 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons Width = 120; BeatmapSetDownloader downloader; - Add(new Container + AddRange(new Drawable[] { - Depth = -1, - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 10 }, - Children = new Drawable[] + new Container { - downloader = new BeatmapSetDownloader(set, noVideo), - new FillFlowContainer + Depth = -1, + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 10 }, + Children = new Drawable[] { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new[] + downloader = new BeatmapSetDownloader(set, noVideo), + new FillFlowContainer { - new OsuSpriteText + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new[] { - Text = "Download", - TextSize = 13, - Font = @"Exo2.0-Bold", - }, - new OsuSpriteText - { - Text = set.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty, - TextSize = 11, - Font = @"Exo2.0-Bold", + new OsuSpriteText + { + Text = "Download", + TextSize = 13, + Font = @"Exo2.0-Bold", + }, + new OsuSpriteText + { + Text = set.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty, + TextSize = 11, + Font = @"Exo2.0-Bold", + }, }, }, + new SpriteIcon + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Icon = FontAwesome.fa_download, + Size = new Vector2(16), + Margin = new MarginPadding { Right = 5 }, + }, }, - new SpriteIcon - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Icon = FontAwesome.fa_download, - Size = new Vector2(16), - Margin = new MarginPadding { Right = 5 }, - }, + }, + new DownloadProgressBar(set) + { + Depth = -2, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, }, }); diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 44556a6360..a3d144e22f 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -16,8 +16,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API.Requests; using osuTK; using osuTK.Graphics; @@ -31,8 +29,6 @@ namespace osu.Game.Overlays.Direct private Container content; - private ProgressBar progressBar; - private BeatmapManager beatmaps; private BeatmapSetOverlay beatmapSetOverlay; public PreviewTrack Preview => PlayButton.Preview; @@ -65,14 +61,10 @@ namespace osu.Game.Overlays.Direct Colour = Color4.Black.Opacity(0.3f), }; - private OsuColour colours; - [BackgroundDependencyLoader(permitNulls: true)] private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) { - this.beatmaps = beatmaps; this.beatmapSetOverlay = beatmapSetOverlay; - this.colours = colours; AddInternal(content = new Container { @@ -82,33 +74,14 @@ namespace osu.Game.Overlays.Direct Children = new[] { CreateBackground(), - progressBar = new ProgressBar + new DownloadProgressBar(SetInfo) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Height = 0, - Alpha = 0, - BackgroundColour = Color4.Black.Opacity(0.7f), - FillColour = colours.Blue, Depth = -1, }, } }); - - var downloadRequest = beatmaps.GetExistingDownload(SetInfo); - - if (downloadRequest != null) - attachDownload(downloadRequest); - - beatmaps.BeatmapDownloadBegan += attachDownload; - beatmaps.ItemAdded += setAdded; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - beatmaps.BeatmapDownloadBegan -= attachDownload; - beatmaps.ItemAdded -= setAdded; } protected override void Update() @@ -149,37 +122,6 @@ namespace osu.Game.Overlays.Direct protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo); - private void attachDownload(DownloadBeatmapSetRequest request) - { - if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID) - return; - - progressBar.FadeIn(400, Easing.OutQuint); - progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); - - progressBar.Current.Value = 0; - - request.Failure += e => - { - progressBar.Current.Value = 0; - progressBar.FadeOut(500); - }; - - request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress); - - request.Success += data => - { - progressBar.Current.Value = 1; - progressBar.FillColour = colours.Yellow; - }; - } - - private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => Schedule(() => - { - if (s.OnlineBeatmapSetID == SetInfo.OnlineBeatmapSetID) - progressBar.FadeOut(500); - }); - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/Direct/DownloadProgressBar.cs new file mode 100644 index 0000000000..60632980a7 --- /dev/null +++ b/osu.Game/Overlays/Direct/DownloadProgressBar.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Direct +{ + public class DownloadProgressBar : DownloadTrackingComponent + { + private readonly ProgressBar progressBar; + + private OsuColour colours; + + public DownloadProgressBar(BeatmapSetInfo setInfo) + : base(setInfo) + { + AddInternal(progressBar = new ProgressBar + { + Height = 0, + Alpha = 0, + }); + + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colours) + { + this.colours = colours; + + progressBar.FillColour = colours.Blue; + progressBar.BackgroundColour = Color4.Black.Opacity(0.7f); + } + + protected override void DownloadFailed() + { + progressBar.Current.Value = 0; + progressBar.FadeOut(500); + } + + protected override void DownloadComleted() + { + progressBar.Current.Value = 1; + progressBar.FillColour = colours.Yellow; + } + + protected override void DownloadStarted() + { + progressBar.FadeIn(400, Easing.OutQuint); + progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); + } + + protected override void BeatmapImported() + { + progressBar.FadeOut(500); + } + + protected override void ProgressChanged(float progress) + { + progressBar.Current.Value = progress; + } + } +} diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs b/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs new file mode 100644 index 0000000000..8b42832554 --- /dev/null +++ b/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs @@ -0,0 +1,77 @@ +// Copyright (c) 2007-2019 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests; + +namespace osu.Game.Overlays.Direct +{ + public abstract class DownloadTrackingComponent : CompositeDrawable + { + private readonly BeatmapSetInfo setInfo; + private BeatmapManager beatmaps; + + protected DownloadTrackingComponent(BeatmapSetInfo beatmapSetInfo) + { + setInfo = beatmapSetInfo; + } + + #region Disposal + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + beatmaps.BeatmapDownloadBegan -= attachDownload; + beatmaps.ItemAdded -= setAdded; + } + + #endregion + + [BackgroundDependencyLoader(true)] + private void load(BeatmapManager beatmaps) + { + this.beatmaps = beatmaps; + + var downloadRequest = beatmaps.GetExistingDownload(setInfo); + + if (downloadRequest != null) + attachDownload(downloadRequest); + + beatmaps.BeatmapDownloadBegan += attachDownload; + beatmaps.ItemAdded += setAdded; + } + + private void attachDownload(DownloadBeatmapSetRequest request) + { + if (request.BeatmapSet.OnlineBeatmapSetID != setInfo.OnlineBeatmapSetID) + return; + + DownloadStarted(); + + request.Failure += e => { DownloadFailed(); }; + + request.DownloadProgressed += progress => Schedule(() => ProgressChanged(progress)); + request.Success += data => { DownloadComleted(); }; + } + + protected abstract void ProgressChanged(float progress); + + protected abstract void DownloadFailed(); + + protected abstract void DownloadComleted(); + + protected abstract void BeatmapImported(); + + protected abstract void DownloadStarted(); + + private void setAdded(BeatmapSetInfo s, bool existing, bool silent) + { + if (s.OnlineBeatmapSetID != setInfo.OnlineBeatmapSetID) + return; + + Schedule(BeatmapImported); + } + } +} \ No newline at end of file From 8848fa8b1b0f8065972327d8d1bafc9d8637b721 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Jan 2019 12:04:49 +0900 Subject: [PATCH 003/167] Improve UX of download buttons on beatmap set overlay --- .../BeatmapSet/Buttons/DownloadButton.cs | 49 +++++++++----- osu.Game/Overlays/BeatmapSet/Header.cs | 64 +++++++++---------- 2 files changed, 63 insertions(+), 50 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index 9d9f71a7fa..a44e2c7fab 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -29,6 +29,8 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons Width = 120; BeatmapSetDownloader downloader; + FillFlowContainer textSprites; + AddRange(new Drawable[] { new Container @@ -39,27 +41,14 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons Children = new Drawable[] { downloader = new BeatmapSetDownloader(set, noVideo), - new FillFlowContainer + textSprites = new FillFlowContainer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, + AutoSizeDuration = 500, + AutoSizeEasing = Easing.OutQuint, Direction = FillDirection.Vertical, - Children = new[] - { - new OsuSpriteText - { - Text = "Download", - TextSize = 13, - Font = @"Exo2.0-Bold", - }, - new OsuSpriteText - { - Text = set.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty, - TextSize = 11, - Font = @"Exo2.0-Bold", - }, - }, }, new SpriteIcon { @@ -97,14 +86,42 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { switch (state) { + case BeatmapSetDownloader.DownloadStatus.Downloading: + textSprites.Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Downloading...", + TextSize = 13, + Font = @"Exo2.0-Bold", + }, + }; + break; case BeatmapSetDownloader.DownloadStatus.Downloaded: this.FadeOut(200); break; case BeatmapSetDownloader.DownloadStatus.NotDownloaded: + textSprites.Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Download", + TextSize = 13, + Font = @"Exo2.0-Bold", + }, + new OsuSpriteText + { + Text = set.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty, + TextSize = 11, + Font = @"Exo2.0-Bold", + }, + }; this.FadeIn(200); break; } }; + + downloader.DownloadState.TriggerChange(); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index d353522d8d..c702c7cb87 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -28,10 +28,8 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Box tabsBg; private readonly UpdateableBeatmapSetCover cover; private readonly OsuSpriteText title, artist; - private readonly Container noVideoButtons; - private readonly FillFlowContainer videoButtons; private readonly AuthorInfo author; - private readonly Container downloadButtonsContainer; + private readonly FillFlowContainer downloadButtonsContainer; private readonly BeatmapSetOnlineStatusPill onlineStatusPill; public Details Details; @@ -54,8 +52,13 @@ namespace osu.Game.Overlays.BeatmapSet } } + private BeatmapSetDownloader downloader; + private void updateDisplay() { + downloader?.Expire(); + + title.Text = BeatmapSet?.Metadata.Title ?? string.Empty; artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty; onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None; @@ -66,24 +69,30 @@ namespace osu.Game.Overlays.BeatmapSet downloadButtonsContainer.FadeIn(transition_duration); favouriteButton.FadeIn(transition_duration); - if (BeatmapSet.OnlineInfo.HasVideo) + Add(downloader = new BeatmapSetDownloader(BeatmapSet)); + downloader.DownloadState.BindValueChanged(state => { - videoButtons.Children = new[] + switch (state) { - new DownloadButton(BeatmapSet), - new DownloadButton(BeatmapSet, true), - }; - - videoButtons.FadeIn(transition_duration); - noVideoButtons.FadeOut(transition_duration); - } - else - { - noVideoButtons.Child = new DownloadButton(BeatmapSet); - - noVideoButtons.FadeIn(transition_duration); - videoButtons.FadeOut(transition_duration); - } + case BeatmapSetDownloader.DownloadStatus.Downloaded: + // temporary for UX until new design is implemented. + downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet) + { + Width = 50, + RelativeSizeAxes = Axes.Y + }; + break; + case BeatmapSetDownloader.DownloadStatus.Downloading: + // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. + downloadButtonsContainer.Child = new DownloadButton(BeatmapSet); + break; + default: + downloadButtonsContainer.Child = new DownloadButton(BeatmapSet); + if (BeatmapSet.OnlineInfo.HasVideo) + downloadButtonsContainer.Add(new DownloadButton(BeatmapSet, true)); + break; + } + }, true); } else { @@ -196,24 +205,11 @@ namespace osu.Game.Overlays.BeatmapSet Children = new Drawable[] { favouriteButton = new FavouriteButton(), - downloadButtonsContainer = new Container + downloadButtonsContainer = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = buttons_height + buttons_spacing }, - Children = new Drawable[] - { - noVideoButtons = new Container - { - RelativeSizeAxes = Axes.Both, - Alpha = 0f, - }, - videoButtons = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Spacing = new Vector2(buttons_spacing), - Alpha = 0f, - }, - }, + Spacing = new Vector2(buttons_spacing), }, }, }, From 21e79f51b1fdef7a1cc166697f607ff87e7b20f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Jan 2019 14:28:06 +0900 Subject: [PATCH 004/167] Remove necessity of BeatmapSetDownloader --- .../Drawables/BeatmapSetDownloader.cs | 112 --------------- .../API/Requests/DownloadBeatmapSetRequest.cs | 2 +- .../BeatmapSet/Buttons/DownloadButton.cs | 123 +++++++++-------- osu.Game/Overlays/BeatmapSet/Header.cs | 127 ++++++++---------- osu.Game/Overlays/BeatmapSetOverlay.cs | 4 +- osu.Game/Overlays/Direct/DownloadButton.cs | 101 +++++++------- .../Overlays/Direct/DownloadProgressBar.cs | 59 ++++---- .../Direct/DownloadTrackingComponent.cs | 118 +++++++++++----- 8 files changed, 295 insertions(+), 351 deletions(-) delete mode 100644 osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs deleted file mode 100644 index baeeaf81a4..0000000000 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Framework.Graphics; -using osu.Game.Online.API.Requests; - -namespace osu.Game.Beatmaps.Drawables -{ - /// - /// A component to allow downloading of a beatmap set. Automatically handles state syncing between other instances. - /// - public class BeatmapSetDownloader : Component - { - private readonly BeatmapSetInfo set; - private readonly bool noVideo; - - private BeatmapManager beatmaps; - - /// - /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded. - /// - public readonly Bindable DownloadState = new Bindable(); - - public BeatmapSetDownloader(BeatmapSetInfo set, bool noVideo = false) - { - this.set = set; - this.noVideo = noVideo; - } - - [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps) - { - this.beatmaps = beatmaps; - - beatmaps.ItemAdded += setAdded; - beatmaps.ItemRemoved += setRemoved; - beatmaps.BeatmapDownloadBegan += downloadBegan; - beatmaps.BeatmapDownloadFailed += downloadFailed; - - // initial value - if (set.OnlineBeatmapSetID != null && beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any()) - DownloadState.Value = DownloadStatus.Downloaded; - else if (beatmaps.GetExistingDownload(set) != null) - DownloadState.Value = DownloadStatus.Downloading; - else - DownloadState.Value = DownloadStatus.NotDownloaded; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - if (beatmaps != null) - { - beatmaps.ItemAdded -= setAdded; - beatmaps.ItemRemoved -= setRemoved; - beatmaps.BeatmapDownloadBegan -= downloadBegan; - beatmaps.BeatmapDownloadFailed -= downloadFailed; - } - } - - /// - /// Begin downloading the associated beatmap set. - /// - /// True if downloading began. False if an existing download is active or completed. - public void Download() - { - if (DownloadState.Value > DownloadStatus.NotDownloaded) - return; - - if (beatmaps.Download(set, noVideo)) - { - // Only change state if download can happen - DownloadState.Value = DownloadStatus.Downloading; - } - } - - private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => Schedule(() => - { - if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID) - DownloadState.Value = DownloadStatus.Downloaded; - }); - - private void setRemoved(BeatmapSetInfo s) => Schedule(() => - { - if (s.OnlineBeatmapSetID == set.OnlineBeatmapSetID) - DownloadState.Value = DownloadStatus.NotDownloaded; - }); - - private void downloadBegan(DownloadBeatmapSetRequest d) - { - if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID) - DownloadState.Value = DownloadStatus.Downloading; - } - - private void downloadFailed(DownloadBeatmapSetRequest d) - { - if (d.BeatmapSet.OnlineBeatmapSetID == set.OnlineBeatmapSetID) - DownloadState.Value = DownloadStatus.NotDownloaded; - } - - public enum DownloadStatus - { - NotDownloaded, - Downloading, - Downloaded, - } - } -} diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs index c72bf8cbed..b57a077f53 100644 --- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs @@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests { public readonly BeatmapSetInfo BeatmapSet; - public Action DownloadProgressed; + public event Action DownloadProgressed; private readonly bool noVideo; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index a44e2c7fab..deaa648844 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -7,8 +7,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Overlays.Direct; @@ -18,75 +18,100 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet.Buttons { - public class DownloadButton : HeaderButton, IHasTooltip + public class DownloadButton : DownloadTrackingComposite, IHasTooltip { - public string TooltipText => Enabled ? null : "You gotta be an osu!supporter to download for now 'yo"; + private readonly bool noVideo; + public string TooltipText => button.Enabled ? null : "You gotta be an osu!supporter to download for now 'yo"; private readonly IBindable localUser = new Bindable(); - public DownloadButton(BeatmapSetInfo set, bool noVideo = false) - { - Width = 120; + private ShakeContainer shakeContainer; + private HeaderButton button; - BeatmapSetDownloader downloader; + public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) + : base(beatmapSet) + { + this.noVideo = noVideo; + + Width = 120; + RelativeSizeAxes = Axes.Y; + } + + [BackgroundDependencyLoader] + private void load(APIAccess api, BeatmapManager beatmaps) + { FillFlowContainer textSprites; - AddRange(new Drawable[] + AddRangeInternal(new Drawable[] { - new Container + shakeContainer = new ShakeContainer { Depth = -1, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 10 }, + Masking = true, + CornerRadius = 5, Children = new Drawable[] { - downloader = new BeatmapSetDownloader(set, noVideo), - textSprites = new FillFlowContainer + button = new HeaderButton { RelativeSizeAxes = Axes.Both }, + new Container { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - AutoSizeDuration = 500, - AutoSizeEasing = Easing.OutQuint, - Direction = FillDirection.Vertical, + // cannot nest inside here due to the structure of button (putting things in its own content). + // requires framework fix. + Padding = new MarginPadding { Horizontal = 10 }, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + textSprites = new FillFlowContainer + { + Depth = -1, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + AutoSizeDuration = 500, + AutoSizeEasing = Easing.OutQuint, + Direction = FillDirection.Vertical, + }, + new SpriteIcon + { + Depth = -1, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Icon = FontAwesome.fa_download, + Size = new Vector2(16), + Margin = new MarginPadding { Right = 5 }, + }, + } }, - new SpriteIcon + new DownloadProgressBar(BeatmapSet) { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Icon = FontAwesome.fa_download, - Size = new Vector2(16), - Margin = new MarginPadding { Right = 5 }, + Depth = -2, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, }, }, }, - new DownloadProgressBar(set) - { - Depth = -2, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - }, }); - Action = () => + button.Action = () => { - if (downloader.DownloadState.Value == BeatmapSetDownloader.DownloadStatus.Downloading) + if (State.Value == DownloadState.Downloading) { - Content.MoveToX(-5, 50, Easing.OutSine).Then() - .MoveToX(5, 100, Easing.InOutSine).Then() - .MoveToX(-5, 100, Easing.InOutSine).Then() - .MoveToX(0, 50, Easing.InSine); + shakeContainer.Shake(); return; } - downloader.Download(); + beatmaps.Download(BeatmapSet, noVideo); }; - downloader.DownloadState.ValueChanged += state => + localUser.BindTo(api.LocalUser); + localUser.BindValueChanged(userChanged, true); + button.Enabled.BindValueChanged(enabledChanged, true); + + State.BindValueChanged(state => { switch (state) { - case BeatmapSetDownloader.DownloadStatus.Downloading: + case DownloadState.Downloading: textSprites.Children = new Drawable[] { new OsuSpriteText @@ -97,10 +122,10 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, }; break; - case BeatmapSetDownloader.DownloadStatus.Downloaded: + case DownloadState.Downloaded: this.FadeOut(200); break; - case BeatmapSetDownloader.DownloadStatus.NotDownloaded: + case DownloadState.NotDownloaded: textSprites.Children = new Drawable[] { new OsuSpriteText @@ -111,7 +136,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, new OsuSpriteText { - Text = set.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty, + Text = BeatmapSet.Value.OnlineInfo.HasVideo && noVideo ? "without Video" : string.Empty, TextSize = 11, Font = @"Exo2.0-Bold", }, @@ -119,20 +144,10 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons this.FadeIn(200); break; } - }; - - downloader.DownloadState.TriggerChange(); + }, true); } - [BackgroundDependencyLoader] - private void load(APIAccess api) - { - localUser.BindTo(api.LocalUser); - localUser.BindValueChanged(userChanged, true); - Enabled.BindValueChanged(enabledChanged, true); - } - - private void userChanged(User user) => Enabled.Value = user.IsSupporter; + private void userChanged(User user) => button.Enabled.Value = user.IsSupporter; private void enabledChanged(bool enabled) => this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); } diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index c702c7cb87..6a7fc1c0ce 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -13,12 +13,14 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.BeatmapSet.Buttons; +using osu.Game.Overlays.Direct; using osuTK; using osuTK.Graphics; +using DownloadButton = osu.Game.Overlays.BeatmapSet.Buttons.DownloadButton; namespace osu.Game.Overlays.BeatmapSet { - public class Header : Container + public class Header : DownloadTrackingComposite { private const float transition_duration = 200; private const float tabs_height = 50; @@ -35,78 +37,16 @@ namespace osu.Game.Overlays.BeatmapSet public readonly BeatmapPicker Picker; - private BeatmapSetInfo beatmapSet; private readonly FavouriteButton favouriteButton; - public BeatmapSetInfo BeatmapSet - { - get { return beatmapSet; } - set - { - if (value == beatmapSet) return; - beatmapSet = value; - - Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; - - updateDisplay(); - } - } - - private BeatmapSetDownloader downloader; - - private void updateDisplay() - { - downloader?.Expire(); - - - title.Text = BeatmapSet?.Metadata.Title ?? string.Empty; - artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty; - onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None; - cover.BeatmapSet = BeatmapSet; - - if (BeatmapSet != null) - { - downloadButtonsContainer.FadeIn(transition_duration); - favouriteButton.FadeIn(transition_duration); - - Add(downloader = new BeatmapSetDownloader(BeatmapSet)); - downloader.DownloadState.BindValueChanged(state => - { - switch (state) - { - case BeatmapSetDownloader.DownloadStatus.Downloaded: - // temporary for UX until new design is implemented. - downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet) - { - Width = 50, - RelativeSizeAxes = Axes.Y - }; - break; - case BeatmapSetDownloader.DownloadStatus.Downloading: - // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet); - break; - default: - downloadButtonsContainer.Child = new DownloadButton(BeatmapSet); - if (BeatmapSet.OnlineInfo.HasVideo) - downloadButtonsContainer.Add(new DownloadButton(BeatmapSet, true)); - break; - } - }, true); - } - else - { - downloadButtonsContainer.FadeOut(transition_duration); - favouriteButton.FadeOut(transition_duration); - } - } - public Header() { ExternalLinkButton externalLink; + RelativeSizeAxes = Axes.X; Height = 400; Masking = true; + EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.25f), @@ -114,7 +54,8 @@ namespace osu.Game.Overlays.BeatmapSet Radius = 3, Offset = new Vector2(0f, 1f), }; - Children = new Drawable[] + + InternalChildren = new Drawable[] { new Container { @@ -241,14 +182,64 @@ namespace osu.Game.Overlays.BeatmapSet }; Picker.Beatmap.ValueChanged += b => Details.Beatmap = b; - Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}"; + Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}"; } [BackgroundDependencyLoader] private void load(OsuColour colours) { tabsBg.Colour = colours.Gray3; - updateDisplay(); + + BeatmapSet.BindValueChanged(beatmapSet => + { + Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = beatmapSet; + + title.Text = beatmapSet?.Metadata.Title ?? string.Empty; + artist.Text = beatmapSet?.Metadata.Artist ?? string.Empty; + onlineStatusPill.Status = beatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None; + cover.BeatmapSet = beatmapSet; + + if (beatmapSet != null) + { + downloadButtonsContainer.FadeIn(transition_duration); + favouriteButton.FadeIn(transition_duration); + } + else + { + downloadButtonsContainer.FadeOut(transition_duration); + favouriteButton.FadeOut(transition_duration); + } + + updateDownloadButtons(); + }); + + State.BindValueChanged(_ => updateDownloadButtons(), true); + } + + private void updateDownloadButtons() + { + if (BeatmapSet.Value == null) return; + switch (State.Value) + { + case DownloadState.LocallyAvailable: + // temporary for UX until new design is implemented. + downloadButtonsContainer.Child = new osu.Game.Overlays.Direct.DownloadButton(BeatmapSet) + { + Width = 50, + RelativeSizeAxes = Axes.Y + }; + break; + case DownloadState.Downloading: + case DownloadState.Downloaded: + // temporary to avoid showing two buttons for maps with novideo. will be fixed in new beatmap overlay design. + downloadButtonsContainer.Child = new DownloadButton(BeatmapSet); + break; + default: + downloadButtonsContainer.Child = new DownloadButton(BeatmapSet); + if (BeatmapSet.Value.OnlineInfo.HasVideo) + downloadButtonsContainer.Add(new DownloadButton(BeatmapSet, true)); + break; + } } } } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 6294851415..84d7861aa0 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays if (value == beatmapSet) return; - header.BeatmapSet = info.BeatmapSet = beatmapSet = value; + header.BeatmapSet.Value = info.BeatmapSet = beatmapSet = value; } } @@ -140,7 +140,7 @@ namespace osu.Game.Overlays req.Success += res => { BeatmapSet = res.ToBeatmapSet(rulesets); - header.Picker.Beatmap.Value = header.BeatmapSet.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); + header.Picker.Beatmap.Value = header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); }; api.Queue(req); Show(); diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index e326f5e592..32cc8b351a 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -5,103 +5,114 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osuTK; namespace osu.Game.Overlays.Direct { - public class DownloadButton : OsuAnimatedButton + public class DownloadButton : DownloadTrackingComposite { - private readonly BeatmapSetInfo beatmapSet; + private readonly bool noVideo; private readonly SpriteIcon icon; private readonly SpriteIcon checkmark; - private readonly BeatmapSetDownloader downloader; private readonly Box background; private OsuColour colours; - public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) - { - this.beatmapSet = beatmapSet; + private readonly ShakeContainer shakeContainer; - AddRange(new Drawable[] + private readonly OsuAnimatedButton button; + + public DownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) + : base(beatmapSet) + { + this.noVideo = noVideo; + + InternalChild = shakeContainer = new ShakeContainer { - downloader = new BeatmapSetDownloader(beatmapSet, noVideo), - background = new Box + RelativeSizeAxes = Axes.Both, + Child = button = new OsuAnimatedButton { RelativeSizeAxes = Axes.Both, - Depth = float.MaxValue - }, - icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(13), - Icon = FontAwesome.fa_download, - }, - checkmark = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - X = 8, - Size = Vector2.Zero, - Icon = FontAwesome.fa_check, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue + }, + icon = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(13), + Icon = FontAwesome.fa_download, + }, + checkmark = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + X = 8, + Size = Vector2.Zero, + Icon = FontAwesome.fa_check, + } + } } - }); + }; } protected override void LoadComplete() { base.LoadComplete(); - downloader.DownloadState.BindValueChanged(updateState, true); + + State.BindValueChanged(updateState, true); FinishTransforms(true); } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OsuGame game) + private void load(OsuColour colours, OsuGame game, BeatmapManager beatmaps) { this.colours = colours; - Action = () => + button.Action = () => { - switch (downloader.DownloadState.Value) + switch (State.Value) { - case BeatmapSetDownloader.DownloadStatus.Downloading: - // todo: replace with ShakeContainer after https://github.com/ppy/osu/pull/2909 is merged. - Content.MoveToX(-5, 50, Easing.OutSine).Then() - .MoveToX(5, 100, Easing.InOutSine).Then() - .MoveToX(-5, 100, Easing.InOutSine).Then() - .MoveToX(0, 50, Easing.InSine); + case DownloadState.Downloading: + case DownloadState.Downloaded: + shakeContainer.Shake(); break; - case BeatmapSetDownloader.DownloadStatus.Downloaded: - game.PresentBeatmap(beatmapSet); + case DownloadState.LocallyAvailable: + game.PresentBeatmap(BeatmapSet); break; default: - downloader.Download(); + beatmaps.Download(BeatmapSet, noVideo); break; } }; } - private void updateState(BeatmapSetDownloader.DownloadStatus state) + private void updateState(DownloadState state) { switch (state) { - case BeatmapSetDownloader.DownloadStatus.NotDownloaded: + case DownloadState.NotDownloaded: background.FadeColour(colours.Gray4, 500, Easing.InOutExpo); icon.MoveToX(0, 500, Easing.InOutExpo); checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); break; - case BeatmapSetDownloader.DownloadStatus.Downloading: + case DownloadState.Downloading: background.FadeColour(colours.Blue, 500, Easing.InOutExpo); icon.MoveToX(0, 500, Easing.InOutExpo); checkmark.ScaleTo(Vector2.Zero, 500, Easing.InOutExpo); break; - - case BeatmapSetDownloader.DownloadStatus.Downloaded: + case DownloadState.Downloaded: + background.FadeColour(colours.Yellow, 500, Easing.InOutExpo); + break; + case DownloadState.LocallyAvailable: background.FadeColour(colours.Green, 500, Easing.InOutExpo); icon.MoveToX(-8, 500, Easing.InOutExpo); checkmark.ScaleTo(new Vector2(13), 500, Easing.InOutExpo); diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/Direct/DownloadProgressBar.cs index 60632980a7..b416377d0f 100644 --- a/osu.Game/Overlays/Direct/DownloadProgressBar.cs +++ b/osu.Game/Overlays/Direct/DownloadProgressBar.cs @@ -11,14 +11,12 @@ using osuTK.Graphics; namespace osu.Game.Overlays.Direct { - public class DownloadProgressBar : DownloadTrackingComponent + public class DownloadProgressBar : DownloadTrackingComposite { private readonly ProgressBar progressBar; - private OsuColour colours; - - public DownloadProgressBar(BeatmapSetInfo setInfo) - : base(setInfo) + public DownloadProgressBar(BeatmapSetInfo beatmapSet) + : base(beatmapSet) { AddInternal(progressBar = new ProgressBar { @@ -33,38 +31,31 @@ namespace osu.Game.Overlays.Direct [BackgroundDependencyLoader(true)] private void load(OsuColour colours) { - this.colours = colours; - progressBar.FillColour = colours.Blue; progressBar.BackgroundColour = Color4.Black.Opacity(0.7f); - } + progressBar.Current = Progress; - protected override void DownloadFailed() - { - progressBar.Current.Value = 0; - progressBar.FadeOut(500); - } - - protected override void DownloadComleted() - { - progressBar.Current.Value = 1; - progressBar.FillColour = colours.Yellow; - } - - protected override void DownloadStarted() - { - progressBar.FadeIn(400, Easing.OutQuint); - progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); - } - - protected override void BeatmapImported() - { - progressBar.FadeOut(500); - } - - protected override void ProgressChanged(float progress) - { - progressBar.Current.Value = progress; + State.BindValueChanged(state => + { + switch (state) + { + case DownloadState.NotDownloaded: + progressBar.Current.Value = 0; + progressBar.FadeOut(500); + break; + case DownloadState.Downloading: + progressBar.FadeIn(400, Easing.OutQuint); + progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); + break; + case DownloadState.Downloaded: + progressBar.Current.Value = 1; + progressBar.FillColour = colours.Yellow; + break; + case DownloadState.LocallyAvailable: + progressBar.FadeOut(500); + break; + } + }, true); } } } diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs b/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs index 8b42832554..0bfb28978f 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs @@ -1,21 +1,56 @@ -// Copyright (c) 2007-2019 ppy Pty Ltd . +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests; namespace osu.Game.Overlays.Direct { - public abstract class DownloadTrackingComponent : CompositeDrawable + public abstract class DownloadTrackingComposite : CompositeDrawable { - private readonly BeatmapSetInfo setInfo; + public readonly Bindable BeatmapSet = new Bindable(); + private BeatmapManager beatmaps; - protected DownloadTrackingComponent(BeatmapSetInfo beatmapSetInfo) + /// + /// Holds the current download state of the beatmap, whether is has already been downloaded, is in progress, or is not downloaded. + /// + protected readonly Bindable State = new Bindable(); + + protected readonly Bindable Progress = new Bindable(); + + protected DownloadTrackingComposite(BeatmapSetInfo beatmapSet = null) { - setInfo = beatmapSetInfo; + BeatmapSet.Value = beatmapSet; + } + + [BackgroundDependencyLoader(true)] + private void load(BeatmapManager beatmaps) + { + this.beatmaps = beatmaps; + + BeatmapSet.BindValueChanged(set => + { + if (set == null) + attachDownload(null); + else if (beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID).Any()) + State.Value = DownloadState.LocallyAvailable; + else + attachDownload(beatmaps.GetExistingDownload(set)); + }, true); + + beatmaps.BeatmapDownloadBegan += download => + { + if (download.BeatmapSet.OnlineBeatmapSetID == BeatmapSet.Value?.OnlineBeatmapSetID) + attachDownload(download); + }; + + beatmaps.ItemAdded += setAdded; } #region Disposal @@ -29,49 +64,62 @@ namespace osu.Game.Overlays.Direct #endregion - [BackgroundDependencyLoader(true)] - private void load(BeatmapManager beatmaps) - { - this.beatmaps = beatmaps; - - var downloadRequest = beatmaps.GetExistingDownload(setInfo); - - if (downloadRequest != null) - attachDownload(downloadRequest); - - beatmaps.BeatmapDownloadBegan += attachDownload; - beatmaps.ItemAdded += setAdded; - } + private DownloadBeatmapSetRequest attachedRequest; private void attachDownload(DownloadBeatmapSetRequest request) { - if (request.BeatmapSet.OnlineBeatmapSetID != setInfo.OnlineBeatmapSetID) - return; + if (attachedRequest != null) + { + attachedRequest.Failure -= onRequestFailure; + attachedRequest.DownloadProgressed -= onRequestProgress; + attachedRequest.Success -= onRequestSuccess; + } - DownloadStarted(); + attachedRequest = request; - request.Failure += e => { DownloadFailed(); }; - - request.DownloadProgressed += progress => Schedule(() => ProgressChanged(progress)); - request.Success += data => { DownloadComleted(); }; + if (attachedRequest != null) + { + State.Value = DownloadState.Downloading; + attachedRequest.Failure += onRequestFailure; + attachedRequest.DownloadProgressed += onRequestProgress; + attachedRequest.Success += onRequestSuccess; + } + else + { + State.Value = DownloadState.NotDownloaded; + } } - protected abstract void ProgressChanged(float progress); + private void onRequestSuccess(byte[] data) + { + Schedule(() => State.Value = DownloadState.Downloaded); + attachDownload(null); + } - protected abstract void DownloadFailed(); + private void onRequestProgress(float progress) + { + Schedule(() => Progress.Value = progress); + } - protected abstract void DownloadComleted(); - - protected abstract void BeatmapImported(); - - protected abstract void DownloadStarted(); + private void onRequestFailure(Exception e) + { + Schedule(() => State.Value = DownloadState.Downloading); + } private void setAdded(BeatmapSetInfo s, bool existing, bool silent) { - if (s.OnlineBeatmapSetID != setInfo.OnlineBeatmapSetID) + if (s.OnlineBeatmapSetID != BeatmapSet.Value?.OnlineBeatmapSetID) return; - Schedule(BeatmapImported); + Schedule(() => State.Value = DownloadState.LocallyAvailable); } } -} \ No newline at end of file + + public enum DownloadState + { + NotDownloaded, + Downloading, + Downloaded, + LocallyAvailable + } +} From f489718e250a6c27881eb9a3f68bf2bad9b34262 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 18 Jan 2019 14:33:40 +0900 Subject: [PATCH 005/167] Fix non-matching filename --- osu.Game/Overlays/Direct/DownloadState.cs | 13 +++++++++++++ ...ingComponent.cs => DownloadTrackingComposite.cs} | 8 -------- 2 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 osu.Game/Overlays/Direct/DownloadState.cs rename osu.Game/Overlays/Direct/{DownloadTrackingComponent.cs => DownloadTrackingComposite.cs} (96%) diff --git a/osu.Game/Overlays/Direct/DownloadState.cs b/osu.Game/Overlays/Direct/DownloadState.cs new file mode 100644 index 0000000000..6fd48b86c3 --- /dev/null +++ b/osu.Game/Overlays/Direct/DownloadState.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Overlays.Direct +{ + public enum DownloadState + { + NotDownloaded, + Downloading, + Downloaded, + LocallyAvailable + } +} diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs similarity index 96% rename from osu.Game/Overlays/Direct/DownloadTrackingComponent.cs rename to osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index 0bfb28978f..0d5055f5b7 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComponent.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -114,12 +114,4 @@ namespace osu.Game.Overlays.Direct Schedule(() => State.Value = DownloadState.LocallyAvailable); } } - - public enum DownloadState - { - NotDownloaded, - Downloading, - Downloaded, - LocallyAvailable - } } From cebeb0a2190946f9100b857f62facb7b46e58276 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Jan 2019 11:40:57 +0900 Subject: [PATCH 006/167] Fix replay playback speed not being displayed initially --- .../Play/PlayerSettings/PlaybackSettings.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 13e403e899..cba6a88c4e 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -19,10 +19,10 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly PlayerSliderBar sliderbar; + private readonly OsuSpriteText multiplierText; + public PlaybackSettings() { - OsuSpriteText multiplierText; - Children = new Drawable[] { new Container @@ -57,9 +57,6 @@ namespace osu.Game.Screens.Play.PlayerSettings }, } }; - - sliderbar.Bindable.ValueChanged += rateMultiplier => multiplierText.Text = $"{sliderbar.Bar.TooltipText}x"; - sliderbar.Bindable.TriggerChange(); } protected override void LoadComplete() @@ -70,7 +67,12 @@ namespace osu.Game.Screens.Play.PlayerSettings return; var clockRate = AdjustableClock.Rate; - sliderbar.Bindable.ValueChanged += rateMultiplier => AdjustableClock.Rate = clockRate * rateMultiplier; + + sliderbar.Bindable.BindValueChanged(multiplier => + { + multiplierText.Text = $"{multiplier:0.0}x"; + AdjustableClock.Rate = clockRate * multiplier; + }, true); } } } From c58fe4f4ffed4f44d31ca11081b8204087bc921e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Jan 2019 13:18:01 +0900 Subject: [PATCH 007/167] Fix nullref --- osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index cba6a88c4e..8ad0e14137 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -68,11 +68,10 @@ namespace osu.Game.Screens.Play.PlayerSettings var clockRate = AdjustableClock.Rate; - sliderbar.Bindable.BindValueChanged(multiplier => - { - multiplierText.Text = $"{multiplier:0.0}x"; - AdjustableClock.Rate = clockRate * multiplier; - }, true); + // can't trigger this line instantly as the underlying clock may not be ready to accept adjustments yet. + sliderbar.Bindable.ValueChanged += multiplier => AdjustableClock.Rate = clockRate * multiplier; + + sliderbar.Bindable.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier:0.0}x", true); } } } From a208874e740510d0532676924c40bd7d06c09d8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Jan 2019 11:59:10 +0900 Subject: [PATCH 008/167] Fix incorrect handling of download failures --- osu.Game/Overlays/Direct/DownloadTrackingComposite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index 0d5055f5b7..dcd1c8cb03 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -103,7 +103,7 @@ namespace osu.Game.Overlays.Direct private void onRequestFailure(Exception e) { - Schedule(() => State.Value = DownloadState.Downloading); + Schedule(() => State.Value = DownloadState.NotDownloaded); } private void setAdded(BeatmapSetInfo s, bool existing, bool silent) From 902be0d059ca8fb4033e674bacafebe5dea2b7fb Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Thu, 31 Jan 2019 17:03:43 +0800 Subject: [PATCH 009/167] add grow mod --- osu-resources | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 53 ++++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs diff --git a/osu-resources b/osu-resources index 677897728f..9880089b4e 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 677897728f4332fa1200e0280ca02c4b987c6c47 +Subproject commit 9880089b4e8fcd78d68f30c8a40d43bf8dccca86 diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs new file mode 100644 index 0000000000..5ab1ea698b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -0,0 +1,53 @@ +// 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 osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + internal class OsuModGrow : Mod, IApplicableToDrawableHitObjects + { + public override string Name => "Grow"; + public override string Acronym => "GR"; + public override FontAwesome Icon => FontAwesome.fa_arrows_v; + public override ModType Type => ModType.Fun; + public override string Description => "Hit them at the right size!"; + public override double ScoreMultiplier => 1; + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables) + { + if (drawable is DrawableSpinner spinner) + return; + + drawable.ApplyCustomUpdateState += applyCustomState; + } + } + + protected virtual void applyCustomState(DrawableHitObject drawable, ArmedState state) + { + var hitObject = (OsuHitObject) drawable.HitObject; + + double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1; + double scaleDuration = hitObject.TimePreempt + 1; + + var originalScale = drawable.Scale; + drawable.Scale /= 2; + + using (drawable.BeginAbsoluteSequence(appearTime, true)) + drawable.ScaleTo(originalScale, scaleDuration, Easing.OutSine); + + if (drawable is DrawableHitCircle circle) + using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) + circle.ApproachCircle.Hide(); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 12d0a28a8f..200f4af3da 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -124,6 +124,7 @@ namespace osu.Game.Rulesets.Osu return new Mod[] { new OsuModTransform(), new OsuModWiggle(), + new OsuModGrow() }; default: return new Mod[] { }; From a8d30f6aee41f38fcbdd35df05e3a010675e9f5b Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Thu, 31 Jan 2019 17:05:50 +0800 Subject: [PATCH 010/167] remove unused using --- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index 5ab1ea698b..b64cfa3ef2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -8,7 +8,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osuTK; namespace osu.Game.Rulesets.Osu.Mods { From 7f2fdac50e67009e4962e37c48890427bf1342cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Jan 2019 18:47:30 +0900 Subject: [PATCH 011/167] Update signatures --- osu.Game/Overlays/Direct/DownloadTrackingComposite.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index dcd1c8cb03..4b68c3824a 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -90,7 +90,7 @@ namespace osu.Game.Overlays.Direct } } - private void onRequestSuccess(byte[] data) + private void onRequestSuccess(string data) { Schedule(() => State.Value = DownloadState.Downloaded); attachDownload(null); From c54a515b3e95588f2ab6a7c545304104109d0fdf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Jan 2019 18:49:54 +0900 Subject: [PATCH 012/167] Change where BindValueChange is immediately run --- osu.Game/Overlays/BeatmapSet/Header.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index c57b70a48c..8721a1ce5a 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -190,6 +190,8 @@ namespace osu.Game.Overlays.BeatmapSet { tabsBg.Colour = colours.Gray3; + State.BindValueChanged(_ => updateDownloadButtons()); + BeatmapSet.BindValueChanged(beatmapSet => { Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = beatmapSet; @@ -211,9 +213,7 @@ namespace osu.Game.Overlays.BeatmapSet } updateDownloadButtons(); - }); - - State.BindValueChanged(_ => updateDownloadButtons(), true); + }, true); } private void updateDownloadButtons() From 7e2f4af00dd2d47212f1208857bccc936b057e0d Mon Sep 17 00:00:00 2001 From: LeNitrous Date: Thu, 31 Jan 2019 17:58:09 +0800 Subject: [PATCH 013/167] remove whitespaces --- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index b64cfa3ef2..8235a2d6a9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -26,13 +26,12 @@ namespace osu.Game.Rulesets.Osu.Mods { if (drawable is DrawableSpinner spinner) return; - drawable.ApplyCustomUpdateState += applyCustomState; } } protected virtual void applyCustomState(DrawableHitObject drawable, ArmedState state) - { + { var hitObject = (OsuHitObject) drawable.HitObject; double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1; From b69a19f81044d7b3f13188b1e15a4b670e1a914e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Jan 2019 19:08:45 +0900 Subject: [PATCH 014/167] Attach progress immediately --- osu.Game/Online/API/APIDownloadRequest.cs | 4 ++-- .../API/Requests/DownloadBeatmapSetRequest.cs | 4 +++- .../Direct/DownloadTrackingComposite.cs | 18 ++++++++++++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index 97b869bccd..efc832a71e 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.API return request; } - private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total)); + private void request_Progress(long current, long total) => API.Schedule(() => Progressed?.Invoke(current, total)); protected APIDownloadRequest() { @@ -29,7 +29,7 @@ namespace osu.Game.Online.API Success?.Invoke(filename); } - public event APIProgressHandler Progress; + public event APIProgressHandler Progressed; public new event APISuccessHandler Success; } diff --git a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs index cd55e9b300..26e8acc2fc 100644 --- a/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/DownloadBeatmapSetRequest.cs @@ -10,6 +10,8 @@ namespace osu.Game.Online.API.Requests { public readonly BeatmapSetInfo BeatmapSet; + public float Progress; + public event Action DownloadProgressed; private readonly bool noVideo; @@ -19,7 +21,7 @@ namespace osu.Game.Online.API.Requests this.noVideo = noVideo; BeatmapSet = set; - Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total); + Progressed += (current, total) => DownloadProgressed?.Invoke(Progress = (float)current / total); } protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}"; diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index 4b68c3824a..733bcf877e 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -79,10 +79,20 @@ namespace osu.Game.Overlays.Direct if (attachedRequest != null) { - State.Value = DownloadState.Downloading; - attachedRequest.Failure += onRequestFailure; - attachedRequest.DownloadProgressed += onRequestProgress; - attachedRequest.Success += onRequestSuccess; + if (attachedRequest.Progress == 1) + { + State.Value = DownloadState.Downloaded; + Progress.Value = 1; + } + else + { + State.Value = DownloadState.Downloading; + Progress.Value = attachedRequest.Progress; + + attachedRequest.Failure += onRequestFailure; + attachedRequest.DownloadProgressed += onRequestProgress; + attachedRequest.Success += onRequestSuccess; + } } else { From 041e3bf823cf17e6edf6475645fdb1a5f94abf81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Jan 2019 19:08:54 +0900 Subject: [PATCH 015/167] Handle unbinding better --- osu.Game/Overlays/Direct/DownloadTrackingComposite.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index 733bcf877e..fddbefdf4d 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -58,8 +58,13 @@ namespace osu.Game.Overlays.Direct protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + beatmaps.BeatmapDownloadBegan -= attachDownload; beatmaps.ItemAdded -= setAdded; + + State.UnbindAll(); + + attachDownload(null); } #endregion @@ -103,7 +108,6 @@ namespace osu.Game.Overlays.Direct private void onRequestSuccess(string data) { Schedule(() => State.Value = DownloadState.Downloaded); - attachDownload(null); } private void onRequestProgress(float progress) @@ -113,7 +117,7 @@ namespace osu.Game.Overlays.Direct private void onRequestFailure(Exception e) { - Schedule(() => State.Value = DownloadState.NotDownloaded); + Schedule(() => attachDownload(null)); } private void setAdded(BeatmapSetInfo s, bool existing, bool silent) From b89694a96935efc76a2c13f962b9017a04bbf3ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Jan 2019 19:09:04 +0900 Subject: [PATCH 016/167] Fix "importing" state display --- .../Overlays/BeatmapSet/Buttons/DownloadButton.cs | 11 +++++++++++ osu.Game/Overlays/Direct/DownloadProgressBar.cs | 1 + 2 files changed, 12 insertions(+) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index 7c0ffaddcb..de0ced3f08 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -124,6 +124,17 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }; break; case DownloadState.Downloaded: + textSprites.Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Importing...", + TextSize = 13, + Font = @"Exo2.0-Bold", + }, + }; + break; + case DownloadState.LocallyAvailable: this.FadeOut(200); break; case DownloadState.NotDownloaded: diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/Direct/DownloadProgressBar.cs index b416377d0f..cc5a98c575 100644 --- a/osu.Game/Overlays/Direct/DownloadProgressBar.cs +++ b/osu.Game/Overlays/Direct/DownloadProgressBar.cs @@ -48,6 +48,7 @@ namespace osu.Game.Overlays.Direct progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); break; case DownloadState.Downloaded: + progressBar.FadeIn(400, Easing.OutQuint); progressBar.Current.Value = 1; progressBar.FillColour = colours.Yellow; break; From 0b8bd520abee4dd9c67594483d7cc8c3718050d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Jan 2019 19:15:04 +0900 Subject: [PATCH 017/167] Shake in all cases --- osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs index de0ced3f08..4d46d41c0f 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons button.Action = () => { - if (State.Value == DownloadState.Downloading) + if (State.Value != DownloadState.NotDownloaded) { shakeContainer.Shake(); return; From 7c6512118e320ca1df32125fd95066e1a73e0c71 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Jan 2019 19:15:17 +0900 Subject: [PATCH 018/167] Fix importing progress bar not displaying --- osu.Game/Overlays/Direct/DownloadProgressBar.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/Direct/DownloadProgressBar.cs index cc5a98c575..c73ce38c54 100644 --- a/osu.Game/Overlays/Direct/DownloadProgressBar.cs +++ b/osu.Game/Overlays/Direct/DownloadProgressBar.cs @@ -49,6 +49,8 @@ namespace osu.Game.Overlays.Direct break; case DownloadState.Downloaded: progressBar.FadeIn(400, Easing.OutQuint); + progressBar.ResizeHeightTo(4, 400, Easing.OutQuint); + progressBar.Current.Value = 1; progressBar.FillColour = colours.Yellow; break; From c7cf3ef1cda7df0bbffe22160242107016e7e9d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 31 Jan 2019 19:17:42 +0900 Subject: [PATCH 019/167] Update licence headers --- osu.Game/Overlays/Direct/DownloadProgressBar.cs | 4 ++-- osu.Game/Overlays/Direct/DownloadState.cs | 4 ++-- osu.Game/Overlays/Direct/DownloadTrackingComposite.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Direct/DownloadProgressBar.cs b/osu.Game/Overlays/Direct/DownloadProgressBar.cs index c73ce38c54..4ac1aba7f5 100644 --- a/osu.Game/Overlays/Direct/DownloadProgressBar.cs +++ b/osu.Game/Overlays/Direct/DownloadProgressBar.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; diff --git a/osu.Game/Overlays/Direct/DownloadState.cs b/osu.Game/Overlays/Direct/DownloadState.cs index 6fd48b86c3..a301c6a3bd 100644 --- a/osu.Game/Overlays/Direct/DownloadState.cs +++ b/osu.Game/Overlays/Direct/DownloadState.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. namespace osu.Game.Overlays.Direct { diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index fddbefdf4d..f255403e81 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -1,5 +1,5 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/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 Microsoft.EntityFrameworkCore.Internal; From ca5c8d37d1b65902815626f4afe73d244c0a502f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Feb 2019 15:42:15 +0900 Subject: [PATCH 020/167] Use leased bindables --- .../UI/Cursor/GameplayCursor.cs | 2 +- .../Visual/TestCaseEditorComposeTimeline.cs | 2 +- osu.Game.Tests/Visual/TestCaseMatchResults.cs | 2 +- osu.Game/Beatmaps/BindableBeatmap.cs | 7 +- osu.Game/Beatmaps/IBindableBeatmap.cs | 19 ----- .../Containers/BeatSyncedContainer.cs | 2 +- osu.Game/OsuGame.cs | 37 +--------- osu.Game/OsuGameBase.cs | 12 +-- osu.Game/Overlays/Music/PlaylistList.cs | 2 +- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 +- osu.Game/Rulesets/UI/Playfield.cs | 2 +- .../Edit/Components/BottomBarContainer.cs | 2 +- .../Timelines/Summary/Parts/TimelinePart.cs | 2 +- .../Compose/Components/Timeline/Timeline.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 3 +- osu.Game/Screens/Edit/EditorScreen.cs | 2 +- osu.Game/Screens/IOsuScreen.cs | 11 ++- osu.Game/Screens/Menu/Intro.cs | 8 +- osu.Game/Screens/Menu/LogoVisualisation.cs | 2 +- osu.Game/Screens/Menu/MenuSideFlashes.cs | 2 +- .../Multi/Match/Components/ReadyButton.cs | 2 +- .../Screens/Multi/Match/MatchSubScreen.cs | 8 +- osu.Game/Screens/Multi/Multiplayer.cs | 26 ++++--- .../Screens/Multi/MultiplayerSubScreen.cs | 17 ++++- .../Screens/Multi/Ranking/MatchResults.cs | 6 +- osu.Game/Screens/OsuScreen.cs | 74 ++++++++++++++++--- osu.Game/Screens/Play/PlayerLoader.cs | 2 + .../Play/ScreenWithBeatmapBackground.cs | 2 - osu.Game/Screens/Play/SoloResults.cs | 4 +- osu.Game/Screens/Ranking/Results.cs | 2 +- osu.Game/Screens/Select/MatchSongSelect.cs | 16 ++++ osu.Game/Screens/Select/SongSelect.cs | 26 ++++--- .../Drawables/DrawableStoryboardAnimation.cs | 3 +- .../Drawables/DrawableStoryboardSample.cs | 3 +- .../Drawables/DrawableStoryboardSprite.cs | 3 +- osu.Game/Tests/Visual/OsuTestCase.cs | 2 +- osu.sln.DotSettings | 2 + 40 files changed, 186 insertions(+), 141 deletions(-) delete mode 100644 osu.Game/Beatmaps/IBindableBeatmap.cs diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 3fef769174..3167e93923 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } [BackgroundDependencyLoader] - private void load(OsuConfigManager config, IBindableBeatmap beatmap) + private void load(OsuConfigManager config, IBindable beatmap) { InternalChild = expandTarget = new Container { diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs index 7e6edf78fc..a2f40f1903 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(IAdjustableClock adjustableClock, IBindableBeatmap beatmap) + private void load(IAdjustableClock adjustableClock, IBindable beatmap) { this.adjustableClock = adjustableClock; this.beatmap.BindTo(beatmap); diff --git a/osu.Game.Tests/Visual/TestCaseMatchResults.cs b/osu.Game.Tests/Visual/TestCaseMatchResults.cs index 3ce03cf723..cfba5f7016 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchResults.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchResults.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual this.room = room; } - protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap, room) }; + protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value, room) }; } private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo diff --git a/osu.Game/Beatmaps/BindableBeatmap.cs b/osu.Game/Beatmaps/BindableBeatmap.cs index bbd0fbfb06..ca03aac685 100644 --- a/osu.Game/Beatmaps/BindableBeatmap.cs +++ b/osu.Game/Beatmaps/BindableBeatmap.cs @@ -12,9 +12,9 @@ namespace osu.Game.Beatmaps { /// /// A for the beatmap. - /// This should be used sparingly in-favour of . + /// This should be used sparingly in-favour of . /// - public abstract class BindableBeatmap : NonNullableBindable, IBindableBeatmap + public abstract class BindableBeatmap : NonNullableBindable { private AudioManager audioManager; private WorkingBeatmap lastBeatmap; @@ -62,9 +62,6 @@ namespace osu.Game.Beatmaps lastBeatmap = beatmap; } - [NotNull] - IBindableBeatmap IBindableBeatmap.GetBoundCopy() => GetBoundCopy(); - /// /// Retrieve a new instance weakly bound to this . /// If you are further binding to events of the retrieved , ensure a local reference is held. diff --git a/osu.Game/Beatmaps/IBindableBeatmap.cs b/osu.Game/Beatmaps/IBindableBeatmap.cs deleted file mode 100644 index 22b7a5a956..0000000000 --- a/osu.Game/Beatmaps/IBindableBeatmap.cs +++ /dev/null @@ -1,19 +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 osu.Framework.Configuration; - -namespace osu.Game.Beatmaps -{ - /// - /// Read-only interface for the beatmap. - /// - public interface IBindableBeatmap : IBindable - { - /// - /// Retrieve a new instance weakly bound to this . - /// If you are further binding to events of the retrieved , ensure a local reference is held. - /// - IBindableBeatmap GetBoundCopy(); - } -} diff --git a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs index 31eea2cd0c..b929217ae4 100644 --- a/osu.Game/Graphics/Containers/BeatSyncedContainer.cs +++ b/osu.Game/Graphics/Containers/BeatSyncedContainer.cs @@ -74,7 +74,7 @@ namespace osu.Game.Graphics.Containers } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap) + private void load(IBindable beatmap) { Beatmap.BindTo(beatmap); } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b1f18341d2..3b71324644 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -292,7 +292,7 @@ namespace osu.Game return; } - if ((screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange != true) + if ((screenStack.CurrentScreen as IOsuScreen)?.DisallowExternalBeatmapRulesetChanges != false) { notifications.Post(new SimpleNotification { @@ -723,46 +723,13 @@ namespace osu.Game { base.UpdateAfterChildren(); - // we only want to apply these restrictions when we are inside a screen stack. - // the use case for not applying is in visual/unit tests. - bool applyBeatmapRulesetRestrictions = !(screenStack.CurrentScreen as IOsuScreen)?.AllowBeatmapRulesetChange ?? false; - - ruleset.Disabled = applyBeatmapRulesetRestrictions; - Beatmap.Disabled = applyBeatmapRulesetRestrictions; - screenContainer.Padding = new MarginPadding { Top = ToolbarOffset }; overlayContent.Padding = new MarginPadding { Top = ToolbarOffset }; MenuCursorContainer.CanShowCursor = (screenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false; } - /// - /// Sets while ignoring any beatmap. - /// - /// The beatmap to set. - public void ForcefullySetBeatmap(WorkingBeatmap beatmap) - { - var beatmapDisabled = Beatmap.Disabled; - - Beatmap.Disabled = false; - Beatmap.Value = beatmap; - Beatmap.Disabled = beatmapDisabled; - } - - /// - /// Sets while ignoring any ruleset restrictions. - /// - /// The beatmap to set. - public void ForcefullySetRuleset(RulesetInfo ruleset) - { - var rulesetDisabled = this.ruleset.Disabled; - - this.ruleset.Disabled = false; - this.ruleset.Value = ruleset; - this.ruleset.Disabled = rulesetDisabled; - } - - protected virtual void ScreenChanged(IScreen lastScreen, IScreen newScreen) + protected virtual void ScreenChanged(IScreen current, IScreen newScreen) { switch (newScreen) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8154466c4f..b6fe20b3a8 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -69,8 +69,9 @@ namespace osu.Game protected override Container Content => content; - private OsuBindableBeatmap beatmap; - protected BindableBeatmap Beatmap => beatmap; + private Bindable beatmap; + + protected Bindable Beatmap => beatmap; private Bindable fpsDisplayVisible; @@ -155,7 +156,6 @@ namespace osu.Game dependencies.CacheAs(API); var defaultBeatmap = new DummyWorkingBeatmap(this); - beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio); dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); @@ -174,8 +174,10 @@ namespace osu.Game // this adds a global reduction of track volume for the time being. Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); - dependencies.CacheAs(beatmap); - dependencies.CacheAs(beatmap); + beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio); + + dependencies.CacheAs>(beatmap); + dependencies.CacheAs(beatmap); FileStore.Cleanup(); diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index b8cf7abd63..b388bb1632 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Music } [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps, IBindableBeatmap beatmap) + private void load(BeatmapManager beatmaps, IBindable beatmap) { beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false, false)); beatmaps.ItemAdded += addBeatmapSet; diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 7b5a59836f..5b1b7f4da9 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Music private PlaylistList list; [BackgroundDependencyLoader] - private void load(OsuColour colours, BindableBeatmap beatmap, BeatmapManager beatmaps) + private void load(OsuColour colours, Bindable beatmap, BeatmapManager beatmaps) { this.beatmap.BindTo(beatmap); this.beatmaps = beatmaps; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index a70dc63c50..5218edd7d5 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours) + private void load(Bindable beatmap, BeatmapManager beatmaps, OsuColour colours) { this.beatmap.BindTo(beatmap); this.beatmaps = beatmaps; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 122f74b0b1..635e32d489 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Edit } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap, IFrameBasedClock framedClock) + private void load(IBindable beatmap, IFrameBasedClock framedClock) { Beatmap.BindTo(beatmap); diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index cff64bc447..3ab62160cd 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Edit } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap, IAdjustableClock clock) + private void load(IBindable beatmap, IAdjustableClock clock) { this.beatmap.BindTo(beatmap); diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index f0a0de6604..572f41b7e6 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.UI private WorkingBeatmap beatmap; [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap) + private void load(IBindable beatmap) { this.beatmap = beatmap.Value; } diff --git a/osu.Game/Screens/Edit/Components/BottomBarContainer.cs b/osu.Game/Screens/Edit/Components/BottomBarContainer.cs index 4d466d743a..726f6d2025 100644 --- a/osu.Game/Screens/Edit/Components/BottomBarContainer.cs +++ b/osu.Game/Screens/Edit/Components/BottomBarContainer.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Edit.Components } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap, OsuColour colours) + private void load(IBindable beatmap, OsuColour colours) { Beatmap.BindTo(beatmap); background.Colour = colours.Gray1; diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs index f07fd59afd..edf7baf687 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap) + private void load(IBindable beatmap) { Beatmap.BindTo(beatmap); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index c0ad53b4bf..b00d0b0b46 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private WaveformGraph waveform; [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap, IAdjustableClock adjustableClock, OsuColour colours) + private void load(IBindable beatmap, IAdjustableClock adjustableClock, OsuColour colours) { this.adjustableClock = adjustableClock; diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index ac5ff504a2..b147679cca 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -29,7 +29,8 @@ namespace osu.Game.Screens.Edit protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); public override bool HideOverlaysOnEnter => true; - public override bool AllowBeatmapRulesetChange => false; + + public override bool DisallowExternalBeatmapRulesetChanges => true; private Box bottomBackground; private Container screenContainer; diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index ededd90a14..bfe0423c8a 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Edit } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap) + private void load(IBindable beatmap) { Beatmap.BindTo(beatmap); } diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index 532d4963a0..f256760a0a 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -1,8 +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 osu.Framework.Configuration; using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Overlays; +using osu.Game.Rulesets; namespace osu.Game.Screens { @@ -12,9 +15,7 @@ namespace osu.Game.Screens /// Whether the beatmap or ruleset should be allowed to be changed by the user or game. /// Used to mark exclusive areas where this is strongly prohibited, like gameplay. /// - bool AllowBeatmapRulesetChange { get; } - - bool AllowExternalScreenChange { get; } + bool DisallowExternalBeatmapRulesetChanges { get; } /// /// Whether this allows the cursor to be displayed. @@ -35,5 +36,9 @@ namespace osu.Game.Screens /// The amount of parallax to be applied while this screen is displayed. /// float BackgroundParallaxAmount { get; } + + Bindable Beatmap { get; } + + Bindable Ruleset { get; } } } diff --git a/osu.Game/Screens/Menu/Intro.cs b/osu.Game/Screens/Menu/Intro.cs index 3a347342d7..10cfc4a299 100644 --- a/osu.Game/Screens/Menu/Intro.cs +++ b/osu.Game/Screens/Menu/Intro.cs @@ -28,8 +28,6 @@ namespace osu.Game.Screens.Menu /// public bool DidLoadMenu; - private readonly Bindable beatmap = new Bindable(); - private MainMenu mainMenu; private SampleChannel welcome; private SampleChannel seeya; @@ -47,10 +45,8 @@ namespace osu.Game.Screens.Menu private WorkingBeatmap introBeatmap; [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game, BindableBeatmap beatmap) + private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) { - this.beatmap.BindTo(beatmap); - menuVoice = config.GetBindable(OsuSetting.MenuVoice); menuMusic = config.GetBindable(OsuSetting.MenuMusic); @@ -95,7 +91,7 @@ namespace osu.Game.Screens.Menu if (!resuming) { - beatmap.Value = introBeatmap; + Beatmap.Value = introBeatmap; if (menuVoice) welcome.Play(); diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index a87b7aea40..2534c57b1e 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Menu } [BackgroundDependencyLoader] - private void load(ShaderManager shaders, IBindableBeatmap beatmap) + private void load(ShaderManager shaders, IBindable beatmap) { this.beatmap.BindTo(beatmap); shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 40ae8a25c5..7288cc8db5 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Menu } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap, OsuColour colours) + private void load(IBindable beatmap, OsuColour colours) { this.beatmap.BindTo(beatmap); diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index 1bde6270f6..e2733b0cf8 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Multi.Match.Components private readonly Room room; [Resolved] - private IBindableBeatmap gameBeatmap { get; set; } + private IBindable gameBeatmap { get; set; } [Resolved] private BeatmapManager beatmaps { get; set; } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 980f321c92..a1346c9615 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -20,7 +20,7 @@ namespace osu.Game.Screens.Multi.Match { public class MatchSubScreen : MultiplayerSubScreen { - public override bool AllowBeatmapRulesetChange => false; + public override bool DisallowExternalBeatmapRulesetChanges => true; public override string Title => room.RoomID.Value == null ? "New room" : room.Name.Value; public override string ShortTitle => "room"; @@ -167,7 +167,7 @@ namespace osu.Game.Screens.Multi.Match // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); - Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); + Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } private void setRuleset(RulesetInfo ruleset) @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Multi.Match if (ruleset == null) return; - Game?.ForcefullySetRuleset(ruleset); + Ruleset.Value = ruleset; } private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => @@ -190,7 +190,7 @@ namespace osu.Game.Screens.Multi.Match var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == bindings.CurrentBeatmap.Value.OnlineBeatmapID); if (localBeatmap != null) - Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); + Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); }); private void addPlaylistItem(PlaylistItem item) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 1741ac0b7b..d9a2fb7507 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -18,6 +18,7 @@ using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet.Buttons; +using osu.Game.Rulesets; using osu.Game.Screens.Menu; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Match; @@ -28,9 +29,9 @@ namespace osu.Game.Screens.Multi [Cached] public class Multiplayer : CompositeDrawable, IOsuScreen, IOnlineComponent { - public bool AllowBeatmapRulesetChange => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.AllowBeatmapRulesetChange ?? true; - public bool AllowExternalScreenChange => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.AllowExternalScreenChange ?? true; - public bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.AllowExternalScreenChange ?? true; + public bool DisallowExternalBeatmapRulesetChanges => false; + + public bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true; public bool HideOverlaysOnEnter => false; public OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; @@ -51,9 +52,6 @@ namespace osu.Game.Screens.Multi [Cached(Type = typeof(IRoomManager))] private RoomManager roomManager; - [Resolved] - private IBindableBeatmap beatmap { get; set; } - [Resolved] private OsuGameBase game { get; set; } @@ -63,6 +61,14 @@ namespace osu.Game.Screens.Multi [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } + public Bindable Beatmap => screenDependencies.Beatmap; + + public Bindable Ruleset => screenDependencies.Ruleset; + + private OsuScreenDependencies screenDependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); + public Multiplayer() { Anchor = Anchor.Centre; @@ -182,6 +188,8 @@ namespace osu.Game.Screens.Multi { waves.Hide(); + screenDependencies.Dispose(); + this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut(); cancelLooping(); @@ -218,7 +226,7 @@ namespace osu.Game.Screens.Multi private void cancelLooping() { - var track = beatmap.Value.Track; + var track = Beatmap.Value.Track; if (track != null) track.Looping = false; } @@ -231,7 +239,7 @@ namespace osu.Game.Screens.Multi if (screenStack.CurrentScreen is MatchSubScreen) { - var track = beatmap.Value.Track; + var track = Beatmap.Value.Track; if (track != null) { track.Looping = true; @@ -239,7 +247,7 @@ namespace osu.Game.Screens.Multi if (!track.IsRunning) { game.Audio.AddItemToList(track); - track.Seek(beatmap.Value.Metadata.PreviewTime); + track.Seek(Beatmap.Value.Metadata.PreviewTime); track.Start(); } } diff --git a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs index ddea4d5dad..1d6561622a 100644 --- a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs +++ b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; @@ -10,13 +11,14 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; using osu.Game.Overlays; +using osu.Game.Rulesets; namespace osu.Game.Screens.Multi { public abstract class MultiplayerSubScreen : CompositeDrawable, IMultiplayerSubScreen, IKeyBindingHandler { - public virtual bool AllowBeatmapRulesetChange => true; - public bool AllowExternalScreenChange => true; + public virtual bool DisallowExternalBeatmapRulesetChanges => false; + public bool CursorVisible => true; public bool HideOverlaysOnEnter => false; @@ -32,8 +34,13 @@ namespace osu.Game.Screens.Multi public abstract string Title { get; } public virtual string ShortTitle => Title; - [Resolved] - protected IBindableBeatmap Beatmap { get; private set; } + public Bindable Beatmap => screenDependencies.Beatmap; + + public Bindable Ruleset => screenDependencies.Ruleset; + + private OsuScreenDependencies screenDependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); [Resolved(CanBeNull = true)] protected OsuGame Game { get; private set; } @@ -60,6 +67,8 @@ namespace osu.Game.Screens.Multi this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); this.MoveToX(200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); + screenDependencies.Dispose(); + return false; } diff --git a/osu.Game/Screens/Multi/Ranking/MatchResults.cs b/osu.Game/Screens/Multi/Ranking/MatchResults.cs index d14d94928d..4e00b7853b 100644 --- a/osu.Game/Screens/Multi/Ranking/MatchResults.cs +++ b/osu.Game/Screens/Multi/Ranking/MatchResults.cs @@ -22,9 +22,9 @@ namespace osu.Game.Screens.Multi.Ranking protected override IEnumerable CreateResultPages() => new IResultPageInfo[] { - new ScoreOverviewPageInfo(Score, Beatmap), - new LocalLeaderboardPageInfo(Score, Beatmap), - new RoomLeaderboardPageInfo(Score, Beatmap, room), + new ScoreOverviewPageInfo(Score, Beatmap.Value), + new LocalLeaderboardPageInfo(Score, Beatmap.Value), + new RoomLeaderboardPageInfo(Score, Beatmap.Value, room), }; } } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index ba5c7b2f0a..bfed4345f1 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.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; using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -50,15 +51,23 @@ namespace osu.Game.Screens protected new OsuGameBase Game => base.Game as OsuGameBase; - public virtual bool AllowBeatmapRulesetChange => true; - protected readonly Bindable Beatmap = new Bindable(); + /// + /// Disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children). + /// + public virtual bool DisallowExternalBeatmapRulesetChanges => false; + + private SampleChannel sampleExit; public virtual float BackgroundParallaxAmount => 1; - protected readonly Bindable Ruleset = new Bindable(); + public Bindable Beatmap => screenDependencies.Beatmap; - private SampleChannel sampleExit; + public Bindable Ruleset => screenDependencies.Ruleset; + + private OsuScreenDependencies screenDependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); protected BackgroundScreen Background => backgroundStack?.CurrentScreen as BackgroundScreen; @@ -77,11 +86,8 @@ namespace osu.Game.Screens } [BackgroundDependencyLoader(true)] - private void load(BindableBeatmap beatmap, OsuGame osu, AudioManager audio, Bindable ruleset) + private void load(OsuGame osu, AudioManager audio) { - Beatmap.BindTo(beatmap); - Ruleset.BindTo(ruleset); - sampleExit = audio.Sample.Get(@"UI/screen-back"); } @@ -134,7 +140,8 @@ namespace osu.Game.Screens if (localBackground != null && backgroundStack?.CurrentScreen == localBackground) backgroundStack?.Exit(); - Beatmap.UnbindAll(); + screenDependencies.Dispose(); + return false; } @@ -201,4 +208,53 @@ namespace osu.Game.Screens /// protected virtual BackgroundScreen CreateBackground() => null; } + + public class OsuScreenDependencies : DependencyContainer, IDisposable + { + private readonly bool leaseOwner; + + public Bindable Beatmap { get; private set; } + + public Bindable Ruleset { get; private set; } + + public OsuScreenDependencies(bool requireLease, IReadOnlyDependencyContainer parent) + : base(parent) + { + if (requireLease) + { + Beatmap = parent.Get>()?.GetBoundCopy(); + if (Beatmap == null) + { + leaseOwner = true; + Cache(Beatmap = parent.Get>().BeginLease(true)); + } + + Ruleset = parent.Get>()?.GetBoundCopy(); + if (Ruleset == null) + { + leaseOwner = true; + Cache(Ruleset = parent.Get>().BeginLease(true)); + } + } + else + { + Beatmap = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); + Ruleset = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); + } + } + + public void Dispose() + { + if (leaseOwner) + { + ((LeasedBindable)Beatmap).Return(); + ((LeasedBindable)Ruleset).Return(); + } + else + { + Beatmap.UnbindAll(); + Ruleset.UnbindAll(); + } + } + } } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 58e59604dd..c55c05f61c 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -37,6 +37,8 @@ namespace osu.Game.Screens.Play private bool hideOverlays; public override bool HideOverlaysOnEnter => hideOverlays; + public override bool DisallowExternalBeatmapRulesetChanges => true; + private Task loadTask; public PlayerLoader(Func createPlayer) diff --git a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs index 93ec7347c8..7e01a84da2 100644 --- a/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs +++ b/osu.Game/Screens/Play/ScreenWithBeatmapBackground.cs @@ -18,8 +18,6 @@ namespace osu.Game.Screens.Play protected new BackgroundScreenBeatmap Background => base.Background as BackgroundScreenBeatmap; - public override bool AllowBeatmapRulesetChange => false; - protected const float BACKGROUND_FADE_DURATION = 800; protected float BackgroundOpacity => 1 - (float)DimLevel; diff --git a/osu.Game/Screens/Play/SoloResults.cs b/osu.Game/Screens/Play/SoloResults.cs index 54392abe61..2b9aec257c 100644 --- a/osu.Game/Screens/Play/SoloResults.cs +++ b/osu.Game/Screens/Play/SoloResults.cs @@ -17,8 +17,8 @@ namespace osu.Game.Screens.Play protected override IEnumerable CreateResultPages() => new IResultPageInfo[] { - new ScoreOverviewPageInfo(Score, Beatmap), - new LocalLeaderboardPageInfo(Score, Beatmap) + new ScoreOverviewPageInfo(Score, Beatmap.Value), + new LocalLeaderboardPageInfo(Score, Beatmap.Value) }; } } diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 31863cea9b..a70d6bd87f 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Ranking private ResultModeTabControl modeChangeButtons; - public override bool AllowBeatmapRulesetChange => false; + public override bool DisallowExternalBeatmapRulesetChanges => true; protected readonly ScoreInfo Score; diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index 298e936c1c..cfeaa1785e 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -40,5 +40,21 @@ namespace osu.Game.Screens.Select return true; } + + public override bool OnExiting(IScreen next) + { + Beatmap.Disabled = true; + Ruleset.Disabled = true; + + return base.OnExiting(next); + } + + public override void OnEntering(IScreen last) + { + base.OnEntering(last); + + Beatmap.Disabled = false; + Ruleset.Disabled = false; + } } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index d800cea736..5b25cc9db7 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Select protected readonly BeatmapDetailArea BeatmapDetails; - protected new readonly Bindable Ruleset = new Bindable(); + private readonly Bindable decoupledRuleset = new Bindable(); [Cached] [Cached(Type = typeof(IBindable>))] @@ -267,8 +267,8 @@ namespace osu.Game.Screens.Select { dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(this); - dependencies.CacheAs(Ruleset); - dependencies.CacheAs>(Ruleset); + dependencies.CacheAs(decoupledRuleset); + dependencies.CacheAs>(decoupledRuleset); return dependencies; } @@ -333,9 +333,9 @@ namespace osu.Game.Screens.Select if (this.IsCurrentScreen() && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false)) // If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch - if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value) + if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != decoupledRuleset.Value) { - base.Ruleset.Value = beatmap.BeatmapInfo.Ruleset; + Ruleset.Value = beatmap.BeatmapInfo.Ruleset; Carousel.SelectBeatmap(beatmap.BeatmapInfo); } } @@ -376,12 +376,12 @@ namespace osu.Game.Screens.Select bool preview = false; - if (ruleset?.Equals(Ruleset.Value) == false) + if (ruleset?.Equals(decoupledRuleset.Value) == false) { - Logger.Log($"ruleset changed from \"{Ruleset.Value}\" to \"{ruleset}\""); + Logger.Log($"ruleset changed from \"{decoupledRuleset.Value}\" to \"{ruleset}\""); Beatmap.Value.Mods.Value = Enumerable.Empty(); - Ruleset.Value = ruleset; + decoupledRuleset.Value = ruleset; // force a filter before attempting to change the beatmap. // we may still be in the wrong ruleset as there is a debounce delay on ruleset changes. @@ -538,7 +538,7 @@ namespace osu.Game.Screens.Select { base.Dispose(isDisposing); - Ruleset.UnbindAll(); + decoupledRuleset.UnbindAll(); if (beatmaps != null) { @@ -599,9 +599,11 @@ namespace osu.Game.Screens.Select if (rulesetNoDebounce == null) { // manual binding to parent ruleset to allow for delayed load in the incoming direction. - rulesetNoDebounce = Ruleset.Value = base.Ruleset.Value; - base.Ruleset.ValueChanged += updateSelectedRuleset; - Ruleset.ValueChanged += r => base.Ruleset.Value = r; + rulesetNoDebounce = decoupledRuleset.Value = Ruleset.Value; + Ruleset.ValueChanged += updateSelectedRuleset; + + decoupledRuleset.ValueChanged += r => Ruleset.Value = r; + decoupledRuleset.DisabledChanged += r => Ruleset.Disabled = r; Beatmap.BindDisabledChanged(disabled => Carousel.AllowSelection = !disabled, true); Beatmap.BindValueChanged(workingBeatmapChanged); diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index 22175cf24b..94bb4f2525 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -3,6 +3,7 @@ using osuTK; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Textures; @@ -63,7 +64,7 @@ namespace osu.Game.Storyboards.Drawables } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap, TextureStore textureStore) + private void load(IBindable beatmap, TextureStore textureStore) { var basePath = Animation.Path.ToLowerInvariant(); for (var frame = 0; frame < Animation.FrameCount; frame++) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index e94a03fc16..1591823bc1 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -4,6 +4,7 @@ using System.IO; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Beatmaps; @@ -28,7 +29,7 @@ namespace osu.Game.Storyboards.Drawables } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap) + private void load(IBindable beatmap) { // Try first with the full name, then attempt with no path channel = beatmap.Value.Skin.GetSample(sample.Path) ?? beatmap.Value.Skin.GetSample(Path.ChangeExtension(sample.Path, null)); diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs index f904d7cc28..05cde37eb7 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs @@ -3,6 +3,7 @@ using osuTK; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; @@ -62,7 +63,7 @@ namespace osu.Game.Storyboards.Drawables } [BackgroundDependencyLoader] - private void load(IBindableBeatmap beatmap, TextureStore textureStore) + private void load(IBindable beatmap, TextureStore textureStore) { var spritePath = Sprite.Path.ToLowerInvariant(); var path = beatmap.Value.BeatmapSetInfo.Files.Find(f => f.Filename.ToLowerInvariant() == spritePath)?.FileInfo.StoragePath; diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 8a723ec647..3dfcd0febd 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual beatmap.Default = new DummyWorkingBeatmap(Dependencies.Get()); Dependencies.CacheAs(beatmap); - Dependencies.CacheAs(beatmap); + Dependencies.CacheAs>(beatmap); Dependencies.CacheAs(Ruleset); Dependencies.CacheAs>(Ruleset); diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 3f5bd9d34d..5efd16c357 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -68,6 +68,7 @@ HINT HINT HINT + HINT HINT WARNING WARNING @@ -204,6 +205,7 @@ HID HUD ID + IL IP IPC LTRB From 4c866e7940fe35beb88399b122ede5ceb874dbea Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Fri, 1 Feb 2019 22:37:27 +0300 Subject: [PATCH 021/167] Add PlaylistItemHandle.HandlePositionalInput override --- osu.Game/Overlays/Music/PlaylistItem.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 910f7698c0..7c7b78afc7 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -169,6 +169,8 @@ namespace osu.Game.Overlays.Music Alpha = 0f; Margin = new MarginPadding { Left = 5, Top = 2 }; } + + public override bool HandlePositionalInput => IsPresent; } } From e01f342ab0b01799792310fa3679a15ca590d0e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 2 Feb 2019 17:11:25 +0900 Subject: [PATCH 022/167] wip --- osu.Game/Screens/Multi/Multiplayer.cs | 16 +++-- .../Screens/Multi/MultiplayerSubScreen.cs | 16 +++-- osu.Game/Screens/OsuScreen.cs | 67 +++---------------- osu.Game/Screens/OsuScreenDependencies.cs | 41 ++++++++++++ 4 files changed, 71 insertions(+), 69 deletions(-) create mode 100644 osu.Game/Screens/OsuScreenDependencies.cs diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index d9a2fb7507..6bd47ff810 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -61,13 +61,19 @@ namespace osu.Game.Screens.Multi [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } - public Bindable Beatmap => screenDependencies.Beatmap; + public Bindable Beatmap { get; set; } - public Bindable Ruleset => screenDependencies.Ruleset; + public Bindable Ruleset { get; set; } - private OsuScreenDependencies screenDependencies; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var deps = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); + Beatmap = deps.Beatmap; + Ruleset = deps.Ruleset; + + return deps; + } public Multiplayer() { @@ -188,8 +194,6 @@ namespace osu.Game.Screens.Multi { waves.Hide(); - screenDependencies.Dispose(); - this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut(); cancelLooping(); diff --git a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs index 1d6561622a..b0d89c9bba 100644 --- a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs +++ b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs @@ -34,13 +34,19 @@ namespace osu.Game.Screens.Multi public abstract string Title { get; } public virtual string ShortTitle => Title; - public Bindable Beatmap => screenDependencies.Beatmap; + public Bindable Beatmap { get; set; } - public Bindable Ruleset => screenDependencies.Ruleset; + public Bindable Ruleset { get; set; } - private OsuScreenDependencies screenDependencies; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var deps = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); + Beatmap = deps.Beatmap; + Ruleset = deps.Ruleset; + + return deps; + } [Resolved(CanBeNull = true)] protected OsuGame Game { get; private set; } @@ -67,8 +73,6 @@ namespace osu.Game.Screens.Multi this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); this.MoveToX(200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); - screenDependencies.Dispose(); - return false; } diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index bfed4345f1..86f1f3a9d9 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.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 Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -51,7 +50,6 @@ namespace osu.Game.Screens protected new OsuGameBase Game => base.Game as OsuGameBase; - /// /// Disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children). /// @@ -61,13 +59,19 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; - public Bindable Beatmap => screenDependencies.Beatmap; + public Bindable Beatmap { get; set; } - public Bindable Ruleset => screenDependencies.Ruleset; + public Bindable Ruleset { get; set; } - private OsuScreenDependencies screenDependencies; + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + var deps = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); + Beatmap = deps.Beatmap; + Ruleset = deps.Ruleset; + + return deps; + } protected BackgroundScreen Background => backgroundStack?.CurrentScreen as BackgroundScreen; @@ -140,8 +144,6 @@ namespace osu.Game.Screens if (localBackground != null && backgroundStack?.CurrentScreen == localBackground) backgroundStack?.Exit(); - screenDependencies.Dispose(); - return false; } @@ -208,53 +210,4 @@ namespace osu.Game.Screens /// protected virtual BackgroundScreen CreateBackground() => null; } - - public class OsuScreenDependencies : DependencyContainer, IDisposable - { - private readonly bool leaseOwner; - - public Bindable Beatmap { get; private set; } - - public Bindable Ruleset { get; private set; } - - public OsuScreenDependencies(bool requireLease, IReadOnlyDependencyContainer parent) - : base(parent) - { - if (requireLease) - { - Beatmap = parent.Get>()?.GetBoundCopy(); - if (Beatmap == null) - { - leaseOwner = true; - Cache(Beatmap = parent.Get>().BeginLease(true)); - } - - Ruleset = parent.Get>()?.GetBoundCopy(); - if (Ruleset == null) - { - leaseOwner = true; - Cache(Ruleset = parent.Get>().BeginLease(true)); - } - } - else - { - Beatmap = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); - Ruleset = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); - } - } - - public void Dispose() - { - if (leaseOwner) - { - ((LeasedBindable)Beatmap).Return(); - ((LeasedBindable)Ruleset).Return(); - } - else - { - Beatmap.UnbindAll(); - Ruleset.UnbindAll(); - } - } - } } diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs new file mode 100644 index 0000000000..1c355d6320 --- /dev/null +++ b/osu.Game/Screens/OsuScreenDependencies.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; + +namespace osu.Game.Screens +{ + public class OsuScreenDependencies : DependencyContainer + { + public Bindable Beatmap { get; } + + public Bindable Ruleset { get; } + + public OsuScreenDependencies(bool requireLease, IReadOnlyDependencyContainer parent) + : base(parent) + { + if (requireLease) + { + Beatmap = parent.Get>()?.GetBoundCopy(); + if (Beatmap == null) + { + Cache(Beatmap = parent.Get>().BeginLease(true)); + } + + Ruleset = parent.Get>()?.GetBoundCopy(); + if (Ruleset == null) + { + Cache(Ruleset = parent.Get>().BeginLease(true)); + } + } + else + { + Beatmap = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); + Ruleset = (parent.Get>() ?? parent.Get>()).GetBoundCopy(); + } + } + } +} \ No newline at end of file From 257321b500d51307404774e552184a3a41392b85 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sat, 2 Feb 2019 12:26:23 -0800 Subject: [PATCH 023/167] Move regular issue template to .github --- ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md (100%) diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md similarity index 100% rename from ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE.md From 3b529b7bda22119a6102a356d9a0422182b99f5b Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Sun, 3 Feb 2019 14:34:04 +0300 Subject: [PATCH 024/167] Enable the sidebar and move "back" button to the key binding overlay --- osu.Game/Overlays/KeyBindingOverlay.cs | 83 +++++++++++++++++++++++- osu.Game/Overlays/MainSettings.cs | 89 +------------------------- 2 files changed, 85 insertions(+), 87 deletions(-) diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs index 38ac83f041..bd3bb3b9de 100644 --- a/osu.Game/Overlays/KeyBindingOverlay.cs +++ b/osu.Game/Overlays/KeyBindingOverlay.cs @@ -3,10 +3,17 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osu.Game.Overlays.KeyBinding; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; +using osu.Game.Screens.Ranking; +using osuTK; namespace osu.Game.Overlays { @@ -21,11 +28,85 @@ namespace osu.Game.Overlays foreach (var ruleset in rulesets.AvailableRulesets) AddSection(new RulesetBindingsSection(ruleset)); + + AddInternal(new BackButton + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Action = () => Hide() + }); } public KeyBindingOverlay() - : base(false) + : base(true) { } + + private class BackButton : OsuClickableContainer, IKeyBindingHandler + { + private AspectContainer aspect; + + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(Sidebar.DEFAULT_WIDTH); + Children = new Drawable[] + { + aspect = new AspectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = -15, + Size = new Vector2(15), + Shadow = true, + Icon = FontAwesome.fa_chevron_left + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 15, + TextSize = 12, + Font = @"Exo2.0-Bold", + Text = @"back", + }, + } + } + }; + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); + return base.OnMouseDown(e); + } + + protected override bool OnMouseUp(MouseUpEvent e) + { + aspect.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(e); + } + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.Back: + Click(); + return true; + } + + return false; + } + + public bool OnReleased(GlobalAction action) => false; + } } } diff --git a/osu.Game/Overlays/MainSettings.cs b/osu.Game/Overlays/MainSettings.cs index 68fa651dfa..62a2ee6fe3 100644 --- a/osu.Game/Overlays/MainSettings.cs +++ b/osu.Game/Overlays/MainSettings.cs @@ -22,7 +22,6 @@ namespace osu.Game.Overlays public class MainSettings : SettingsOverlay { private readonly KeyBindingOverlay keyBindingOverlay; - private BackButton backButton; protected override IEnumerable CreateSections() => new SettingsSection[] { @@ -53,8 +52,6 @@ namespace osu.Game.Overlays public override bool AcceptsFocus => keyBindingOverlay.State != Visibility.Visible; - private const float hidden_width = 120; - private void keyBindingOverlay_StateChanged(Visibility visibility) { switch (visibility) @@ -64,9 +61,7 @@ namespace osu.Game.Overlays Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint); SectionsContainer.FadeOut(300, Easing.OutQuint); - ContentContainer.MoveToX(hidden_width - WIDTH, 500, Easing.OutQuint); - - backButton.Delay(100).FadeIn(100); + ContentContainer.MoveToX(-WIDTH, 500, Easing.OutQuint); break; case Visibility.Hidden: Background.FadeTo(0.6f, 500, Easing.OutQuint); @@ -74,94 +69,16 @@ namespace osu.Game.Overlays SectionsContainer.FadeIn(500, Easing.OutQuint); ContentContainer.MoveToX(0, 500, Easing.OutQuint); - - backButton.FadeOut(100); break; } } - protected override float ExpandedPosition => keyBindingOverlay.State == Visibility.Visible ? hidden_width - WIDTH : base.ExpandedPosition; + protected override float ExpandedPosition => keyBindingOverlay.State == Visibility.Visible ? -WIDTH : base.ExpandedPosition; [BackgroundDependencyLoader] private void load() { ContentContainer.Add(keyBindingOverlay); - - ContentContainer.Add(backButton = new BackButton - { - Alpha = 0, - Width = hidden_width, - RelativeSizeAxes = Axes.Y, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Action = () => keyBindingOverlay.Hide() - }); - } - - private class BackButton : OsuClickableContainer, IKeyBindingHandler - { - private AspectContainer aspect; - - [BackgroundDependencyLoader] - private void load() - { - Children = new Drawable[] - { - aspect = new AspectContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = -15, - Size = new Vector2(15), - Shadow = true, - Icon = FontAwesome.fa_chevron_left - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = 15, - TextSize = 12, - Font = @"Exo2.0-Bold", - Text = @"back", - }, - } - } - }; - } - - protected override bool OnMouseDown(MouseDownEvent e) - { - aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(e); - } - - protected override bool OnMouseUp(MouseUpEvent e) - { - aspect.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(e); - } - - public bool OnPressed(GlobalAction action) - { - switch (action) - { - case GlobalAction.Back: - Click(); - return true; - } - - return false; - } - - public bool OnReleased(GlobalAction action) => false; - } + } } } From ead28e71023e0875d4eb55d39de6e48779f7125c Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 3 Feb 2019 12:34:38 +0100 Subject: [PATCH 025/167] stop logging keyboard events from password textbox --- osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs | 3 ++- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index 81690f3728..aeb974681d 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -9,12 +9,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Framework.Platform; namespace osu.Game.Graphics.UserInterface { - public class OsuPasswordTextBox : OsuTextBox + public class OsuPasswordTextBox : OsuTextBox, ISuppressKeyEventLogging { protected override Drawable GetDrawableCharacter(char c) => new PasswordMaskChar(CalculatedTextSize); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 596190fcf7..15cc72d77c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + From 4b17ca085781de3403d3fd60d46ba468031f4f07 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Sun, 3 Feb 2019 14:50:03 +0300 Subject: [PATCH 026/167] Fix wrong icons --- osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs | 2 +- osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index f5f2ec453b..b67081846c 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -9,7 +9,7 @@ namespace osu.Game.Overlays.KeyBinding { public class GlobalKeyBindingsSection : SettingsSection { - public override FontAwesome Icon => FontAwesome.fa_osu_hot; + public override FontAwesome Icon => FontAwesome.fa_globe; public override string Header => "Global"; public GlobalKeyBindingsSection(GlobalActionContainer manager) diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs index cfd3aaf6f7..5edeabfd2a 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs @@ -9,7 +9,7 @@ namespace osu.Game.Overlays.KeyBinding { public class RulesetBindingsSection : SettingsSection { - public override FontAwesome Icon => FontAwesome.fa_osu_hot; + public override FontAwesome Icon => (ruleset.CreateInstance().CreateIcon() as SpriteIcon).Icon; public override string Header => ruleset.Name; private readonly RulesetInfo ruleset; From eeecbfcbe0419c7ee71360e93dd91ee44361b013 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Sun, 3 Feb 2019 17:09:04 +0300 Subject: [PATCH 027/167] Fix warnings --- .../Overlays/KeyBinding/RulesetBindingsSection.cs | 2 +- osu.Game/Overlays/KeyBindingOverlay.cs | 2 +- osu.Game/Overlays/MainSettings.cs | 12 ++---------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs index 5edeabfd2a..ea6faf98c1 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs @@ -9,7 +9,7 @@ namespace osu.Game.Overlays.KeyBinding { public class RulesetBindingsSection : SettingsSection { - public override FontAwesome Icon => (ruleset.CreateInstance().CreateIcon() as SpriteIcon).Icon; + public override FontAwesome Icon => (ruleset?.CreateInstance().CreateIcon() as SpriteIcon)?.Icon ?? FontAwesome.fa_osu_hot; public override string Header => ruleset.Name; private readonly RulesetInfo ruleset; diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs index bd3bb3b9de..d0382a4264 100644 --- a/osu.Game/Overlays/KeyBindingOverlay.cs +++ b/osu.Game/Overlays/KeyBindingOverlay.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - Action = () => Hide() + Action = Hide }); } diff --git a/osu.Game/Overlays/MainSettings.cs b/osu.Game/Overlays/MainSettings.cs index 62a2ee6fe3..61dd51d16f 100644 --- a/osu.Game/Overlays/MainSettings.cs +++ b/osu.Game/Overlays/MainSettings.cs @@ -1,21 +1,13 @@ // 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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Input.Bindings; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; -using osu.Game.Screens.Ranking; -using osuTK; using osuTK.Graphics; +using System.Collections.Generic; namespace osu.Game.Overlays { @@ -79,6 +71,6 @@ namespace osu.Game.Overlays private void load() { ContentContainer.Add(keyBindingOverlay); - } + } } } From d6d5bb5ecfaaa88647aba1bac618e017476675e5 Mon Sep 17 00:00:00 2001 From: EVAST9919 Date: Sun, 3 Feb 2019 17:20:35 +0300 Subject: [PATCH 028/167] remove useless nullcheck --- osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs index ea6faf98c1..7b3bef90c0 100644 --- a/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/RulesetBindingsSection.cs @@ -9,7 +9,7 @@ namespace osu.Game.Overlays.KeyBinding { public class RulesetBindingsSection : SettingsSection { - public override FontAwesome Icon => (ruleset?.CreateInstance().CreateIcon() as SpriteIcon)?.Icon ?? FontAwesome.fa_osu_hot; + public override FontAwesome Icon => (ruleset.CreateInstance().CreateIcon() as SpriteIcon)?.Icon ?? FontAwesome.fa_osu_hot; public override string Header => ruleset.Name; private readonly RulesetInfo ruleset; From cd92dddd467b209fbdc0bbaedcd4c3e80c90b943 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Feb 2019 12:31:05 +0900 Subject: [PATCH 029/167] Add per-ruleset mappings --- osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 38 +++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index 069dadf891..6bb454da76 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -59,18 +59,32 @@ namespace osu.Game.Scoring.Legacy var windows = currentRuleset.CreateRulesetContainerWith(workingBeatmap).CreateScoreProcessor().CreateHitWindows(); - if (windows.IsHitResultAllowed(HitResult.Great)) - score.ScoreInfo.Statistics[HitResult.Great] = count300; - if (windows.IsHitResultAllowed(HitResult.Good)) - score.ScoreInfo.Statistics[HitResult.Good] = count100; - if (windows.IsHitResultAllowed(HitResult.Meh)) - score.ScoreInfo.Statistics[HitResult.Meh] = count50; - if (windows.IsHitResultAllowed(HitResult.Perfect)) - score.ScoreInfo.Statistics[HitResult.Perfect] = countGeki; - if (windows.IsHitResultAllowed(HitResult.Ok)) - score.ScoreInfo.Statistics[HitResult.Ok] = countKatu; - if (windows.IsHitResultAllowed(HitResult.Miss)) - score.ScoreInfo.Statistics[HitResult.Miss] = countMiss; + switch (currentRuleset.LegacyID) + { + case 0: + score.ScoreInfo.Statistics[HitResult.Great] = count300; + score.ScoreInfo.Statistics[HitResult.Good] = count100; + score.ScoreInfo.Statistics[HitResult.Meh] = count50; + score.ScoreInfo.Statistics[HitResult.Miss] = countMiss; + break; + case 1: + score.ScoreInfo.Statistics[HitResult.Great] = count300; + score.ScoreInfo.Statistics[HitResult.Good] = count100; + score.ScoreInfo.Statistics[HitResult.Miss] = countMiss; + break; + case 2: + score.ScoreInfo.Statistics[HitResult.Perfect] = count300; + score.ScoreInfo.Statistics[HitResult.Miss] = countMiss; + break; + case 3: + score.ScoreInfo.Statistics[HitResult.Perfect] = countGeki; + score.ScoreInfo.Statistics[HitResult.Great] = count300; + score.ScoreInfo.Statistics[HitResult.Good] = countKatu; + score.ScoreInfo.Statistics[HitResult.Ok] = count100; + score.ScoreInfo.Statistics[HitResult.Meh] = count50; + score.ScoreInfo.Statistics[HitResult.Miss] = countMiss; + break; + } score.ScoreInfo.TotalScore = sr.ReadInt32(); score.ScoreInfo.MaxCombo = sr.ReadUInt16(); From 91d875db0d9224bb836df8476c66310ef1141130 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Feb 2019 12:56:55 +0900 Subject: [PATCH 030/167] Remove unused local --- osu.Game/Scoring/Legacy/LegacyScoreParser.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs index 6bb454da76..f89f8e80bf 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreParser.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreParser.cs @@ -57,8 +57,6 @@ namespace osu.Game.Scoring.Legacy var countKatu = (int)sr.ReadUInt16(); var countMiss = (int)sr.ReadUInt16(); - var windows = currentRuleset.CreateRulesetContainerWith(workingBeatmap).CreateScoreProcessor().CreateHitWindows(); - switch (currentRuleset.LegacyID) { case 0: From cf91b882c860ecd3a33352596bbf90e5209c7499 Mon Sep 17 00:00:00 2001 From: Kyle Chang Date: Fri, 1 Feb 2019 13:28:56 -0500 Subject: [PATCH 031/167] Fix slider tail evaluation in osu difficulty calculator The slider tail circle was already included as a nested hit object and is judged before the end of the slider's actual duration, so using the slider end time leads to an inaccurate travel distance and end position. --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 4e9ac26dc5..1ec12adb3b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -143,7 +143,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing var scoringTimes = slider.NestedHitObjects.Skip(1).Select(t => t.StartTime); foreach (var time in scoringTimes) computeVertex(time); - computeVertex(slider.EndTime); } private Vector2 getEndCursorPosition(OsuHitObject hitObject) From 065b0c9076fa526ec7d7413d2ee33ae47732fee7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Feb 2019 17:04:52 +0900 Subject: [PATCH 032/167] Fix background not being faded correctly --- osu.Game/Screens/Select/SongSelect.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index d800cea736..f3091f7d4e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -31,6 +31,7 @@ using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Screens.Select.Options; using osu.Game.Skinning; +using osuTK.Graphics; namespace osu.Game.Screens.Select { @@ -564,7 +565,7 @@ namespace osu.Game.Screens.Select { backgroundModeBeatmap.Beatmap = beatmap; backgroundModeBeatmap.BlurTo(background_blur, 750, Easing.OutQuint); - backgroundModeBeatmap.FadeTo(1, 250); + backgroundModeBeatmap.FadeColour(Color4.White, 250); } beatmapInfoWedge.Beatmap = beatmap; From 37c1f5a8242fdb2ffe904a77ec5cd1db07326ec1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Feb 2019 15:38:19 +0900 Subject: [PATCH 033/167] Split polling logic from RoomManager, now a container --- osu.Game/Screens/Multi/IRoomManager.cs | 7 -- .../Multi/Lounge/Components/FilterControl.cs | 33 ++++++++-- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 10 +-- osu.Game/Screens/Multi/Multiplayer.cs | 18 +++-- osu.Game/Screens/Multi/RoomManager.cs | 64 +++++------------- .../Screens/Multi/RoomPollingComponent.cs | 66 +++++++++++++++++++ 6 files changed, 125 insertions(+), 73 deletions(-) create mode 100644 osu.Game/Screens/Multi/RoomPollingComponent.cs diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index 944bcf7ce7..980879d79a 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Configuration; using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Screens.Multi { @@ -40,11 +39,5 @@ namespace osu.Game.Screens.Multi /// Parts the currently-joined . /// void PartRoom(); - - /// - /// Queries for s matching a new . - /// - /// The to match. - void Filter(FilterCriteria criteria); } } diff --git a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs index a027125bb5..950d475a30 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/FilterControl.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.ComponentModel; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Game.Graphics; using osu.Game.Overlays.SearchableList; using osuTK.Graphics; @@ -15,17 +17,38 @@ namespace osu.Game.Screens.Multi.Lounge.Components protected override float ContentHorizontalPadding => base.ContentHorizontalPadding + OsuScreen.HORIZONTAL_OVERFLOW_PADDING; + [Resolved(CanBeNull = true)] + private Bindable filter { get; set; } + public FilterControl() { DisplayStyleControl.Hide(); } - public FilterCriteria CreateCriteria() => new FilterCriteria + [BackgroundDependencyLoader] + private void load() { - SearchString = Search.Current.Value ?? string.Empty, - PrimaryFilter = Tabs.Current, - SecondaryFilter = DisplayStyleControl.Dropdown.Current - }; + if (filter == null) + filter = new Bindable(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Search.Current.BindValueChanged(_ => updateFilter()); + Tabs.Current.BindValueChanged(_ => updateFilter(), true); + } + + private void updateFilter() + { + filter.Value = new FilterCriteria + { + SearchString = Search.Current.Value ?? string.Empty, + PrimaryFilter = Tabs.Current, + SecondaryFilter = DisplayStyleControl.Dropdown.Current + }; + } } public enum PrimaryFilter diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index a56d2892a4..5bc36090d5 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -21,7 +21,6 @@ namespace osu.Game.Screens.Multi.Lounge protected readonly FilterControl Filter; private readonly Container content; - private readonly RoomsContainer rooms; private readonly Action pushGameplayScreen; private readonly ProcessingOverlay processingOverlay; @@ -30,6 +29,7 @@ namespace osu.Game.Screens.Multi.Lounge this.pushGameplayScreen = pushGameplayScreen; RoomInspector inspector; + RoomsContainer rooms; InternalChildren = new Drawable[] { @@ -73,8 +73,6 @@ namespace osu.Game.Screens.Multi.Lounge inspector.Room.BindTo(rooms.SelectedRoom); - Filter.Search.Current.ValueChanged += s => filterRooms(); - Filter.Tabs.Current.ValueChanged += t => filterRooms(); Filter.Search.Exit += this.Exit; } @@ -113,12 +111,6 @@ namespace osu.Game.Screens.Multi.Lounge Filter.Search.HoldFocus = false; } - private void filterRooms() - { - rooms.Filter(Filter.CreateCriteria()); - Manager?.Filter(Filter.CreateCriteria()); - } - private void joinRequested(Room room) { processingOverlay.Show(); diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 1741ac0b7b..be9650019d 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -20,6 +20,7 @@ using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Screens.Menu; using osu.Game.Screens.Multi.Lounge; +using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; using osuTK; @@ -47,6 +48,10 @@ namespace osu.Game.Screens.Multi private readonly OsuButton createButton; private readonly LoungeSubScreen loungeSubScreen; private readonly ScreenStack screenStack; + private readonly RoomPollingComponent pollingComponent; + + [Cached] + private readonly Bindable filter = new Bindable(); [Cached(Type = typeof(IRoomManager))] private RoomManager roomManager; @@ -99,7 +104,7 @@ namespace osu.Game.Screens.Multi }, }, }, - new Container + roomManager = new RoomManager { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = Header.HEIGHT }, @@ -123,9 +128,12 @@ namespace osu.Game.Screens.Multi Name = { Value = $"{api.LocalUser}'s awesome room" } }), }, - roomManager = new RoomManager() + pollingComponent = new RoomPollingComponent() }); + pollingComponent.Filter.BindTo(filter); + pollingComponent.RoomsRetrieved += roomManager.UpdateRooms; + screenStack.ScreenPushed += screenPushed; screenStack.ScreenExited += screenExited; } @@ -149,8 +157,8 @@ namespace osu.Game.Screens.Multi private void updatePollingRate(bool idle) { - roomManager.TimeBetweenPolls = !this.IsCurrentScreen() || !(screenStack.CurrentScreen is LoungeSubScreen) ? 0 : (idle ? 120000 : 15000); - Logger.Log($"Polling adjusted to {roomManager.TimeBetweenPolls}"); + pollingComponent.TimeBetweenPolls = !this.IsCurrentScreen() || !(screenStack.CurrentScreen is LoungeSubScreen) ? 0 : (idle ? 120000 : 15000); + Logger.Log($"Polling adjusted to {pollingComponent.TimeBetweenPolls}"); } public void APIStateChanged(APIAccess api, APIState state) @@ -213,7 +221,7 @@ namespace osu.Game.Screens.Multi this.FadeOut(250); cancelLooping(); - roomManager.TimeBetweenPolls = 0; + pollingComponent.TimeBetweenPolls = 0; } private void cancelLooping() diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index bb943b80c6..1457261770 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -2,22 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Game.Beatmaps; -using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; -using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Screens.Multi { - public class RoomManager : PollingComponent, IRoomManager + public class RoomManager : Container, IRoomManager { public event Action RoomsUpdated; @@ -26,8 +25,6 @@ namespace osu.Game.Screens.Multi private Room currentRoom; - private FilterCriteria currentFilter = new FilterCriteria(); - [Resolved] private APIAccess api { get; set; } @@ -102,52 +99,25 @@ namespace osu.Game.Screens.Multi currentRoom = null; } - public void Filter(FilterCriteria criteria) + public void UpdateRooms(List newRooms) { - currentFilter = criteria; - PollImmediately(); - } - - private GetRoomsRequest pollReq; - - protected override Task Poll() - { - if (!api.IsLoggedIn) - return base.Poll(); - - var tcs = new TaskCompletionSource(); - - pollReq?.Cancel(); - pollReq = new GetRoomsRequest(currentFilter.PrimaryFilter); - - pollReq.Success += result => + // Remove past matches + foreach (var r in rooms.ToList()) { - // Remove past matches - foreach (var r in rooms.ToList()) - { - if (result.All(e => e.RoomID.Value != r.RoomID.Value)) - rooms.Remove(r); - } + if (newRooms.All(e => e.RoomID.Value != r.RoomID.Value)) + rooms.Remove(r); + } - for (int i = 0; i < result.Count; i++) - { - var r = result[i]; - r.Position = i; + for (int i = 0; i < newRooms.Count; i++) + { + var r = newRooms[i]; + r.Position = i; - update(r, r); - addRoom(r); - } + update(r, r); + addRoom(r); + } - RoomsUpdated?.Invoke(); - - tcs.SetResult(true); - }; - - pollReq.Failure += _ => tcs.SetResult(false); - - api.Queue(pollReq); - - return tcs.Task; + RoomsUpdated?.Invoke(); } /// diff --git a/osu.Game/Screens/Multi/RoomPollingComponent.cs b/osu.Game/Screens/Multi/RoomPollingComponent.cs new file mode 100644 index 0000000000..53c90d4d61 --- /dev/null +++ b/osu.Game/Screens/Multi/RoomPollingComponent.cs @@ -0,0 +1,66 @@ +// 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.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Online; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Multiplayer; +using osu.Game.Screens.Multi.Lounge.Components; + +namespace osu.Game.Screens.Multi +{ + public class RoomPollingComponent : PollingComponent + { + /// + /// Invoked when s have been retrieved from the API. + /// + public Action> RoomsRetrieved; + + /// + /// The to use when polling for s. + /// + public readonly Bindable Filter = new Bindable(); + + [Resolved] + private APIAccess api { get; set; } + + public RoomPollingComponent() + { + Filter.BindValueChanged(_ => + { + if (IsLoaded) + PollImmediately(); + }); + } + + private GetRoomsRequest pollReq; + + protected override Task Poll() + { + if (!api.IsLoggedIn) + return base.Poll(); + + var tcs = new TaskCompletionSource(); + + pollReq?.Cancel(); + pollReq = new GetRoomsRequest(Filter.Value.PrimaryFilter); + + pollReq.Success += result => + { + RoomsRetrieved?.Invoke(result); + tcs.SetResult(true); + }; + + pollReq.Failure += _ => tcs.SetResult(false); + + api.Queue(pollReq); + + return tcs.Task; + } + } +} From d9537017c8e353c76fbf072eb81c6fe97de993f6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Feb 2019 15:56:18 +0900 Subject: [PATCH 034/167] Use CMC in the Multiplayer screen --- osu.Game/Online/Multiplayer/Room.cs | 2 +- osu.Game/Screens/Multi/Multiplayer.cs | 9 +++++++++ osu.Game/Screens/Multi/RoomManager.cs | 12 ++++++------ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index c20780aecc..46c22ef0cd 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -83,7 +83,7 @@ namespace osu.Game.Online.Multiplayer /// The position of this in the list. This is not read from or written to the API. /// [JsonIgnore] - public int Position = -1; + public Bindable Position { get; private set; } = new Bindable(-1); public void CopyFrom(Room other) { diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index be9650019d..f0297cdfa9 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -149,6 +149,15 @@ namespace osu.Game.Screens.Multi isIdle.BindTo(idleTracker.IsIdle); } + private CachedModelDependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); + dependencies.Model.BindTo(roomManager.CurrentRoom); + return dependencies; + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index 1457261770..d9ddd6f867 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Multi private readonly BindableList rooms = new BindableList(); public IBindableList Rooms => rooms; - private Room currentRoom; + public readonly Bindable CurrentRoom = new Bindable(); [Resolved] private APIAccess api { get; set; } @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Multi currentJoinRoomRequest = new JoinRoomRequest(room, api.LocalUser.Value); currentJoinRoomRequest.Success += () => { - currentRoom = room; + CurrentRoom.Value = room; onSuccess?.Invoke(room); }; @@ -92,11 +92,11 @@ namespace osu.Game.Screens.Multi public void PartRoom() { - if (currentRoom == null) + if (CurrentRoom.Value == null) return; - api.Queue(new PartRoomRequest(currentRoom, api.LocalUser.Value)); - currentRoom = null; + api.Queue(new PartRoomRequest(CurrentRoom.Value, api.LocalUser.Value)); + CurrentRoom.Value = null; } public void UpdateRooms(List newRooms) @@ -111,7 +111,7 @@ namespace osu.Game.Screens.Multi for (int i = 0; i < newRooms.Count; i++) { var r = newRooms[i]; - r.Position = i; + r.Position.Value = i; update(r, r); addRoom(r); From be51ee4ed5b266da78be77df3c44e19d1f3d3122 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Feb 2019 16:14:37 +0900 Subject: [PATCH 035/167] Implement MultiplayerComposite, replaces RoomBindings --- .../Screens/Multi/MultiplayerComposite.cs | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 osu.Game/Screens/Multi/MultiplayerComposite.cs diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs new file mode 100644 index 0000000000..f09750b112 --- /dev/null +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -0,0 +1,89 @@ +// 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.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Online.Multiplayer; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Users; + +namespace osu.Game.Screens.Multi +{ + public class MultiplayerComposite : CompositeDrawable + { + [Resolved] + public Room Room { get; private set; } + + [Resolved(typeof(Room))] + public Bindable RoomID { get; private set; } + + [Resolved(typeof(Room))] + public Bindable Name { get; private set; } + + [Resolved(typeof(Room))] + public Bindable Host { get; private set; } + + [Resolved(typeof(Room))] + public Bindable Status { get; private set; } + + [Resolved(typeof(Room))] + public Bindable Type { get; private set; } + + [Resolved(typeof(Room))] + public BindableList Playlist { get; private set; } + + [Resolved(typeof(Room))] + public Bindable> Participants { get; private set; } + + [Resolved(typeof(Room))] + public Bindable ParticipantCount { get; private set; } + + [Resolved(typeof(Room))] + public Bindable MaxParticipants { get; private set; } + + [Resolved(typeof(Room))] + public Bindable EndDate { get; private set; } + + [Resolved(typeof(Room))] + public Bindable Availability { get; private set; } + + [Resolved(typeof(Room))] + public Bindable Duration { get; private set; } + + private readonly Bindable currentBeatmap = new Bindable(); + public IBindable CurrentBeatmap => currentBeatmap; + + private readonly Bindable> currentMods = new Bindable>(); + public IBindable> CurrentMods => currentMods; + + private readonly Bindable currentRuleset = new Bindable(); + public IBindable CurrentRuleset => currentRuleset; + + protected override void LoadComplete() + { + base.LoadComplete(); + + Playlist.ItemsAdded += _ => updatePlaylist(); + Playlist.ItemsRemoved += _ => updatePlaylist(); + + updatePlaylist(); + } + + private void updatePlaylist() + { + // Todo: We only ever have one playlist item for now. In the future, this will be user-settable + + var playlistItem = Playlist.FirstOrDefault(); + + currentBeatmap.Value = playlistItem?.Beatmap; + currentMods.Value = playlistItem?.RequiredMods ?? Enumerable.Empty(); + currentRuleset.Value = playlistItem?.Ruleset; + } + } +} From 2f8f4fac64cfeeaee0ddd4c977fd084e730452db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Feb 2019 17:50:32 +0900 Subject: [PATCH 036/167] Fix combo colour fallbacks when skin is not providing any --- .../Objects/Drawables/DrawableOsuHitObject.cs | 2 +- osu.Game.Tests/Resources/skin-empty.ini | 20 +++++++++ osu.Game.Tests/Resources/skin.ini | 26 +++++++++++ osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 44 +++++++++++++++++++ osu.Game/Skinning/DefaultSkin.cs | 12 +---- .../Skinning/LocalSkinOverrideContainer.cs | 19 ++++---- osu.Game/Skinning/SkinConfiguration.cs | 8 +++- 7 files changed, 110 insertions(+), 21 deletions(-) create mode 100644 osu.Game.Tests/Resources/skin-empty.ini create mode 100644 osu.Game.Tests/Resources/skin.ini create mode 100644 osu.Game.Tests/Skins/LegacySkinDecoderTest.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 2608706264..10b37af957 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.SkinChanged(skin, allowFallback); if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White); + AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; } protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn); diff --git a/osu.Game.Tests/Resources/skin-empty.ini b/osu.Game.Tests/Resources/skin-empty.ini new file mode 100644 index 0000000000..bdc2c4dfcf --- /dev/null +++ b/osu.Game.Tests/Resources/skin-empty.ini @@ -0,0 +1,20 @@ +[General] +Name: test skin +Author: +Version: +AnimationFramerate: +AllowSliderBallTint: +ComboBurstRandom: +CursorCentre: +CursorExpand: +CursorRotate: +CursorTrailRotate: +CustomComboBurstSounds: +HitCircleOverlayAboveNumber: +LayeredHitSounds: +SliderBallFlip: +SliderBallFrames: +SliderStyle: +SpinnerFadePlayfield: +SpinnerFrequencyModulate: +SpinnerNoBlink: \ No newline at end of file diff --git a/osu.Game.Tests/Resources/skin.ini b/osu.Game.Tests/Resources/skin.ini new file mode 100644 index 0000000000..2ae8fcbfc2 --- /dev/null +++ b/osu.Game.Tests/Resources/skin.ini @@ -0,0 +1,26 @@ +[General] +Name: test skin +Author: +Version: +AnimationFramerate: +AllowSliderBallTint: +ComboBurstRandom: +CursorCentre: +CursorExpand: +CursorRotate: +CursorTrailRotate: +CustomComboBurstSounds: +HitCircleOverlayAboveNumber: +LayeredHitSounds: +SliderBallFlip: +SliderBallFrames: +SliderStyle: +SpinnerFadePlayfield: +SpinnerFrequencyModulate: +SpinnerNoBlink: + +[Colours] +Combo1 : 142,199,255 +Combo2 : 255,128,128 +Combo3 : 128,255,255 +Combo7 : 100,100,100,100 \ No newline at end of file diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs new file mode 100644 index 0000000000..2849b49fe5 --- /dev/null +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -0,0 +1,44 @@ +// 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 System.IO; +using NUnit.Framework; +using osu.Game.Skinning; +using osu.Game.Tests.Resources; +using osuTK.Graphics; + +namespace osu.Game.Tests.Skins +{ + [TestFixture] + public class SkinFallbackTest + { + [TestCase(true)] + [TestCase(false)] + public void TestDecodeSkinColours(bool hasColours) + { + var decoder = new LegacySkinDecoder(); + using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini")) + using (var stream = new StreamReader(resStream)) + { + var comboColors = decoder.Decode(stream).ComboColours; + + List expectedColors; + if (hasColours) + expectedColors = new List + { + new Color4(142, 199, 255, 255), + new Color4(255, 128, 128, 255), + new Color4(128, 255, 255, 255), + new Color4(100, 100, 100, 100), + }; + else + expectedColors = new DefaultSkin().Configuration.ComboColours; + + Assert.AreEqual(expectedColors.Count, comboColors.Count); + for (int i = 0; i < expectedColors.Count; i++) + Assert.AreEqual(expectedColors[i], comboColors[i]); + } + } + } +} diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 61343b4e62..c7556dddd5 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -4,7 +4,6 @@ using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; -using osuTK.Graphics; namespace osu.Game.Skinning { @@ -13,16 +12,7 @@ namespace osu.Game.Skinning public DefaultSkin() : base(SkinInfo.Default) { - Configuration = new SkinConfiguration - { - ComboColours = - { - new Color4(17, 136, 170, 255), - new Color4(102, 136, 0, 255), - new Color4(204, 102, 0, 255), - new Color4(121, 9, 13, 255) - } - }; + Configuration = new SkinConfiguration(); } public override Drawable GetDrawableComponent(string componentName) => null; diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index 8c172ffbcc..d51c11c5f4 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -12,6 +12,9 @@ using osu.Game.Configuration; namespace osu.Game.Skinning { + /// + /// A container which overrides existing skin options with beatmap-local values. + /// public class LocalSkinOverrideContainer : Container, ISkinSource { public event Action SourceChanged; @@ -19,6 +22,14 @@ namespace osu.Game.Skinning private readonly Bindable beatmapSkins = new Bindable(); private readonly Bindable beatmapHitsounds = new Bindable(); + private readonly ISkinSource source; + private ISkinSource fallbackSource; + + public LocalSkinOverrideContainer(ISkinSource source) + { + this.source = source; + } + public Drawable GetDrawableComponent(string componentName) { Drawable sourceDrawable; @@ -53,14 +64,6 @@ namespace osu.Game.Skinning return fallbackSource == null ? default : fallbackSource.GetValue(query); } - private readonly ISkinSource source; - private ISkinSource fallbackSource; - - public LocalSkinOverrideContainer(ISkinSource source) - { - this.source = source; - } - private void onSourceChanged() => SourceChanged?.Invoke(); protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 4d939bd87c..a8091d1f36 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -11,7 +11,13 @@ namespace osu.Game.Skinning { public readonly SkinInfo SkinInfo = new SkinInfo(); - public List ComboColours { get; set; } = new List(); + public List ComboColours { get; set; } = new List + { + new Color4(17, 136, 170, 255), + new Color4(102, 136, 0, 255), + new Color4(204, 102, 0, 255), + new Color4(121, 9, 13, 255) + }; public Dictionary CustomColours { get; set; } = new Dictionary(); From a6b2e9eb0b591ee2b379c26cf21405827d243899 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Feb 2019 18:08:27 +0900 Subject: [PATCH 037/167] Remove unused pieces of ini --- osu.Game.Tests/Resources/skin-empty.ini | 20 +------------------- osu.Game.Tests/Resources/skin.ini | 18 ------------------ 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/osu.Game.Tests/Resources/skin-empty.ini b/osu.Game.Tests/Resources/skin-empty.ini index bdc2c4dfcf..b6c319fe3c 100644 --- a/osu.Game.Tests/Resources/skin-empty.ini +++ b/osu.Game.Tests/Resources/skin-empty.ini @@ -1,20 +1,2 @@ [General] -Name: test skin -Author: -Version: -AnimationFramerate: -AllowSliderBallTint: -ComboBurstRandom: -CursorCentre: -CursorExpand: -CursorRotate: -CursorTrailRotate: -CustomComboBurstSounds: -HitCircleOverlayAboveNumber: -LayeredHitSounds: -SliderBallFlip: -SliderBallFrames: -SliderStyle: -SpinnerFadePlayfield: -SpinnerFrequencyModulate: -SpinnerNoBlink: \ No newline at end of file +Name: test skin \ No newline at end of file diff --git a/osu.Game.Tests/Resources/skin.ini b/osu.Game.Tests/Resources/skin.ini index 2ae8fcbfc2..0e5737b4ea 100644 --- a/osu.Game.Tests/Resources/skin.ini +++ b/osu.Game.Tests/Resources/skin.ini @@ -1,23 +1,5 @@ [General] Name: test skin -Author: -Version: -AnimationFramerate: -AllowSliderBallTint: -ComboBurstRandom: -CursorCentre: -CursorExpand: -CursorRotate: -CursorTrailRotate: -CustomComboBurstSounds: -HitCircleOverlayAboveNumber: -LayeredHitSounds: -SliderBallFlip: -SliderBallFrames: -SliderStyle: -SpinnerFadePlayfield: -SpinnerFrequencyModulate: -SpinnerNoBlink: [Colours] Combo1 : 142,199,255 From 8ae2861ed6b85147d269c5f72aa8721afbf4afda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Feb 2019 18:09:15 +0900 Subject: [PATCH 038/167] Fix class name --- osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index 2849b49fe5..2a97519e21 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -11,7 +11,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Skins { [TestFixture] - public class SkinFallbackTest + public class LegacySkinDecoderTest { [TestCase(true)] [TestCase(false)] From 5b1f111922786783d6121ce426fb16ae39155b7f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Feb 2019 18:14:43 +0900 Subject: [PATCH 039/167] Rollback other fallthrough regressions --- .../Objects/Drawable/DrawableCatchHitObject.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 922d6bf3e9..294fd97d59 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable base.SkinChanged(skin, allowFallback); if (HitObject is IHasComboInformation combo) - AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White); + AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; } private const float preempt = 1000; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 1a1b0530d8..ca9a27976e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -156,9 +156,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.SkinChanged(skin, allowFallback); - Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : Body.AccentColour); - Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : Body.BorderColour); - Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : Ball.AccentColour); + Body.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? Body.AccentColour; + Body.BorderColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Body.BorderColour; + Ball.AccentColour = skin.GetValue(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? Ball.AccentColour; } protected override void CheckForResult(bool userTriggered, double timeOffset) From aac371ba6e9b226cfde6b808ac68c7985aa60cb2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Feb 2019 19:00:01 +0900 Subject: [PATCH 040/167] Use CMC for all of multiplayer --- .../Visual/TestCaseLoungeRoomsContainer.cs | 4 +- osu.Game.Tests/Visual/TestCaseMatchHeader.cs | 12 +- osu.Game.Tests/Visual/TestCaseMatchInfo.cs | 29 +- .../Visual/TestCaseMatchLeaderboard.cs | 6 +- .../Visual/TestCaseMatchParticipants.cs | 24 +- osu.Game.Tests/Visual/TestCaseMatchResults.cs | 41 +- .../Visual/TestCaseMatchSettingsOverlay.cs | 33 +- osu.Game/Online/Multiplayer/Room.cs | 17 +- .../Screens/Multi/Components/BeatmapTitle.cs | 21 +- .../Multi/Components/BeatmapTypeInfo.cs | 29 +- .../Screens/Multi/Components/ModeTypeInfo.cs | 27 +- .../Components/MultiplayerBackgroundSprite.cs | 24 ++ .../Multi/Components/ParticipantCount.cs | 16 +- .../Multi/Components/RoomStatusInfo.cs | 20 +- osu.Game/Screens/Multi/IRoomManager.cs | 5 + .../Multi/Lounge/Components/DrawableRoom.cs | 44 +-- .../Lounge/Components/ParticipantInfo.cs | 31 +- .../Multi/Lounge/Components/RoomInspector.cs | 66 +--- .../Multi/Lounge/Components/RoomsContainer.cs | 6 +- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 27 +- .../Screens/Multi/Match/Components/Header.cs | 68 ++-- .../Screens/Multi/Match/Components/Info.cs | 28 +- .../Match/Components/MatchChatDisplay.cs | 16 +- .../Match/Components/MatchLeaderboard.cs | 16 +- .../Match/Components/MatchSettingsOverlay.cs | 181 +++++---- .../Multi/Match/Components/MatchTabControl.cs | 9 +- .../Multi/Match/Components/Participants.cs | 21 +- .../Multi/Match/Components/ReadyButton.cs | 8 +- .../Screens/Multi/Match/MatchSubScreen.cs | 361 ++++++++++-------- osu.Game/Screens/Multi/Multiplayer.cs | 2 +- .../Screens/Multi/MultiplayerComposite.cs | 32 +- .../Screens/Multi/Play/TimeshiftPlayer.cs | 14 +- .../Screens/Multi/Ranking/MatchResults.cs | 8 +- .../Ranking/Pages/RoomLeaderboardPage.cs | 21 +- .../Ranking/Types/RoomLeaderboardPageInfo.cs | 7 +- osu.Game/Screens/Multi/RoomBindings.cs | 106 ----- osu.Game/Screens/Multi/RoomManager.cs | 4 +- osu.Game/Tests/Visual/MultiplayerTestCase.cs | 36 ++ 38 files changed, 652 insertions(+), 768 deletions(-) create mode 100644 osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs delete mode 100644 osu.Game/Screens/Multi/RoomBindings.cs create mode 100644 osu.Game/Tests/Visual/MultiplayerTestCase.cs diff --git a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs index 1dfc3cdc60..2b362743e8 100644 --- a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0)); AddStep("select first room", () => container.Rooms.First().Action?.Invoke()); - AddAssert("first room selected", () => container.SelectedRoom.Value == roomManager.Rooms.First()); + AddAssert("first room selected", () => roomManager.CurrentRoom.Value == roomManager.Rooms.First()); AddStep("join first room", () => container.Rooms.First().Action?.Invoke()); AddAssert("first room joined", () => roomManager.Rooms.First().Status.Value is JoinedRoomStatus); @@ -76,6 +76,8 @@ namespace osu.Game.Tests.Visual public readonly BindableList Rooms = new BindableList(); IBindableList IRoomManager.Rooms => Rooms; + public Bindable CurrentRoom { get; } = new Bindable(); + public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => Rooms.Add(room); public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) diff --git a/osu.Game.Tests/Visual/TestCaseMatchHeader.cs b/osu.Game.Tests/Visual/TestCaseMatchHeader.cs index c1664c99a3..296e5f24ac 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchHeader.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchHeader.cs @@ -12,7 +12,7 @@ using osu.Game.Screens.Multi.Match.Components; namespace osu.Game.Tests.Visual { - public class TestCaseMatchHeader : OsuTestCase + public class TestCaseMatchHeader : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -21,11 +21,7 @@ namespace osu.Game.Tests.Visual public TestCaseMatchHeader() { - var room = new Room(); - - var header = new Header(room); - - room.Playlist.Add(new PlaylistItem + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { @@ -46,9 +42,9 @@ namespace osu.Game.Tests.Visual } }); - room.Type.Value = new GameTypeTimeshift(); + Room.Type.Value = new GameTypeTimeshift(); - Child = header; + Child = new Header(); } } } diff --git a/osu.Game.Tests/Visual/TestCaseMatchInfo.cs b/osu.Game.Tests/Visual/TestCaseMatchInfo.cs index 57b21f2d79..901c4f1644 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchInfo.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchInfo.cs @@ -14,7 +14,7 @@ using osu.Game.Screens.Multi.Match.Components; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseMatchInfo : OsuTestCase + public class TestCaseMatchInfo : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -27,18 +27,15 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - var room = new Room(); + Add(new Info()); - Info info = new Info(room); - Add(info); - - AddStep(@"set name", () => room.Name.Value = @"Room Name?"); - AddStep(@"set availability", () => room.Availability.Value = RoomAvailability.FriendsOnly); - AddStep(@"set status", () => room.Status.Value = new RoomStatusPlaying()); + AddStep(@"set name", () => Room.Name.Value = @"Room Name?"); + AddStep(@"set availability", () => Room.Availability.Value = RoomAvailability.FriendsOnly); + AddStep(@"set status", () => Room.Status.Value = new RoomStatusPlaying()); AddStep(@"set beatmap", () => { - room.Playlist.Clear(); - room.Playlist.Add(new PlaylistItem + Room.Playlist.Clear(); + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { @@ -54,14 +51,14 @@ namespace osu.Game.Tests.Visual }); }); - AddStep(@"change name", () => room.Name.Value = @"Room Name!"); - AddStep(@"change availability", () => room.Availability.Value = RoomAvailability.InviteOnly); - AddStep(@"change status", () => room.Status.Value = new RoomStatusOpen()); - AddStep(@"null beatmap", () => room.Playlist.Clear()); + AddStep(@"change name", () => Room.Name.Value = @"Room Name!"); + AddStep(@"change availability", () => Room.Availability.Value = RoomAvailability.InviteOnly); + AddStep(@"change status", () => Room.Status.Value = new RoomStatusOpen()); + AddStep(@"null beatmap", () => Room.Playlist.Clear()); AddStep(@"change beatmap", () => { - room.Playlist.Clear(); - room.Playlist.Add(new PlaylistItem + Room.Playlist.Clear(); + Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { diff --git a/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs index 110c7699cb..42a886a5a3 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchLeaderboard.cs @@ -6,24 +6,24 @@ using Newtonsoft.Json; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Users; using osuTK; namespace osu.Game.Tests.Visual { - public class TestCaseMatchLeaderboard : OsuTestCase + public class TestCaseMatchLeaderboard : MultiplayerTestCase { public TestCaseMatchLeaderboard() { + Room.RoomID.Value = 3; + Add(new MatchLeaderboard { Origin = Anchor.Centre, Anchor = Anchor.Centre, Size = new Vector2(550f, 450f), Scope = MatchLeaderboardScope.Overall, - Room = new Room { RoomID = { Value = 3 } } }); } diff --git a/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs b/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs index 174f39a702..716523c23c 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchParticipants.cs @@ -1,9 +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.Collections.Generic; using NUnit.Framework; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Users; @@ -11,22 +9,14 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseMatchParticipants : OsuTestCase + public class TestCaseMatchParticipants : MultiplayerTestCase { - private readonly Bindable maxParticipants = new Bindable(); - private readonly Bindable> users = new Bindable>(); - public TestCaseMatchParticipants() { - Participants participants; + Add(new Participants { RelativeSizeAxes = Axes.Both }); - Add(participants = new Participants { RelativeSizeAxes = Axes.Both }); - - participants.MaxParticipants.BindTo(maxParticipants); - participants.Users.BindTo(users); - - AddStep(@"set max to null", () => maxParticipants.Value = null); - AddStep(@"set users", () => users.Value = new[] + AddStep(@"set max to null", () => Room.MaxParticipants.Value = null); + AddStep(@"set users", () => Room.Participants.Value = new[] { new User { @@ -54,9 +44,9 @@ namespace osu.Game.Tests.Visual }, }); - AddStep(@"set max", () => maxParticipants.Value = 10); - AddStep(@"clear users", () => users.Value = new User[] { }); - AddStep(@"set max to null", () => maxParticipants.Value = null); + AddStep(@"set max", () => Room.MaxParticipants.Value = 10); + AddStep(@"clear users", () => Room.Participants.Value = new User[] { }); + AddStep(@"set max to null", () => Room.MaxParticipants.Value = null); } } } diff --git a/osu.Game.Tests/Visual/TestCaseMatchResults.cs b/osu.Game.Tests/Visual/TestCaseMatchResults.cs index 3ce03cf723..9ef520af4b 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchResults.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchResults.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Online.Multiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Ranking; @@ -19,7 +18,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { - public class TestCaseMatchResults : OsuTestCase + public class TestCaseMatchResults : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -38,6 +37,9 @@ namespace osu.Game.Tests.Visual if (beatmapInfo != null) Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); + Room.RoomID.Value = 1; + Room.Name.Value = "an awesome room"; + Child = new TestMatchResults(new ScoreInfo { User = new User { Id = 10 }, @@ -46,60 +48,41 @@ namespace osu.Game.Tests.Visual private class TestMatchResults : MatchResults { - private readonly Room room; - public TestMatchResults(ScoreInfo score) - : this(score, new Room - { - RoomID = { Value = 1 }, - Name = { Value = "an awesome room" } - }) + : base(score) { } - public TestMatchResults(ScoreInfo score, Room room) - : base(score, room) - { - this.room = room; - } - - protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap, room) }; + protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap) }; } private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo { private readonly ScoreInfo score; private readonly WorkingBeatmap beatmap; - private readonly Room room; - public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap, Room room) - : base(score, beatmap, room) + public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap) + : base(score, beatmap) { this.score = score; this.beatmap = beatmap; - this.room = room; } - public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap, room); + public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap); } private class TestRoomLeaderboardPage : RoomLeaderboardPage { - public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room) - : base(score, beatmap, room) + public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap) + : base(score, beatmap) { } - protected override MatchLeaderboard CreateLeaderboard(Room room) => new TestMatchLeaderboard(room); + protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard(); } private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard { - public TestMatchLeaderboard(Room room) - : base(room) - { - } - protected override APIRequest FetchScores(Action> scoresCallback) { var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray(); diff --git a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs index 16240f0c45..54b2e8a8fc 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs @@ -18,7 +18,7 @@ using osu.Game.Screens.Multi.Match.Components; namespace osu.Game.Tests.Visual { - public class TestCaseMatchSettingsOverlay : OsuTestCase + public class TestCaseMatchSettingsOverlay : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -28,14 +28,14 @@ namespace osu.Game.Tests.Visual [Cached(Type = typeof(IRoomManager))] private TestRoomManager roomManager = new TestRoomManager(); - private Room room; private TestRoomSettings settings; [SetUp] public void Setup() => Schedule(() => { - room = new Room(); - settings = new TestRoomSettings(room) + Room = new Room(); + + settings = new TestRoomSettings { RelativeSizeAxes = Axes.Both, State = Visibility.Visible @@ -49,19 +49,19 @@ namespace osu.Game.Tests.Visual { AddStep("clear name and beatmap", () => { - room.Name.Value = ""; - room.Playlist.Clear(); + Room.Name.Value = ""; + Room.Playlist.Clear(); }); AddAssert("button disabled", () => !settings.ApplyButton.Enabled); - AddStep("set name", () => room.Name.Value = "Room name"); + AddStep("set name", () => Room.Name.Value = "Room name"); AddAssert("button disabled", () => !settings.ApplyButton.Enabled); - AddStep("set beatmap", () => room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo })); + AddStep("set beatmap", () => Room.Playlist.Add(new PlaylistItem { Beatmap = new DummyWorkingBeatmap().BeatmapInfo })); AddAssert("button enabled", () => settings.ApplyButton.Enabled); - AddStep("clear name", () => room.Name.Value = ""); + AddStep("clear name", () => Room.Name.Value = ""); AddAssert("button disabled", () => !settings.ApplyButton.Enabled); } @@ -117,17 +117,12 @@ namespace osu.Game.Tests.Visual private class TestRoomSettings : MatchSettingsOverlay { - public new TriangleButton ApplyButton => base.ApplyButton; + public TriangleButton ApplyButton => Settings.ApplyButton; - public new OsuTextBox NameField => base.NameField; - public new OsuDropdown DurationField => base.DurationField; + public OsuTextBox NameField => Settings.NameField; + public OsuDropdown DurationField => Settings.DurationField; - public new OsuSpriteText ErrorText => base.ErrorText; - - public TestRoomSettings(Room room) - : base(room) - { - } + public OsuSpriteText ErrorText => Settings.ErrorText; } private class TestRoomManager : IRoomManager @@ -140,6 +135,8 @@ namespace osu.Game.Tests.Visual public IBindableList Rooms { get; } = null; + public Bindable CurrentRoom { get; } = new Bindable(); + public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { if (CreateRequested == null) diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 46c22ef0cd..0d5b168dcb 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; +using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Online.Multiplayer.RoomStatuses; @@ -14,42 +15,55 @@ namespace osu.Game.Online.Multiplayer { public class Room { + [Cached] [JsonProperty("id")] public Bindable RoomID { get; private set; } = new Bindable(); + [Cached] [JsonProperty("name")] public Bindable Name { get; private set; } = new Bindable(); + [Cached] [JsonProperty("host")] public Bindable Host { get; private set; } = new Bindable(); + [Cached] [JsonProperty("playlist")] - public BindableList Playlist { get; set; } = new BindableList(); + public BindableList Playlist { get; private set; } = new BindableList(); + [Cached] [JsonProperty("channel_id")] public Bindable ChannelId { get; private set; } = new Bindable(); + [Cached] [JsonIgnore] public Bindable Duration { get; private set; } = new Bindable(TimeSpan.FromMinutes(30)); + [Cached] [JsonIgnore] public Bindable MaxAttempts { get; private set; } = new Bindable(); + [Cached] [JsonIgnore] public Bindable Status { get; private set; } = new Bindable(new RoomStatusOpen()); + [Cached] [JsonIgnore] public Bindable Availability { get; private set; } = new Bindable(); + [Cached] [JsonIgnore] public Bindable Type { get; private set; } = new Bindable(new GameTypeTimeshift()); + [Cached] [JsonIgnore] public Bindable MaxParticipants { get; private set; } = new Bindable(); + [Cached] [JsonIgnore] public Bindable> Participants { get; private set; } = new Bindable>(Enumerable.Empty()); + [Cached] public Bindable ParticipantCount { get; private set; } = new Bindable(); // todo: TEMPORARY @@ -68,6 +82,7 @@ namespace osu.Game.Online.Multiplayer } // Only supports retrieval for now + [Cached] [JsonProperty("ends_at")] public Bindable EndDate { get; private set; } = new Bindable(); diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs index 145375422a..dca0545035 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs @@ -2,11 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Configuration; 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; @@ -14,10 +11,8 @@ using osu.Game.Online.Chat; namespace osu.Game.Screens.Multi.Components { - public class BeatmapTitle : CompositeDrawable + public class BeatmapTitle : MultiplayerComposite { - public readonly IBindable Beatmap = new Bindable(); - private readonly LinkFlowContainer textFlow; public BeatmapTitle() @@ -27,10 +22,10 @@ namespace osu.Game.Screens.Multi.Components InternalChild = textFlow = new LinkFlowContainer { AutoSizeAxes = Axes.Both }; } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load() { - base.LoadComplete(); - Beatmap.BindValueChanged(v => updateText(), true); + CurrentBeatmap.BindValueChanged(v => updateText(), true); } private float textSize = OsuSpriteText.FONT_SIZE; @@ -58,7 +53,7 @@ namespace osu.Game.Screens.Multi.Components textFlow.Clear(); - if (Beatmap.Value == null) + if (CurrentBeatmap.Value == null) textFlow.AddText("No beatmap selected", s => { s.TextSize = TextSize; @@ -70,7 +65,7 @@ namespace osu.Game.Screens.Multi.Components { new OsuSpriteText { - Text = new LocalisedString((Beatmap.Value.Metadata.ArtistUnicode, Beatmap.Value.Metadata.Artist)), + Text = new LocalisedString((CurrentBeatmap.Value.Metadata.ArtistUnicode, CurrentBeatmap.Value.Metadata.Artist)), TextSize = TextSize, }, new OsuSpriteText @@ -80,10 +75,10 @@ namespace osu.Game.Screens.Multi.Components }, new OsuSpriteText { - Text = new LocalisedString((Beatmap.Value.Metadata.TitleUnicode, Beatmap.Value.Metadata.Title)), + Text = new LocalisedString((CurrentBeatmap.Value.Metadata.TitleUnicode, CurrentBeatmap.Value.Metadata.Title)), TextSize = TextSize, } - }, null, LinkAction.OpenBeatmap, Beatmap.Value.OnlineBeatmapID.ToString(), "Open beatmap"); + }, null, LinkAction.OpenBeatmap, CurrentBeatmap.Value.OnlineBeatmapID.ToString(), "Open beatmap"); } } } diff --git a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs index 24c8e3c148..35d1ffad1c 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs @@ -1,31 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.Chat; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; using osuTK; namespace osu.Game.Screens.Multi.Components { - public class BeatmapTypeInfo : CompositeDrawable + public class BeatmapTypeInfo : MultiplayerComposite { - public readonly IBindable Beatmap = new Bindable(); - public readonly IBindable Ruleset = new Bindable(); - public readonly IBindable Type = new Bindable(); - public BeatmapTypeInfo() { AutoSizeAxes = Axes.Both; + } - BeatmapTitle beatmapTitle; - ModeTypeInfo modeTypeInfo; + [BackgroundDependencyLoader] + private void load() + { LinkFlowContainer beatmapAuthor; InternalChild = new FillFlowContainer @@ -36,7 +31,7 @@ namespace osu.Game.Screens.Multi.Components Spacing = new Vector2(5, 0), Children = new Drawable[] { - modeTypeInfo = new ModeTypeInfo(), + new ModeTypeInfo(), new Container { AutoSizeAxes = Axes.X, @@ -44,7 +39,7 @@ namespace osu.Game.Screens.Multi.Components Margin = new MarginPadding { Left = 5 }, Children = new Drawable[] { - beatmapTitle = new BeatmapTitle(), + new BeatmapTitle(), beatmapAuthor = new LinkFlowContainer(s => s.TextSize = 14) { Anchor = Anchor.BottomLeft, @@ -56,13 +51,7 @@ namespace osu.Game.Screens.Multi.Components } }; - modeTypeInfo.Beatmap.BindTo(Beatmap); - modeTypeInfo.Ruleset.BindTo(Ruleset); - modeTypeInfo.Type.BindTo(Type); - - beatmapTitle.Beatmap.BindTo(Beatmap); - - Beatmap.BindValueChanged(v => + CurrentBeatmap.BindValueChanged(v => { beatmapAuthor.Clear(); diff --git a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs index cdb5974f8f..6ae3b7be7c 100644 --- a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs @@ -1,32 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; using osuTK; namespace osu.Game.Screens.Multi.Components { - public class ModeTypeInfo : CompositeDrawable + public class ModeTypeInfo : MultiplayerComposite { private const float height = 30; private const float transition_duration = 100; - private readonly Container rulesetContainer; - - public readonly IBindable Beatmap = new Bindable(); - public readonly IBindable Ruleset = new Bindable(); - public readonly IBindable Type = new Bindable(); + private Container rulesetContainer; public ModeTypeInfo() { AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] + private void load() + { Container gameTypeContainer; InternalChild = new FillFlowContainer @@ -48,17 +45,17 @@ namespace osu.Game.Screens.Multi.Components }, }; - Beatmap.BindValueChanged(updateBeatmap); - Ruleset.BindValueChanged(_ => updateBeatmap(Beatmap.Value)); + CurrentBeatmap.BindValueChanged(_ => updateBeatmap()); + CurrentRuleset.BindValueChanged(_ => updateBeatmap()); Type.BindValueChanged(v => gameTypeContainer.Child = new DrawableGameType(v) { Size = new Vector2(height) }); } - private void updateBeatmap(BeatmapInfo beatmap) + private void updateBeatmap() { - if (beatmap != null) + if (CurrentBeatmap.Value != null) { rulesetContainer.FadeIn(transition_duration); - rulesetContainer.Child = new DifficultyIcon(beatmap, Ruleset.Value) { Size = new Vector2(height) }; + rulesetContainer.Child = new DifficultyIcon(CurrentBeatmap.Value, CurrentRuleset.Value) { Size = new Vector2(height) }; } else rulesetContainer.FadeOut(transition_duration); diff --git a/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs b/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs new file mode 100644 index 0000000000..8eff7b14af --- /dev/null +++ b/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs @@ -0,0 +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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Drawables; + +namespace osu.Game.Screens.Multi.Components +{ + public class MultiplayerBackgroundSprite : MultiplayerComposite + { + [BackgroundDependencyLoader] + private void load() + { + UpdateableBeatmapBackgroundSprite sprite; + + InternalChild = sprite = CreateBackgroundSprite(); + + sprite.Beatmap.BindTo(CurrentBeatmap); + } + + protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }; + } +} diff --git a/osu.Game/Screens/Multi/Components/ParticipantCount.cs b/osu.Game/Screens/Multi/Components/ParticipantCount.cs index 95cd6a7a47..9711767924 100644 --- a/osu.Game/Screens/Multi/Components/ParticipantCount.cs +++ b/osu.Game/Screens/Multi/Components/ParticipantCount.cs @@ -1,31 +1,29 @@ // 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 System.Linq; -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Users; namespace osu.Game.Screens.Multi.Components { - public class ParticipantCountDisplay : CompositeDrawable + public class ParticipantCountDisplay : MultiplayerComposite { private const float text_size = 30; private const float transition_duration = 100; - private readonly OsuSpriteText slash, maxText; - - public readonly IBindable> Participants = new Bindable>(); - public readonly IBindable ParticipantCount = new Bindable(); - public readonly IBindable MaxParticipants = new Bindable(); + private OsuSpriteText slash, maxText; public ParticipantCountDisplay() { AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] + private void load() + { OsuSpriteText count; InternalChild = new FillFlowContainer diff --git a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs b/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs index 743cfe0114..24a2d70b60 100644 --- a/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs +++ b/osu.Game/Screens/Multi/Components/RoomStatusInfo.cs @@ -13,16 +13,16 @@ using osu.Game.Online.Multiplayer.RoomStatuses; namespace osu.Game.Screens.Multi.Components { - public class RoomStatusInfo : CompositeDrawable + public class RoomStatusInfo : MultiplayerComposite { - private readonly RoomBindings bindings = new RoomBindings(); - - public RoomStatusInfo(Room room) + public RoomStatusInfo() { - bindings.Room = room; - AutoSizeAxes = Axes.Both; + } + [BackgroundDependencyLoader] + private void load() + { StatusPart statusPart; EndDatePart endDatePart; @@ -41,10 +41,10 @@ namespace osu.Game.Screens.Multi.Components } }; - statusPart.EndDate.BindTo(bindings.EndDate); - statusPart.Status.BindTo(bindings.Status); - statusPart.Availability.BindTo(bindings.Availability); - endDatePart.EndDate.BindTo(bindings.EndDate); + statusPart.EndDate.BindTo(EndDate); + statusPart.Status.BindTo(Status); + statusPart.Availability.BindTo(Availability); + endDatePart.EndDate.BindTo(EndDate); } private class EndDatePart : DrawableDate diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index 980879d79a..a00e08a80a 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -19,6 +19,11 @@ namespace osu.Game.Screens.Multi /// IBindableList Rooms { get; } + /// + /// The currently-active . + /// + Bindable CurrentRoom { get; } + /// /// Creates a new . /// diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index 896a5eafdc..3918148b07 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -34,12 +33,8 @@ namespace osu.Game.Screens.Multi.Lounge.Components public event Action StateChanged; - private readonly RoomBindings bindings = new RoomBindings(); - private readonly Box selectionBox; - private UpdateableBeatmapBackgroundSprite background; - private BeatmapTitle beatmapTitle; - private ModeTypeInfo modeTypeInfo; + private CachedModelDependencyContainer dependencies; [Resolved] private BeatmapManager beatmaps { get; set; } @@ -80,7 +75,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components public DrawableRoom(Room room) { Room = room; - bindings.Room = room; RelativeSizeAxes = Axes.X; Height = height + SELECTION_BORDER_WIDTH * 2; @@ -99,7 +93,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components private void load(OsuColour colours) { Box sideStrip; - ParticipantInfo participantInfo; OsuSpriteText name; Children = new Drawable[] @@ -138,7 +131,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components Width = cover_width, Masking = true, Margin = new MarginPadding { Left = side_strip_width }, - Child = background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both } + Child = new MultiplayerBackgroundSprite { RelativeSizeAxes = Axes.Both } }, new Container { @@ -159,8 +152,11 @@ namespace osu.Game.Screens.Multi.Lounge.Components Spacing = new Vector2(5f), Children = new Drawable[] { - name = new OsuSpriteText { TextSize = 18 }, - participantInfo = new ParticipantInfo(), + name = new OsuSpriteText + { + TextSize = 18 + }, + new ParticipantInfo(), }, }, new FillFlowContainer @@ -173,11 +169,11 @@ namespace osu.Game.Screens.Multi.Lounge.Components Spacing = new Vector2(0, 5), Children = new Drawable[] { - new RoomStatusInfo(Room), - beatmapTitle = new BeatmapTitle { TextSize = 14 }, + new RoomStatusInfo(), + new BeatmapTitle { TextSize = 14 }, }, }, - modeTypeInfo = new ModeTypeInfo + new ModeTypeInfo { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -189,23 +185,21 @@ namespace osu.Game.Screens.Multi.Lounge.Components }, }; - background.Beatmap.BindTo(bindings.CurrentBeatmap); - modeTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap); - modeTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset); - modeTypeInfo.Type.BindTo(bindings.Type); - beatmapTitle.Beatmap.BindTo(bindings.CurrentBeatmap); - participantInfo.Host.BindTo(bindings.Host); - participantInfo.Participants.BindTo(bindings.Participants); - participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount); - - bindings.Name.BindValueChanged(n => name.Text = n, true); - bindings.Status.BindValueChanged(s => + dependencies.ShadowModel.Name.BindValueChanged(n => name.Text = n, true); + dependencies.ShadowModel.Status.BindValueChanged(s => { foreach (Drawable d in new Drawable[] { selectionBox, sideStrip }) d.FadeColour(s.GetAppropriateColour(colours), transition_duration); }, true); } + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); + dependencies.Model.Value = Room; + return dependencies; + } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs index a053032404..806bc92882 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/ParticipantInfo.cs @@ -1,10 +1,8 @@ // 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 Humanizer; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -16,26 +14,24 @@ using osuTK; namespace osu.Game.Screens.Multi.Lounge.Components { - public class ParticipantInfo : Container + public class ParticipantInfo : MultiplayerComposite { - private readonly FillFlowContainer summaryContainer; - - public readonly IBindable Host = new Bindable(); - public readonly IBindable> Participants = new Bindable>(); - public readonly IBindable ParticipantCount = new Bindable(); - public ParticipantInfo() { - OsuSpriteText summary; RelativeSizeAxes = Axes.X; Height = 15f; + } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + OsuSpriteText summary; OsuSpriteText levelRangeHigher; OsuSpriteText levelRangeLower; Container flagContainer; LinkFlowContainer hostText; - Children = new Drawable[] + InternalChildren = new Drawable[] { new FillFlowContainer { @@ -73,12 +69,13 @@ namespace osu.Game.Screens.Multi.Lounge.Components } }, }, - summaryContainer = new FillFlowContainer + new FillFlowContainer { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, + Colour = colours.Gray9, Children = new[] { summary = new OsuSpriteText @@ -101,9 +98,9 @@ namespace osu.Game.Screens.Multi.Lounge.Components hostText.AddLink(v.Username, null, LinkAction.OpenUserProfile, v.Id.ToString(), "Open profile", s => s.Font = "Exo2.0-BoldItalic"); flagContainer.Child = new DrawableFlag(v.Country) { RelativeSizeAxes = Axes.Both }; } - }); + }, true); - ParticipantCount.BindValueChanged(v => summary.Text = $"{v:#,0}{" participant".Pluralize(v == 1)}"); + ParticipantCount.BindValueChanged(v => summary.Text = $"{v:#,0}{" participant".Pluralize(v == 1)}", true); /*Participants.BindValueChanged(v => { @@ -112,11 +109,5 @@ namespace osu.Game.Screens.Multi.Lounge.Components levelRangeHigher.Text = ranks.Max().ToString(); });*/ } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - summaryContainer.Colour = colours.Gray9; - } } } diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index 16af06fdd7..45855c2812 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -3,7 +3,6 @@ using System; using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -11,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; @@ -24,24 +22,18 @@ using osuTK.Graphics; namespace osu.Game.Screens.Multi.Lounge.Components { - public class RoomInspector : Container + public class RoomInspector : MultiplayerComposite { private const float transition_duration = 100; - public readonly IBindable Room = new Bindable(); - private readonly MarginPadding contentPadding = new MarginPadding { Horizontal = 20, Vertical = 10 }; - private readonly RoomBindings bindings = new RoomBindings(); - private OsuColour colours; private Box statusStrip; - private UpdateableBeatmapBackgroundSprite background; private ParticipantCountDisplay participantCount; private OsuSpriteText name, status; private BeatmapTypeInfo beatmapTypeInfo; private ParticipantInfo participantInfo; - private MatchParticipants participants; [Resolved] private BeatmapManager beatmaps { get; set; } @@ -51,7 +43,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { this.colours = colours; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Box { @@ -84,7 +76,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components Masking = true, Children = new Drawable[] { - background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }, + new MultiplayerBackgroundSprite { RelativeSizeAxes = Axes.Both }, new Box { RelativeSizeAxes = Axes.Both, @@ -162,7 +154,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components }, new Drawable[] { - participants = new MatchParticipants + new MatchParticipants { RelativeSizeAxes = Axes.Both, } @@ -171,27 +163,15 @@ namespace osu.Game.Screens.Multi.Lounge.Components } }; - participantInfo.Host.BindTo(bindings.Host); - participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount); - participantInfo.Participants.BindTo(bindings.Participants); - participantCount.Participants.BindTo(bindings.Participants); - participantCount.ParticipantCount.BindTo(bindings.ParticipantCount); - participantCount.MaxParticipants.BindTo(bindings.MaxParticipants); - beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap); - beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset); - beatmapTypeInfo.Type.BindTo(bindings.Type); - background.Beatmap.BindTo(bindings.CurrentBeatmap); - bindings.Status.BindValueChanged(displayStatus); - bindings.Name.BindValueChanged(n => name.Text = n); - Room.BindValueChanged(updateRoom, true); + Status.BindValueChanged(displayStatus); + Name.BindValueChanged(n => name.Text = n); + + RoomID.BindValueChanged(updateRoom); } - private void updateRoom(Room room) + private void updateRoom(int? roomId) { - bindings.Room = room; - participants.Room = room; - - if (room != null) + if (roomId != null) { participantCount.FadeIn(transition_duration); beatmapTypeInfo.FadeIn(transition_duration); @@ -224,24 +204,10 @@ namespace osu.Game.Screens.Multi.Lounge.Components public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray8; } - private class MatchParticipants : CompositeDrawable + private class MatchParticipants : MultiplayerComposite { - private Room room; private readonly FillFlowContainer fill; - public Room Room - { - get { return room; } - set - { - if (room == value) - return; - - room = value; - updateParticipants(); - } - } - public MatchParticipants() { Padding = new MarginPadding { Horizontal = 10 }; @@ -259,6 +225,12 @@ namespace osu.Game.Screens.Multi.Lounge.Components }; } + [BackgroundDependencyLoader] + private void load() + { + RoomID.BindValueChanged(_ => updateParticipants(), true); + } + [Resolved] private APIAccess api { get; set; } @@ -266,7 +238,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components private void updateParticipants() { - var roomId = room?.RoomID.Value ?? 0; + var roomId = RoomID.Value ?? 0; request?.Cancel(); @@ -284,7 +256,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components request = new GetRoomScoresRequest(roomId); request.Success += scores => { - if (roomId != room.RoomID.Value) + if (roomId != RoomID.Value) return; fill.Clear(); diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs index ac13a16388..12d9b2957c 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs @@ -19,8 +19,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { public Action JoinRequested; - private readonly Bindable selectedRoom = new Bindable(); - public IBindable SelectedRoom => selectedRoom; + private readonly Bindable currentRoom = new Bindable(); private readonly IBindableList rooms = new BindableList(); @@ -47,6 +46,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components [BackgroundDependencyLoader] private void load() { + currentRoom.BindTo(roomManager.CurrentRoom); rooms.BindTo(roomManager.Rooms); rooms.ItemsAdded += addRooms; @@ -121,7 +121,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components else roomFlow.Children.ForEach(r => r.State = r.Room == room ? SelectionState.Selected : SelectionState.NotSelected); - selectedRoom.Value = room; + currentRoom.Value = room; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 5bc36090d5..50788a2fc2 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -24,13 +26,12 @@ namespace osu.Game.Screens.Multi.Lounge private readonly Action pushGameplayScreen; private readonly ProcessingOverlay processingOverlay; + private readonly Bindable currentRoom = new Bindable(); + public LoungeSubScreen(Action pushGameplayScreen) { this.pushGameplayScreen = pushGameplayScreen; - RoomInspector inspector; - RoomsContainer rooms; - InternalChildren = new Drawable[] { Filter = new FilterControl { Depth = -1 }, @@ -54,13 +55,13 @@ namespace osu.Game.Screens.Multi.Lounge { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = rooms = new RoomsContainer { JoinRequested = joinRequested } + Child = new RoomsContainer { JoinRequested = joinRequested } }, }, processingOverlay = new ProcessingOverlay { Alpha = 0 } } }, - inspector = new RoomInspector + new RoomInspector { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -71,11 +72,15 @@ namespace osu.Game.Screens.Multi.Lounge }, }; - inspector.Room.BindTo(rooms.SelectedRoom); - Filter.Search.Exit += this.Exit; } + [BackgroundDependencyLoader] + private void load(IRoomManager roomManager) + { + currentRoom.BindTo(roomManager.CurrentRoom); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -116,7 +121,7 @@ namespace osu.Game.Screens.Multi.Lounge processingOverlay.Show(); Manager?.JoinRoom(room, r => { - Push(room); + Open(room); processingOverlay.Hide(); }, _ => processingOverlay.Hide()); } @@ -124,13 +129,15 @@ namespace osu.Game.Screens.Multi.Lounge /// /// Push a room as a new subscreen. /// - public void Push(Room room) + public void Open(Room room) { // Handles the case where a room is clicked 3 times in quick succession if (!this.IsCurrentScreen()) return; - this.Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s))); + currentRoom.Value = room; + + this.Push(new MatchSubScreen(s => pushGameplayScreen?.Invoke(s))); } } } diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 29546f9b06..55c7c02be4 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -20,31 +20,27 @@ using osuTK.Graphics; namespace osu.Game.Screens.Multi.Match.Components { - public class Header : Container + public class Header : MultiplayerComposite { public const float HEIGHT = 200; - private readonly RoomBindings bindings = new RoomBindings(); + public MatchTabControl Tabs; - private readonly Box tabStrip; + public Action RequestBeatmapSelection; - public readonly MatchTabControl Tabs; - - public Action OnRequestSelectBeatmap; - - public Header(Room room) + public Header() { RelativeSizeAxes = Axes.X; Height = HEIGHT; + } - bindings.Room = room; - - BeatmapTypeInfo beatmapTypeInfo; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { BeatmapSelectButton beatmapButton; - UpdateableBeatmapBackgroundSprite background; ModDisplay modDisplay; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Container { @@ -52,7 +48,7 @@ namespace osu.Game.Screens.Multi.Match.Components Masking = true, Children = new Drawable[] { - background = new HeaderBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }, + new HeaderBackgroundSprite { RelativeSizeAxes = Axes.Both }, new Box { RelativeSizeAxes = Axes.Both, @@ -60,12 +56,13 @@ namespace osu.Game.Screens.Multi.Match.Components }, } }, - tabStrip = new Box + new Box { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.X, Height = 1, + Colour = colours.Yellow }, new Container { @@ -80,7 +77,7 @@ namespace osu.Game.Screens.Multi.Match.Components Direction = FillDirection.Vertical, Children = new Drawable[] { - beatmapTypeInfo = new BeatmapTypeInfo(), + new BeatmapTypeInfo(), modDisplay = new ModDisplay { Scale = new Vector2(0.75f), @@ -95,13 +92,13 @@ namespace osu.Game.Screens.Multi.Match.Components RelativeSizeAxes = Axes.Y, Width = 200, Padding = new MarginPadding { Vertical = 10 }, - Child = beatmapButton = new BeatmapSelectButton(room) + Child = beatmapButton = new BeatmapSelectButton { RelativeSizeAxes = Axes.Both, Height = 1, }, }, - Tabs = new MatchTabControl(room) + Tabs = new MatchTabControl { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -111,37 +108,36 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap); - beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset); - beatmapTypeInfo.Type.BindTo(bindings.Type); - background.Beatmap.BindTo(bindings.CurrentBeatmap); - bindings.CurrentMods.BindValueChanged(m => modDisplay.Current.Value = m, true); + CurrentMods.BindValueChanged(m => modDisplay.Current.Value = m, true); - beatmapButton.Action = () => OnRequestSelectBeatmap?.Invoke(); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - tabStrip.Colour = colours.Yellow; + beatmapButton.Action = () => RequestBeatmapSelection?.Invoke(); } private class BeatmapSelectButton : HeaderButton { - private readonly IBindable roomIDBind = new Bindable(); + [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.RoomID))] + private Bindable roomId { get; set; } - public BeatmapSelectButton(Room room) + public BeatmapSelectButton() { Text = "Select beatmap"; + } - roomIDBind.BindTo(room.RoomID); - roomIDBind.BindValueChanged(v => this.FadeTo(v.HasValue ? 0 : 1), true); + [BackgroundDependencyLoader] + private void load() + { + roomId.BindValueChanged(v => this.FadeTo(v.HasValue ? 0 : 1), true); } } - private class HeaderBeatmapBackgroundSprite : UpdateableBeatmapBackgroundSprite + private class HeaderBackgroundSprite : MultiplayerBackgroundSprite { - protected override double FadeDuration => 200; + protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BackgroundSprite { RelativeSizeAxes = Axes.Both }; + + private class BackgroundSprite : UpdateableBeatmapBackgroundSprite + { + protected override double FadeDuration => 200; + } } } } diff --git a/osu.Game/Screens/Multi/Match/Components/Info.cs b/osu.Game/Screens/Multi/Match/Components/Info.cs index 7894385afb..ec6dbb6d12 100644 --- a/osu.Game/Screens/Multi/Match/Components/Info.cs +++ b/osu.Game/Screens/Multi/Match/Components/Info.cs @@ -2,35 +2,37 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Online.Multiplayer; using osu.Game.Overlays.SearchableList; using osu.Game.Screens.Multi.Components; using osuTK; namespace osu.Game.Screens.Multi.Match.Components { - public class Info : Container + public class Info : MultiplayerComposite { public Action OnStart; - private readonly RoomBindings bindings = new RoomBindings(); - - public Info(Room room) + public Info() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; + } + [BackgroundDependencyLoader] + private void load() + { ReadyButton readyButton; ViewBeatmapButton viewBeatmapButton; HostInfo hostInfo; RoomStatusInfo statusInfo; - Children = new Drawable[] + InternalChildren = new Drawable[] { new Box { @@ -61,9 +63,9 @@ namespace osu.Game.Screens.Multi.Match.Components new OsuSpriteText { TextSize = 30, - Current = bindings.Name + Current = Name }, - new RoomStatusInfo(room), + new RoomStatusInfo(), } }, hostInfo = new HostInfo(), @@ -80,7 +82,7 @@ namespace osu.Game.Screens.Multi.Match.Components Children = new Drawable[] { viewBeatmapButton = new ViewBeatmapButton(), - readyButton = new ReadyButton(room) + readyButton = new ReadyButton { Action = () => OnStart?.Invoke() } @@ -90,11 +92,9 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - viewBeatmapButton.Beatmap.BindTo(bindings.CurrentBeatmap); - readyButton.Beatmap.BindTo(bindings.CurrentBeatmap); - hostInfo.Host.BindTo(bindings.Host); - - bindings.Room = room; + viewBeatmapButton.Beatmap.BindTo(CurrentBeatmap); + readyButton.Beatmap.BindTo(CurrentBeatmap); + hostInfo.Host.BindTo(Host); } } } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs index e0438e6729..0b61637cd5 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchChatDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Game.Online.Chat; using osu.Game.Online.Multiplayer; @@ -9,28 +10,31 @@ namespace osu.Game.Screens.Multi.Match.Components { public class MatchChatDisplay : StandAloneChatDisplay { - private readonly Room room; + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } + + [Resolved(typeof(Room), nameof(Room.ChannelId))] + private Bindable channelId { get; set; } [Resolved(CanBeNull = true)] private ChannelManager channelManager { get; set; } - public MatchChatDisplay(Room room) + public MatchChatDisplay() : base(true) { - this.room = room; } protected override void LoadComplete() { base.LoadComplete(); - room.RoomID.BindValueChanged(v => updateChannel(), true); + roomId.BindValueChanged(v => updateChannel(), true); } private void updateChannel() { - if (room.RoomID.Value != null) - Channel.Value = channelManager?.JoinChannel(new Channel { Id = room.ChannelId, Type = ChannelType.Multiplayer, Name = $"#mp_{room.RoomID}" }); + if (roomId.Value != null) + Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId, Type = ChannelType.Multiplayer, Name = $"#mp_{roomId.Value}" }); } } } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs index 33953bd5a6..60604eeb5c 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchLeaderboard.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -16,18 +17,13 @@ namespace osu.Game.Screens.Multi.Match.Components { public Action> ScoresLoaded; - public Room Room - { - get => bindings.Room; - set => bindings.Room = value; - } - - private readonly RoomBindings bindings = new RoomBindings(); + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } [BackgroundDependencyLoader] private void load() { - bindings.RoomID.BindValueChanged(id => + roomId.BindValueChanged(id => { if (id == null) return; @@ -39,10 +35,10 @@ namespace osu.Game.Screens.Multi.Match.Components protected override APIRequest FetchScores(Action> scoresCallback) { - if (bindings.RoomID.Value == null) + if (roomId.Value == null) return null; - var req = new GetRoomScoresRequest(bindings.RoomID.Value ?? 0); + var req = new GetRoomScoresRequest(roomId.Value ?? 0); req.Success += r => { diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 5b9402683e..dcbeb07904 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -22,43 +22,53 @@ namespace osu.Game.Screens.Multi.Match.Components { private const float transition_duration = 350; private const float field_padding = 45; - private const float disabled_alpha = 0.2f; - private readonly RoomBindings bindings = new RoomBindings(); + protected MatchSettings Settings { get; private set; } - private readonly Container content; - - private readonly OsuSpriteText typeLabel; - - protected readonly OsuTextBox NameField, MaxParticipantsField; - protected readonly OsuDropdown DurationField; - protected readonly RoomAvailabilityPicker AvailabilityPicker; - protected readonly GameTypePicker TypePicker; - protected readonly TriangleButton ApplyButton; - protected readonly OsuPasswordTextBox PasswordField; - - protected readonly OsuSpriteText ErrorText; - - private readonly ProcessingOverlay processingOverlay; - - private readonly Room room; - - [Resolved(CanBeNull = true)] - private IRoomManager manager { get; set; } - - public MatchSettingsOverlay(Room room) + [BackgroundDependencyLoader] + private void load() { - this.room = room; - - bindings.Room = room; - Masking = true; - Child = content = new Container + Child = Settings = new MatchSettings { RelativeSizeAxes = Axes.Both, - RelativePositionAxes = Axes.Y, - Children = new Drawable[] + RelativePositionAxes = Axes.Y + }; + } + + protected override void PopIn() + { + Settings.MoveToY(0, transition_duration, Easing.OutQuint); + } + + protected override void PopOut() + { + Settings.MoveToY(-1, transition_duration, Easing.InSine); + } + + protected class MatchSettings : MultiplayerComposite + { + private const float disabled_alpha = 0.2f; + + public OsuTextBox NameField, MaxParticipantsField; + public OsuDropdown DurationField; + public RoomAvailabilityPicker AvailabilityPicker; + public GameTypePicker TypePicker; + public TriangleButton ApplyButton; + + public OsuSpriteText ErrorText; + + private OsuSpriteText typeLabel; + private ProcessingOverlay processingOverlay; + + [Resolved(CanBeNull = true)] + private IRoomManager manager { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] { new Box { @@ -111,7 +121,10 @@ namespace osu.Game.Screens.Multi.Match.Components new Section("Room visibility") { Alpha = disabled_alpha, - Child = AvailabilityPicker = new RoomAvailabilityPicker(), + Child = AvailabilityPicker = new RoomAvailabilityPicker + { + Enabled = { Value = false } + }, }, new Section("Game type") { @@ -127,10 +140,12 @@ namespace osu.Game.Screens.Multi.Match.Components TypePicker = new GameTypePicker { RelativeSizeAxes = Axes.X, + Enabled = { Value = false } }, typeLabel = new OsuSpriteText { TextSize = 14, + Colour = colours.Yellow }, }, }, @@ -151,7 +166,8 @@ namespace osu.Game.Screens.Multi.Match.Components { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, - OnCommit = (sender, text) => apply(), + ReadOnly = true, + OnCommit = (sender, text) => apply() }, }, new Section("Duration") @@ -177,10 +193,11 @@ namespace osu.Game.Screens.Multi.Match.Components new Section("Password (optional)") { Alpha = disabled_alpha, - Child = PasswordField = new SettingsPasswordTextBox + Child = new SettingsPasswordTextBox { RelativeSizeAxes = Axes.X, TabbableContentContainer = this, + ReadOnly = true, OnCommit = (sender, text) => apply() }, }, @@ -222,6 +239,7 @@ namespace osu.Game.Screens.Multi.Match.Components Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Size = new Vector2(230, 55), + Enabled = { Value = false }, Action = apply, }, ErrorText = new OsuSpriteText @@ -229,7 +247,8 @@ namespace osu.Game.Screens.Multi.Match.Components Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Alpha = 0, - Depth = 1 + Depth = 1, + Colour = colours.RedDark } } } @@ -239,80 +258,58 @@ namespace osu.Game.Screens.Multi.Match.Components } }, processingOverlay = new ProcessingOverlay { Alpha = 0 } - }, - }; + }; - TypePicker.Current.ValueChanged += t => typeLabel.Text = t.Name; + TypePicker.Current.ValueChanged += t => typeLabel.Text = t.Name; - bindings.Name.BindValueChanged(n => NameField.Text = n, true); - bindings.Availability.BindValueChanged(a => AvailabilityPicker.Current.Value = a, true); - bindings.Type.BindValueChanged(t => TypePicker.Current.Value = t, true); - bindings.MaxParticipants.BindValueChanged(m => MaxParticipantsField.Text = m?.ToString(), true); - bindings.Duration.BindValueChanged(d => DurationField.Current.Value = d, true); - } + Name.BindValueChanged(n => NameField.Text = n, true); + Availability.BindValueChanged(a => AvailabilityPicker.Current.Value = a, true); + Type.BindValueChanged(t => TypePicker.Current.Value = t, true); + MaxParticipants.BindValueChanged(m => MaxParticipantsField.Text = m?.ToString(), true); + Duration.BindValueChanged(d => DurationField.Current.Value = d, true); + } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - typeLabel.Colour = colours.Yellow; - ErrorText.Colour = colours.RedDark; + protected override void Update() + { + base.Update(); - MaxParticipantsField.ReadOnly = true; - PasswordField.ReadOnly = true; - AvailabilityPicker.Enabled.Value = false; - TypePicker.Enabled.Value = false; - ApplyButton.Enabled.Value = false; - } + ApplyButton.Enabled.Value = hasValidSettings; + } - protected override void Update() - { - base.Update(); + private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0; - ApplyButton.Enabled.Value = hasValidSettings; - } - private bool hasValidSettings => bindings.Room.RoomID.Value == null && NameField.Text.Length > 0 && bindings.Playlist.Count > 0; + private void apply() + { + hideError(); - protected override void PopIn() - { - content.MoveToY(0, transition_duration, Easing.OutQuint); - } + Name.Value = NameField.Text; + Availability.Value = AvailabilityPicker.Current.Value; + Type.Value = TypePicker.Current.Value; - protected override void PopOut() - { - content.MoveToY(-1, transition_duration, Easing.InSine); - } + if (int.TryParse(MaxParticipantsField.Text, out int max)) + MaxParticipants.Value = max; + else + MaxParticipants.Value = null; - private void apply() - { - hideError(); + Duration.Value = DurationField.Current.Value; - bindings.Name.Value = NameField.Text; - bindings.Availability.Value = AvailabilityPicker.Current.Value; - bindings.Type.Value = TypePicker.Current.Value; + manager?.CreateRoom(Room, onSuccess, onError); - if (int.TryParse(MaxParticipantsField.Text, out int max)) - bindings.MaxParticipants.Value = max; - else - bindings.MaxParticipants.Value = null; + processingOverlay.Show(); + } - bindings.Duration.Value = DurationField.Current.Value; + private void hideError() => ErrorText.FadeOut(50); - manager?.CreateRoom(room, onSuccess, onError); + private void onSuccess(Room room) => processingOverlay.Hide(); - processingOverlay.Show(); - } + private void onError(string text) + { + ErrorText.Text = text; + ErrorText.FadeIn(50); - private void hideError() => ErrorText.FadeOut(50); - - private void onSuccess(Room room) => processingOverlay.Hide(); - - private void onError(string text) - { - ErrorText.Text = text; - ErrorText.FadeIn(50); - - processingOverlay.Hide(); + processingOverlay.Hide(); + } } private class SettingsTextBox : OsuTextBox diff --git a/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs b/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs index 84998417e9..7ac1016e72 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchTabControl.cs @@ -14,12 +14,11 @@ namespace osu.Game.Screens.Multi.Match.Components { public class MatchTabControl : PageTabControl { - private readonly IBindable roomIdBind = new Bindable(); + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } - public MatchTabControl(Room room) + public MatchTabControl() { - roomIdBind.BindTo(room.RoomID); - AddItem(new RoomMatchPage()); AddItem(new SettingsMatchPage()); } @@ -27,7 +26,7 @@ namespace osu.Game.Screens.Multi.Match.Components [BackgroundDependencyLoader] private void load() { - roomIdBind.BindValueChanged(v => + roomId.BindValueChanged(v => { if (v.HasValue) { diff --git a/osu.Game/Screens/Multi/Match/Components/Participants.cs b/osu.Game/Screens/Multi/Match/Components/Participants.cs index 5b9498ce7c..82a79475ff 100644 --- a/osu.Game/Screens/Multi/Match/Components/Participants.cs +++ b/osu.Game/Screens/Multi/Match/Components/Participants.cs @@ -1,9 +1,8 @@ // 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 System.Linq; -using osu.Framework.Configuration; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Overlays.SearchableList; @@ -13,16 +12,12 @@ using osuTK; namespace osu.Game.Screens.Multi.Match.Components { - public class Participants : CompositeDrawable + public class Participants : MultiplayerComposite { - public readonly IBindable> Users = new Bindable>(); - public readonly IBindable ParticipantCount = new Bindable(); - public readonly IBindable MaxParticipants = new Bindable(); - - public Participants() + [BackgroundDependencyLoader] + private void load() { FillFlowContainer usersFlow; - ParticipantCountDisplay count; InternalChild = new Container { @@ -36,7 +31,7 @@ namespace osu.Game.Screens.Multi.Match.Components Padding = new MarginPadding { Top = 10 }, Children = new Drawable[] { - count = new ParticipantCountDisplay + new ParticipantCountDisplay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -55,11 +50,7 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - count.Participants.BindTo(Users); - count.ParticipantCount.BindTo(ParticipantCount); - count.MaxParticipants.BindTo(MaxParticipants); - - Users.BindValueChanged(v => + Participants.BindValueChanged(v => { usersFlow.Children = v.Select(u => new UserPanel(u) { diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index 1bde6270f6..276e8f3530 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -16,7 +16,8 @@ namespace osu.Game.Screens.Multi.Match.Components { public readonly IBindable Beatmap = new Bindable(); - private readonly Room room; + [Resolved(typeof(Room), nameof(Room.EndDate))] + private Bindable endDate { get; set; } [Resolved] private IBindableBeatmap gameBeatmap { get; set; } @@ -26,9 +27,8 @@ namespace osu.Game.Screens.Multi.Match.Components private bool hasBeatmap; - public ReadyButton(Room room) + public ReadyButton() { - this.room = room; RelativeSizeAxes = Axes.Y; Size = new Vector2(200, 1); @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Multi.Match.Components return; } - bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < room.EndDate; + bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate; Enabled.Value = hasBeatmap && hasEnoughTime; } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 980f321c92..12930d814b 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Screens; @@ -22,130 +23,38 @@ namespace osu.Game.Screens.Multi.Match { public override bool AllowBeatmapRulesetChange => false; - public override string Title => room.RoomID.Value == null ? "New room" : room.Name.Value; + public override string Title => roomId?.Value == null ? "New room" : name.Value; + public override string ShortTitle => "room"; - private readonly RoomBindings bindings = new RoomBindings(); + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } - private readonly MatchLeaderboard leaderboard; + [Resolved(typeof(Room), nameof(Room.Name))] + private Bindable name { get; set; } - private readonly Action pushGameplayScreen; + [Resolved(typeof(Room), nameof(Room.Playlist))] + private BindableList playlist { get; set; } - [Cached] - private readonly Room room; - - [Resolved] - private BeatmapManager beatmapManager { get; set; } - - public MatchSubScreen(Room room, Action pushGameplayScreen) + public MatchSubScreen(Action pushGameplayScreen) { - this.room = room; - this.pushGameplayScreen = pushGameplayScreen; - - bindings.Room = room; - - MatchChatDisplay chat; - Components.Header header; - Info info; - GridContainer bottomRow; - MatchSettingsOverlay settings; - - InternalChildren = new Drawable[] + InternalChild = new Match(pushGameplayScreen) { - new GridContainer + RelativeSizeAxes = Axes.Both, + RequestBeatmapSelection = () => this.Push(new MatchSongSelect { - RelativeSizeAxes = Axes.Both, - Content = new[] + Selected = item => { - new Drawable[] { header = new Components.Header(room) { Depth = -1 } }, - new Drawable[] { info = new Info(room) { OnStart = onStart } }, - new Drawable[] - { - bottomRow = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - leaderboard = new MatchLeaderboard - { - Padding = new MarginPadding - { - Left = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Right = 10, - Vertical = 10, - }, - RelativeSizeAxes = Axes.Both, - Room = room - }, - new Container - { - Padding = new MarginPadding - { - Left = 10, - Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Vertical = 10, - }, - RelativeSizeAxes = Axes.Both, - Child = chat = new MatchChatDisplay(room) - { - RelativeSizeAxes = Axes.Both - } - }, - }, - }, - } - }, + playlist.Clear(); + playlist.Add(item); }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Distributed), - } - }, - new Container + }), + RequestExit = () => { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = Components.Header.HEIGHT }, - Child = settings = new MatchSettingsOverlay(room) { RelativeSizeAxes = Axes.Both }, - }, - }; - - header.OnRequestSelectBeatmap = () => this.Push(new MatchSongSelect - { - Selected = addPlaylistItem, - }); - - header.Tabs.Current.ValueChanged += t => - { - const float fade_duration = 500; - if (t is SettingsMatchPage) - { - settings.Show(); - info.FadeOut(fade_duration, Easing.OutQuint); - bottomRow.FadeOut(fade_duration, Easing.OutQuint); - } - else - { - settings.Hide(); - info.FadeIn(fade_duration, Easing.OutQuint); - bottomRow.FadeIn(fade_duration, Easing.OutQuint); + if (this.IsCurrentScreen()) + this.Exit(); } }; - - chat.Exit += () => - { - if (this.IsCurrentScreen()) - this.Exit(); - }; - } - - [BackgroundDependencyLoader] - private void load() - { - beatmapManager.ItemAdded += beatmapAdded; } public override bool OnExiting(IScreen next) @@ -154,73 +63,191 @@ namespace osu.Game.Screens.Multi.Match return base.OnExiting(next); } - protected override void LoadComplete() + private class Match : MultiplayerComposite { - base.LoadComplete(); + public Action RequestBeatmapSelection; + public Action RequestExit; - bindings.CurrentBeatmap.BindValueChanged(setBeatmap, true); - bindings.CurrentRuleset.BindValueChanged(setRuleset, true); - } + private readonly Action pushGameplayScreen; - private void setBeatmap(BeatmapInfo beatmap) - { - // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info - var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); + private MatchLeaderboard leaderboard; - Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); - } + [Resolved] + private IBindableBeatmap gameBeatmap { get; set; } - private void setRuleset(RulesetInfo ruleset) - { - if (ruleset == null) - return; + [Resolved] + private BeatmapManager beatmapManager { get; set; } - Game?.ForcefullySetRuleset(ruleset); - } + [Resolved(CanBeNull = true)] + private OsuGame game { get; set; } - private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => - { - if (Beatmap.Value != beatmapManager.DefaultBeatmap) - return; - - if (bindings.CurrentBeatmap.Value == null) - return; - - // Try to retrieve the corresponding local beatmap - var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == bindings.CurrentBeatmap.Value.OnlineBeatmapID); - - if (localBeatmap != null) - Game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); - }); - - private void addPlaylistItem(PlaylistItem item) - { - bindings.Playlist.Clear(); - bindings.Playlist.Add(item); - } - - private void onStart() - { - Beatmap.Value.Mods.Value = bindings.CurrentMods.Value.ToArray(); - - switch (bindings.Type.Value) + public Match(Action pushGameplayScreen) { - default: - case GameTypeTimeshift _: - pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(room, room.Playlist.First().ID) - { - Exited = () => leaderboard.RefreshScores() - })); - break; + this.pushGameplayScreen = pushGameplayScreen; } - } - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); + [BackgroundDependencyLoader] + private void load() + { + MatchChatDisplay chat; + Components.Header header; + Info info; + GridContainer bottomRow; + MatchSettingsOverlay settings; - if (beatmapManager != null) - beatmapManager.ItemAdded -= beatmapAdded; + InternalChildren = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + header = new Components.Header + { + Depth = -1, + RequestBeatmapSelection = () => RequestBeatmapSelection?.Invoke() + } + }, + new Drawable[] { info = new Info { OnStart = onStart } }, + new Drawable[] + { + bottomRow = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + leaderboard = new MatchLeaderboard + { + Padding = new MarginPadding + { + Left = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Right = 10, + Vertical = 10, + }, + RelativeSizeAxes = Axes.Both + }, + new Container + { + Padding = new MarginPadding + { + Left = 10, + Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10, + }, + RelativeSizeAxes = Axes.Both, + Child = chat = new MatchChatDisplay + { + RelativeSizeAxes = Axes.Both + } + }, + }, + }, + } + }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Distributed), + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = Components.Header.HEIGHT }, + Child = settings = new MatchSettingsOverlay { RelativeSizeAxes = Axes.Both }, + }, + }; + + header.Tabs.Current.ValueChanged += t => + { + const float fade_duration = 500; + if (t is SettingsMatchPage) + { + settings.Show(); + info.FadeOut(fade_duration, Easing.OutQuint); + bottomRow.FadeOut(fade_duration, Easing.OutQuint); + } + else + { + settings.Hide(); + info.FadeIn(fade_duration, Easing.OutQuint); + bottomRow.FadeIn(fade_duration, Easing.OutQuint); + } + }; + + chat.Exit += () => RequestExit?.Invoke(); + + beatmapManager.ItemAdded += beatmapAdded; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentBeatmap.BindValueChanged(setBeatmap, true); + CurrentRuleset.BindValueChanged(setRuleset, true); + } + + private void setBeatmap(BeatmapInfo beatmap) + { + // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info + var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); + + game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); + } + + private void setRuleset(RulesetInfo ruleset) + { + if (ruleset == null) + return; + + game?.ForcefullySetRuleset(ruleset); + } + + private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => + { + if (gameBeatmap.Value != beatmapManager.DefaultBeatmap) + return; + + if (CurrentBeatmap.Value == null) + return; + + // Try to retrieve the corresponding local beatmap + var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == CurrentBeatmap.Value.OnlineBeatmapID); + + if (localBeatmap != null) + game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); + }); + + private void onStart() + { + gameBeatmap.Value.Mods.Value = CurrentMods.Value.ToArray(); + + switch (Type.Value) + { + default: + case GameTypeTimeshift _: + pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(Playlist.First().ID) + { + Exited = () => leaderboard.RefreshScores() + })); + break; + } + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmapManager != null) + beatmapManager.ItemAdded -= beatmapAdded; + } } } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index f0297cdfa9..6851bc2a2a 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -123,7 +123,7 @@ namespace osu.Game.Screens.Multi Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, }, Text = "Create room", - Action = () => loungeSubScreen.Push(new Room + Action = () => loungeSubScreen.Open(new Room { Name = { Value = $"{api.LocalUser}'s awesome room" } }), diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs index f09750b112..1e68727438 100644 --- a/osu.Game/Screens/Multi/MultiplayerComposite.cs +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -18,52 +18,52 @@ namespace osu.Game.Screens.Multi public class MultiplayerComposite : CompositeDrawable { [Resolved] - public Room Room { get; private set; } + protected Room Room { get; private set; } [Resolved(typeof(Room))] - public Bindable RoomID { get; private set; } + protected Bindable RoomID { get; private set; } [Resolved(typeof(Room))] - public Bindable Name { get; private set; } + protected Bindable Name { get; private set; } [Resolved(typeof(Room))] - public Bindable Host { get; private set; } + protected Bindable Host { get; private set; } [Resolved(typeof(Room))] - public Bindable Status { get; private set; } + protected Bindable Status { get; private set; } [Resolved(typeof(Room))] - public Bindable Type { get; private set; } + protected Bindable Type { get; private set; } [Resolved(typeof(Room))] - public BindableList Playlist { get; private set; } + protected BindableList Playlist { get; private set; } [Resolved(typeof(Room))] - public Bindable> Participants { get; private set; } + protected Bindable> Participants { get; private set; } [Resolved(typeof(Room))] - public Bindable ParticipantCount { get; private set; } + protected Bindable ParticipantCount { get; private set; } [Resolved(typeof(Room))] - public Bindable MaxParticipants { get; private set; } + protected Bindable MaxParticipants { get; private set; } [Resolved(typeof(Room))] - public Bindable EndDate { get; private set; } + protected Bindable EndDate { get; private set; } [Resolved(typeof(Room))] - public Bindable Availability { get; private set; } + protected Bindable Availability { get; private set; } [Resolved(typeof(Room))] - public Bindable Duration { get; private set; } + protected Bindable Duration { get; private set; } private readonly Bindable currentBeatmap = new Bindable(); - public IBindable CurrentBeatmap => currentBeatmap; + protected IBindable CurrentBeatmap => currentBeatmap; private readonly Bindable> currentMods = new Bindable>(); - public IBindable> CurrentMods => currentMods; + protected IBindable> CurrentMods => currentMods; private readonly Bindable currentRuleset = new Bindable(); - public IBindable CurrentRuleset => currentRuleset; + protected IBindable CurrentRuleset => currentRuleset; protected override void LoadComplete() { diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 36cf0f0282..6cddd235e1 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Threading; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; @@ -21,15 +22,16 @@ namespace osu.Game.Screens.Multi.Play { public Action Exited; - private readonly Room room; + [Resolved(typeof(Room), nameof(Room.RoomID))] + private Bindable roomId { get; set; } + private readonly int playlistItemId; [Resolved] private APIAccess api { get; set; } - public TimeshiftPlayer(Room room, int playlistItemId) + public TimeshiftPlayer(int playlistItemId) { - this.room = room; this.playlistItemId = playlistItemId; } @@ -42,7 +44,7 @@ namespace osu.Game.Screens.Multi.Play bool failed = false; - var req = new CreateRoomScoreRequest(room.RoomID.Value ?? 0, playlistItemId); + var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItemId); req.Success += r => token = r.ID; req.Failure += e => { @@ -87,7 +89,7 @@ namespace osu.Game.Screens.Multi.Play Debug.Assert(token != null); - var request = new SubmitRoomScoreRequest(token.Value, room.RoomID.Value ?? 0, playlistItemId, score); + var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItemId, score); request.Failure += e => Logger.Error(e, "Failed to submit score"); api.Queue(request); } @@ -99,6 +101,6 @@ namespace osu.Game.Screens.Multi.Play Exited = null; } - protected override Results CreateResults(ScoreInfo score) => new MatchResults(score, room); + protected override Results CreateResults(ScoreInfo score) => new MatchResults(score); } } diff --git a/osu.Game/Screens/Multi/Ranking/MatchResults.cs b/osu.Game/Screens/Multi/Ranking/MatchResults.cs index d14d94928d..db52a79ccb 100644 --- a/osu.Game/Screens/Multi/Ranking/MatchResults.cs +++ b/osu.Game/Screens/Multi/Ranking/MatchResults.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Online.Multiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Ranking.Types; using osu.Game.Screens.Ranking; @@ -12,19 +11,16 @@ namespace osu.Game.Screens.Multi.Ranking { public class MatchResults : Results { - private readonly Room room; - - public MatchResults(ScoreInfo score, Room room) + public MatchResults(ScoreInfo score) : base(score) { - this.room = room; } protected override IEnumerable CreateResultPages() => new IResultPageInfo[] { new ScoreOverviewPageInfo(Score, Beatmap), new LocalLeaderboardPageInfo(Score, Beatmap), - new RoomLeaderboardPageInfo(Score, Beatmap, room), + new RoomLeaderboardPageInfo(Score, Beatmap), }; } } diff --git a/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs b/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs index a1d0a1ea44..1b4c99d972 100644 --- a/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs +++ b/osu.Game/Screens/Multi/Ranking/Pages/RoomLeaderboardPage.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Internal; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -23,16 +24,15 @@ namespace osu.Game.Screens.Multi.Ranking.Pages { public class RoomLeaderboardPage : ResultsPage { - private readonly Room room; - private OsuColour colours; - private TextFlowContainer rankText; - public RoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap, Room room) + [Resolved(typeof(Room), nameof(Room.Name))] + private Bindable name { get; set; } + + public RoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap) : base(score, beatmap) { - this.room = room; } [BackgroundDependencyLoader] @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages { RelativeSizeAxes = Axes.Both, BackgroundColour = colours.Gray6, - Child = leaderboard = CreateLeaderboard(room) + Child = leaderboard = CreateLeaderboard() }, rankText = new TextFlowContainer { @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Multi.Ranking.Pages s.Colour = colours.GrayF; }; - rankText.AddText(room.Name + "\n", white); + rankText.AddText(name + "\n", white); rankText.AddText("You are placed ", gray); int index = scores.IndexOf(new APIRoomScoreInfo { User = Score.User }, new FuncEqualityComparer((s1, s2) => s1.User.Id.Equals(s2.User.Id))); @@ -98,15 +98,10 @@ namespace osu.Game.Screens.Multi.Ranking.Pages rankText.AddText("in the room!", gray); } - protected virtual MatchLeaderboard CreateLeaderboard(Room room) => new ResultsMatchLeaderboard(room); + protected virtual MatchLeaderboard CreateLeaderboard() => new ResultsMatchLeaderboard(); public class ResultsMatchLeaderboard : MatchLeaderboard { - public ResultsMatchLeaderboard(Room room) - { - Room = room; - } - protected override bool FadeTop => true; protected override LeaderboardScore CreateDrawableScore(APIRoomScoreInfo model, int index) diff --git a/osu.Game/Screens/Multi/Ranking/Types/RoomLeaderboardPageInfo.cs b/osu.Game/Screens/Multi/Ranking/Types/RoomLeaderboardPageInfo.cs index 09ebef4ee2..6cc13f88a5 100644 --- a/osu.Game/Screens/Multi/Ranking/Types/RoomLeaderboardPageInfo.cs +++ b/osu.Game/Screens/Multi/Ranking/Types/RoomLeaderboardPageInfo.cs @@ -3,7 +3,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Online.Multiplayer; using osu.Game.Scoring; using osu.Game.Screens.Multi.Ranking.Pages; using osu.Game.Screens.Ranking; @@ -14,19 +13,17 @@ namespace osu.Game.Screens.Multi.Ranking.Types { private readonly ScoreInfo score; private readonly WorkingBeatmap beatmap; - private readonly Room room; - public RoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap, Room room) + public RoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap) { this.score = score; this.beatmap = beatmap; - this.room = room; } public FontAwesome Icon => FontAwesome.fa_users; public string Name => "Room Leaderboard"; - public virtual ResultsPage CreatePage() => new RoomLeaderboardPage(score, beatmap, room); + public virtual ResultsPage CreatePage() => new RoomLeaderboardPage(score, beatmap); } } diff --git a/osu.Game/Screens/Multi/RoomBindings.cs b/osu.Game/Screens/Multi/RoomBindings.cs deleted file mode 100644 index e3114c768b..0000000000 --- a/osu.Game/Screens/Multi/RoomBindings.cs +++ /dev/null @@ -1,106 +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 System; -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Configuration; -using osu.Game.Beatmaps; -using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; -using osu.Game.Users; - -namespace osu.Game.Screens.Multi -{ - /// - /// Helper class which binds to values from a . - /// - public class RoomBindings - { - public RoomBindings() - { - Playlist.ItemsAdded += _ => updatePlaylist(); - Playlist.ItemsRemoved += _ => updatePlaylist(); - } - - private Room room; - - /// - /// The to bind to. - /// - public Room Room - { - get => room; - set - { - if (room == value) - return; - - if (room != null) - { - RoomID.UnbindFrom(room.RoomID); - Name.UnbindFrom(room.Name); - Host.UnbindFrom(room.Host); - Status.UnbindFrom(room.Status); - Type.UnbindFrom(room.Type); - Playlist.UnbindFrom(room.Playlist); - Participants.UnbindFrom(room.Participants); - ParticipantCount.UnbindFrom(room.ParticipantCount); - MaxParticipants.UnbindFrom(room.MaxParticipants); - EndDate.UnbindFrom(room.EndDate); - Availability.UnbindFrom(room.Availability); - Duration.UnbindFrom(room.Duration); - } - - room = value ?? new Room(); - - RoomID.BindTo(room.RoomID); - Name.BindTo(room.Name); - Host.BindTo(room.Host); - Status.BindTo(room.Status); - Type.BindTo(room.Type); - Playlist.BindTo(room.Playlist); - Participants.BindTo(room.Participants); - ParticipantCount.BindTo(room.ParticipantCount); - MaxParticipants.BindTo(room.MaxParticipants); - EndDate.BindTo(room.EndDate); - Availability.BindTo(room.Availability); - Duration.BindTo(room.Duration); - } - } - - private void updatePlaylist() - { - // Todo: We only ever have one playlist item for now. In the future, this will be user-settable - - var playlistItem = Playlist.FirstOrDefault(); - - currentBeatmap.Value = playlistItem?.Beatmap; - currentMods.Value = playlistItem?.RequiredMods ?? Enumerable.Empty(); - currentRuleset.Value = playlistItem?.Ruleset; - } - - public readonly Bindable RoomID = new Bindable(); - public readonly Bindable Name = new Bindable(); - public readonly Bindable Host = new Bindable(); - public readonly Bindable Status = new Bindable(); - public readonly Bindable Type = new Bindable(); - public readonly BindableList Playlist = new BindableList(); - public readonly Bindable> Participants = new Bindable>(); - public readonly Bindable ParticipantCount = new Bindable(); - public readonly Bindable MaxParticipants = new Bindable(); - public readonly Bindable EndDate = new Bindable(); - public readonly Bindable Availability = new Bindable(); - public readonly Bindable Duration = new Bindable(); - - private readonly Bindable currentBeatmap = new Bindable(); - public IBindable CurrentBeatmap => currentBeatmap; - - private readonly Bindable> currentMods = new Bindable>(); - public IBindable> CurrentMods => currentMods; - - private readonly Bindable currentRuleset = new Bindable(); - public IBindable CurrentRuleset => currentRuleset; - } -} diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index d9ddd6f867..8cf50245e0 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Multi private readonly BindableList rooms = new BindableList(); public IBindableList Rooms => rooms; - public readonly Bindable CurrentRoom = new Bindable(); + public Bindable CurrentRoom { get; } = new Bindable(); [Resolved] private APIAccess api { get; set; } @@ -51,6 +51,8 @@ namespace osu.Game.Screens.Multi update(room, result); addRoom(room); + CurrentRoom.Value = room; + RoomsUpdated?.Invoke(); onSuccess?.Invoke(room); diff --git a/osu.Game/Tests/Visual/MultiplayerTestCase.cs b/osu.Game/Tests/Visual/MultiplayerTestCase.cs new file mode 100644 index 0000000000..5ed4f012c6 --- /dev/null +++ b/osu.Game/Tests/Visual/MultiplayerTestCase.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Online.Multiplayer; + +namespace osu.Game.Tests.Visual +{ + public class MultiplayerTestCase : OsuTestCase + { + private Room room; + + protected Room Room + { + get => room; + set + { + if (room == value) + return; + room = value; + + if (dependencies != null) + dependencies.Model.Value = value; + } + } + + private CachedModelDependencyContainer dependencies; + + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + { + dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); + dependencies.Model.Value = room; + return dependencies; + } + } +} From 594dffba417514c2bb382dd58630a5e7e59e16ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 5 Feb 2019 19:11:20 +0900 Subject: [PATCH 041/167] Fix a few cases of missed instant invocation --- osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs | 2 +- osu.Game/Screens/Multi/Components/ModeTypeInfo.cs | 4 ++-- osu.Game/Screens/Multi/Components/ParticipantCount.cs | 3 +-- osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs | 7 +++---- .../Screens/Multi/Match/Components/MatchSettingsOverlay.cs | 3 +-- osu.Game/Screens/Multi/Match/Components/Participants.cs | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs index 35d1ffad1c..3904df2069 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.Multi.Components beatmapAuthor.AddText("mapped by ", s => s.Colour = OsuColour.Gray(0.8f)); beatmapAuthor.AddLink(v.Metadata.Author.Username, null, LinkAction.OpenUserProfile, v.Metadata.Author.Id.ToString(), "View Profile"); } - }); + }, true); } } } diff --git a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs index 6ae3b7be7c..97ea1b5f36 100644 --- a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs @@ -46,8 +46,8 @@ namespace osu.Game.Screens.Multi.Components }; CurrentBeatmap.BindValueChanged(_ => updateBeatmap()); - CurrentRuleset.BindValueChanged(_ => updateBeatmap()); - Type.BindValueChanged(v => gameTypeContainer.Child = new DrawableGameType(v) { Size = new Vector2(height) }); + CurrentRuleset.BindValueChanged(_ => updateBeatmap(), true); + Type.BindValueChanged(v => gameTypeContainer.Child = new DrawableGameType(v) { Size = new Vector2(height) }, true); } private void updateBeatmap() diff --git a/osu.Game/Screens/Multi/Components/ParticipantCount.cs b/osu.Game/Screens/Multi/Components/ParticipantCount.cs index 9711767924..3b002ee034 100644 --- a/osu.Game/Screens/Multi/Components/ParticipantCount.cs +++ b/osu.Game/Screens/Multi/Components/ParticipantCount.cs @@ -52,9 +52,8 @@ namespace osu.Game.Screens.Multi.Components } }; - Participants.BindValueChanged(v => count.Text = v.Count().ToString()); MaxParticipants.BindValueChanged(_ => updateMax(), true); - ParticipantCount.BindValueChanged(v => count.Text = v.ToString("#,0")); + ParticipantCount.BindValueChanged(v => count.Text = v.ToString("#,0"), true); } private void updateMax() diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index 45855c2812..754d8b8d24 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -163,10 +163,9 @@ namespace osu.Game.Screens.Multi.Lounge.Components } }; - Status.BindValueChanged(displayStatus); - Name.BindValueChanged(n => name.Text = n); - - RoomID.BindValueChanged(updateRoom); + Status.BindValueChanged(displayStatus, true); + Name.BindValueChanged(n => name.Text = n, true); + RoomID.BindValueChanged(updateRoom, true); } private void updateRoom(int? roomId) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index dcbeb07904..75cabe8b35 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -260,8 +260,7 @@ namespace osu.Game.Screens.Multi.Match.Components processingOverlay = new ProcessingOverlay { Alpha = 0 } }; - TypePicker.Current.ValueChanged += t => typeLabel.Text = t.Name; - + TypePicker.Current.BindValueChanged(t => typeLabel.Text = t?.Name ?? string.Empty, true); Name.BindValueChanged(n => NameField.Text = n, true); Availability.BindValueChanged(a => AvailabilityPicker.Current.Value = a, true); Type.BindValueChanged(t => TypePicker.Current.Value = t, true); diff --git a/osu.Game/Screens/Multi/Match/Components/Participants.cs b/osu.Game/Screens/Multi/Match/Components/Participants.cs index 82a79475ff..1565d75c21 100644 --- a/osu.Game/Screens/Multi/Match/Components/Participants.cs +++ b/osu.Game/Screens/Multi/Match/Components/Participants.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Multi.Match.Components Width = 300, OnLoadComplete = d => d.FadeInFromZero(60), }).ToList(); - }); + }, true); } } } From 0e3a087ed3044990f97182bafe30fc0e07883ddf Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 6 Feb 2019 12:02:04 +0900 Subject: [PATCH 042/167] Fix VSCode Restore task --- .vscode/tasks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2e9bec22c2..de799a7c03 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -70,7 +70,8 @@ "type": "shell", "command": "dotnet", "args": [ - "restore" + "restore", + "osu.sln" ], "problemMatcher": [] } From a9aa22c97bd3247723b2bbab93eff1e0302a9f57 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Feb 2019 13:52:48 +0900 Subject: [PATCH 043/167] Remove ShadowModel requirement --- .../Multi/Components/ParticipantCount.cs | 1 - .../Multi/Lounge/Components/DrawableRoom.cs | 49 +++++++++++++------ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Multi/Components/ParticipantCount.cs b/osu.Game/Screens/Multi/Components/ParticipantCount.cs index 3b002ee034..27bfc9a3f7 100644 --- a/osu.Game/Screens/Multi/Components/ParticipantCount.cs +++ b/osu.Game/Screens/Multi/Components/ParticipantCount.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.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index 3918148b07..bfbf81f298 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -92,12 +93,13 @@ namespace osu.Game.Screens.Multi.Lounge.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - Box sideStrip; - OsuSpriteText name; - Children = new Drawable[] { - selectionBox, + new StatusColouredContainer + { + RelativeSizeAxes = Axes.Both, + Child = selectionBox + }, new Container { RelativeSizeAxes = Axes.Both, @@ -120,10 +122,11 @@ namespace osu.Game.Screens.Multi.Lounge.Components RelativeSizeAxes = Axes.Both, Colour = OsuColour.FromHex(@"212121"), }, - sideStrip = new Box + new StatusColouredContainer { RelativeSizeAxes = Axes.Y, Width = side_strip_width, + Child = new Box { RelativeSizeAxes = Axes.Both } }, new Container { @@ -152,9 +155,10 @@ namespace osu.Game.Screens.Multi.Lounge.Components Spacing = new Vector2(5f), Children = new Drawable[] { - name = new OsuSpriteText + new StatusColouredContainer { - TextSize = 18 + AutoSizeAxes = Axes.Both, + Child = new RoomName { TextSize = 18 } }, new ParticipantInfo(), }, @@ -184,13 +188,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components }, }, }; - - dependencies.ShadowModel.Name.BindValueChanged(n => name.Text = n, true); - dependencies.ShadowModel.Status.BindValueChanged(s => - { - foreach (Drawable d in new Drawable[] { selectionBox, sideStrip }) - d.FadeColour(s.GetAppropriateColour(colours), transition_duration); - }, true); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -205,5 +202,29 @@ namespace osu.Game.Screens.Multi.Lounge.Components base.LoadComplete(); this.FadeInFromZero(transition_duration); } + + private class RoomName : OsuSpriteText + { + [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.Name))] + private Bindable name { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + Current = name; + } + } + + private class StatusColouredContainer : Container + { + [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.Status))] + private Bindable status { get; set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + status.BindValueChanged(s => this.FadeColour(s.GetAppropriateColour(colours), transition_duration), true); + } + } } } From 3207983a1c2b17fd3d9658cbdc705c5ebccb9b95 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Feb 2019 13:59:11 +0900 Subject: [PATCH 044/167] Fix current room being set to null when parted --- osu.Game/Screens/Multi/RoomManager.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index 8cf50245e0..ceee918586 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -25,6 +25,8 @@ namespace osu.Game.Screens.Multi public Bindable CurrentRoom { get; } = new Bindable(); + private Room joinedRoom; + [Resolved] private APIAccess api { get; set; } @@ -50,8 +52,7 @@ namespace osu.Game.Screens.Multi { update(room, result); addRoom(room); - - CurrentRoom.Value = room; + joinRoom(room); RoomsUpdated?.Invoke(); @@ -79,7 +80,7 @@ namespace osu.Game.Screens.Multi currentJoinRoomRequest = new JoinRoomRequest(room, api.LocalUser.Value); currentJoinRoomRequest.Success += () => { - CurrentRoom.Value = room; + joinRoom(room); onSuccess?.Invoke(room); }; @@ -92,13 +93,19 @@ namespace osu.Game.Screens.Multi api.Queue(currentJoinRoomRequest); } + private void joinRoom(Room room) + { + CurrentRoom.Value = room; + joinedRoom = room; + } + public void PartRoom() { - if (CurrentRoom.Value == null) + if (joinedRoom == null) return; - api.Queue(new PartRoomRequest(CurrentRoom.Value, api.LocalUser.Value)); - CurrentRoom.Value = null; + api.Queue(new PartRoomRequest(joinedRoom, api.LocalUser.Value)); + joinedRoom = null; } public void UpdateRooms(List newRooms) From 9f8e724a7659e20bb9c8787c5daa65862ae21e0a Mon Sep 17 00:00:00 2001 From: ProgrammaticNajel Date: Wed, 6 Feb 2019 17:14:33 +0800 Subject: [PATCH 045/167] Fix background being cut off at the top of the screen (#4207) Fix background being cut off at the top of the screen --- osu.Game/Screens/BackgroundScreenStack.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/BackgroundScreenStack.cs b/osu.Game/Screens/BackgroundScreenStack.cs index 7bf167295c..b010a70e66 100644 --- a/osu.Game/Screens/BackgroundScreenStack.cs +++ b/osu.Game/Screens/BackgroundScreenStack.cs @@ -14,6 +14,8 @@ namespace osu.Game.Screens { Scale = new Vector2(1.06f); RelativeSizeAxes = Axes.Both; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; } //public float ParallaxAmount { set => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * value; } From 4f86fe92eab271cf2edecae2f22a50e0965e39ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 6 Feb 2019 18:50:43 +0900 Subject: [PATCH 046/167] Fix match settings not showing --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 12930d814b..adfd77c182 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -164,7 +164,7 @@ namespace osu.Game.Screens.Multi.Match }, }; - header.Tabs.Current.ValueChanged += t => + header.Tabs.Current.BindValueChanged(t => { const float fade_duration = 500; if (t is SettingsMatchPage) @@ -179,7 +179,7 @@ namespace osu.Game.Screens.Multi.Match info.FadeIn(fade_duration, Easing.OutQuint); bottomRow.FadeIn(fade_duration, Easing.OutQuint); } - }; + }, true); chat.Exit += () => RequestExit?.Invoke(); From ae9d5f999c3c391e043bcaf7d31d71e8e8ec24c8 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Wed, 6 Feb 2019 21:28:42 +0100 Subject: [PATCH 047/167] Use correct DifficultyAttributes where possible --- osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs | 2 +- osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 5b6fd4ecf3..b8588dbce2 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime)); if (!calculateStrainValues(difficultyHitObjects, timeRate)) - return new DifficultyAttributes(mods, 0); + return new ManiaDifficultyAttributes(mods, 0); double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 16cebb0d96..2322446666 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); if (!calculateStrainValues(difficultyHitObjects, timeRate)) - return new DifficultyAttributes(mods, 0); + return new TaikoDifficultyAttributes(mods, 0); double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor; From 6e2ecd5fccbc3efb9fef3831e51ed4be1d68bc4d Mon Sep 17 00:00:00 2001 From: Moritz Bender <35152647+Morilli@users.noreply.github.com> Date: Wed, 6 Feb 2019 21:59:47 +0100 Subject: [PATCH 048/167] Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94ab385bfd..8cfc2ffebf 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ Sometimes it may be necessary to cross-test changes in [osu-resources](https://g ## Code analysis -Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice. +Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install resharper or use rider to get inline support in your IDE of choice. # Contributing From 2d615482514c1cea76bc6bfe126beb2e1ceb0c3b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Feb 2019 16:45:02 +0900 Subject: [PATCH 049/167] Fix room name being coloured --- osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index bfbf81f298..8a2b033ac8 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -155,11 +155,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components Spacing = new Vector2(5f), Children = new Drawable[] { - new StatusColouredContainer - { - AutoSizeAxes = Axes.Both, - Child = new RoomName { TextSize = 18 } - }, + new RoomName { TextSize = 18 }, new ParticipantInfo(), }, }, From 1e0135af6f10b46012ff49dc8509db6b52aa57dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Feb 2019 17:52:40 +0900 Subject: [PATCH 050/167] Move StatusColouredContainer into a more public location --- .../Components/StatusColouredContainer.cs | 31 +++++++++++++++++++ .../Multi/Lounge/Components/DrawableRoom.cs | 16 ++-------- 2 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 osu.Game/Screens/Multi/Components/StatusColouredContainer.cs diff --git a/osu.Game/Screens/Multi/Components/StatusColouredContainer.cs b/osu.Game/Screens/Multi/Components/StatusColouredContainer.cs new file mode 100644 index 0000000000..6d573d0866 --- /dev/null +++ b/osu.Game/Screens/Multi/Components/StatusColouredContainer.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Online.Multiplayer; + +namespace osu.Game.Screens.Multi.Components +{ + public class StatusColouredContainer : Container + { + private readonly double transitionDuration; + + [Resolved(typeof(Room), nameof(Room.Status))] + private Bindable status { get; set; } + + public StatusColouredContainer(double transitionDuration = 100) + { + this.transitionDuration = transitionDuration; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + status.BindValueChanged(s => this.FadeColour(s.GetAppropriateColour(colours), transitionDuration), true); + } + } +} diff --git a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs index 8a2b033ac8..afe2b70524 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/DrawableRoom.cs @@ -95,7 +95,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components { Children = new Drawable[] { - new StatusColouredContainer + new StatusColouredContainer(transition_duration) { RelativeSizeAxes = Axes.Both, Child = selectionBox @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components RelativeSizeAxes = Axes.Both, Colour = OsuColour.FromHex(@"212121"), }, - new StatusColouredContainer + new StatusColouredContainer(transition_duration) { RelativeSizeAxes = Axes.Y, Width = side_strip_width, @@ -210,17 +210,5 @@ namespace osu.Game.Screens.Multi.Lounge.Components Current = name; } } - - private class StatusColouredContainer : Container - { - [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.Status))] - private Bindable status { get; set; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - status.BindValueChanged(s => this.FadeColour(s.GetAppropriateColour(colours), transition_duration), true); - } - } } } From 1c2450a95a0a201d088eb14cdac3b7593e7a4cbc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Feb 2019 17:59:14 +0900 Subject: [PATCH 051/167] Fix up room inspector status --- .../Multi/Lounge/Components/RoomInspector.cs | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index 754d8b8d24..a25d49a514 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -28,21 +29,19 @@ namespace osu.Game.Screens.Multi.Lounge.Components private readonly MarginPadding contentPadding = new MarginPadding { Horizontal = 20, Vertical = 10 }; - private OsuColour colours; - private Box statusStrip; private ParticipantCountDisplay participantCount; - private OsuSpriteText name, status; + private OsuSpriteText name; private BeatmapTypeInfo beatmapTypeInfo; private ParticipantInfo participantInfo; [Resolved] private BeatmapManager beatmaps { get; set; } + private readonly Bindable status = new Bindable(new RoomStatusNoneSelected()); + [BackgroundDependencyLoader] private void load(OsuColour colours) { - this.colours = colours; - InternalChildren = new Drawable[] { new Box @@ -98,15 +97,17 @@ namespace osu.Game.Screens.Multi.Lounge.Components Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, TextSize = 30, + Current = Name }, }, }, }, }, - statusStrip = new Box + new StatusColouredContainer(transition_duration) { RelativeSizeAxes = Axes.X, Height = 5, + Child = new Box { RelativeSizeAxes = Axes.Both } }, new Container { @@ -129,10 +130,14 @@ namespace osu.Game.Screens.Multi.Lounge.Components Spacing = new Vector2(0f, 5f), Children = new Drawable[] { - status = new OsuSpriteText + new StatusColouredContainer(transition_duration) { - TextSize = 14, - Font = @"Exo2.0-Bold", + AutoSizeAxes = Axes.Both, + Child = new StatusText + { + TextSize = 14, + Font = @"Exo2.0-Bold", + } }, beatmapTypeInfo = new BeatmapTypeInfo(), }, @@ -163,38 +168,37 @@ namespace osu.Game.Screens.Multi.Lounge.Components } }; - Status.BindValueChanged(displayStatus, true); - Name.BindValueChanged(n => name.Text = n, true); - RoomID.BindValueChanged(updateRoom, true); + Status.BindValueChanged(_ => updateStatus(), true); + RoomID.BindValueChanged(_ => updateStatus(), true); } - private void updateRoom(int? roomId) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - if (roomId != null) + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + dependencies.CacheAs(status, new CacheInfo(nameof(Online.Multiplayer.Room.Status), typeof(Room))); + return dependencies; + } + + private void updateStatus() + { + if (RoomID.Value == null) { + status.Value = new RoomStatusNoneSelected(); + + participantCount.FadeOut(transition_duration); + beatmapTypeInfo.FadeOut(transition_duration); + name.FadeOut(transition_duration); + participantInfo.FadeOut(transition_duration); + } + else + { + status.Value = Status; + participantCount.FadeIn(transition_duration); beatmapTypeInfo.FadeIn(transition_duration); name.FadeIn(transition_duration); participantInfo.FadeIn(transition_duration); } - else - { - participantCount.FadeOut(transition_duration); - beatmapTypeInfo.FadeOut(transition_duration); - name.FadeOut(transition_duration); - participantInfo.FadeOut(transition_duration); - - displayStatus(new RoomStatusNoneSelected()); - } - } - - private void displayStatus(RoomStatus s) - { - status.Text = s.Message; - - Color4 c = s.GetAppropriateColour(colours); - statusStrip.FadeColour(c, transition_duration); - status.FadeColour(c, transition_duration); } private class RoomStatusNoneSelected : RoomStatus @@ -203,6 +207,18 @@ namespace osu.Game.Screens.Multi.Lounge.Components public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray8; } + private class StatusText : OsuSpriteText + { + [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.Status))] + private Bindable status { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + status.BindValueChanged(s => Text = s.Message, true); + } + } + private class MatchParticipants : MultiplayerComposite { private readonly FillFlowContainer fill; From 17fdfc15d92b43a83b19845240fa83d1f6b36a1f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Feb 2019 19:52:33 +0900 Subject: [PATCH 052/167] Fix room title in the breadcrumbs --- osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs | 2 +- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 50788a2fc2..7ba9382f65 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Multi.Lounge currentRoom.Value = room; - this.Push(new MatchSubScreen(s => pushGameplayScreen?.Invoke(s))); + this.Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s))); } } } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index adfd77c182..7b94de7d76 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Multi.Match { public override bool AllowBeatmapRulesetChange => false; - public override string Title => roomId?.Value == null ? "New room" : name.Value; + public override string Title { get; } public override string ShortTitle => "room"; @@ -36,8 +36,10 @@ namespace osu.Game.Screens.Multi.Match [Resolved(typeof(Room), nameof(Room.Playlist))] private BindableList playlist { get; set; } - public MatchSubScreen(Action pushGameplayScreen) + public MatchSubScreen(Room room, Action pushGameplayScreen) { + Title = room.RoomID.Value == null ? "New room" : room.Name; + InternalChild = new Match(pushGameplayScreen) { RelativeSizeAxes = Axes.Both, From 990bd44ca27868a76f690cd47b49ba3fe4e512bb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 13:01:10 +0900 Subject: [PATCH 053/167] Combine roommanager and roompollingcomponent --- osu.Game/Screens/Multi/Multiplayer.cs | 14 ++- osu.Game/Screens/Multi/RoomManager.cs | 85 ++++++++++++++----- .../Screens/Multi/RoomPollingComponent.cs | 66 -------------- 3 files changed, 68 insertions(+), 97 deletions(-) delete mode 100644 osu.Game/Screens/Multi/RoomPollingComponent.cs diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 6851bc2a2a..2a2927734b 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -48,7 +48,6 @@ namespace osu.Game.Screens.Multi private readonly OsuButton createButton; private readonly LoungeSubScreen loungeSubScreen; private readonly ScreenStack screenStack; - private readonly RoomPollingComponent pollingComponent; [Cached] private readonly Bindable filter = new Bindable(); @@ -104,7 +103,7 @@ namespace osu.Game.Screens.Multi }, }, }, - roomManager = new RoomManager + new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = Header.HEIGHT }, @@ -128,11 +127,10 @@ namespace osu.Game.Screens.Multi Name = { Value = $"{api.LocalUser}'s awesome room" } }), }, - pollingComponent = new RoomPollingComponent() + roomManager = new RoomManager() }); - pollingComponent.Filter.BindTo(filter); - pollingComponent.RoomsRetrieved += roomManager.UpdateRooms; + roomManager.Filter.BindTo(filter); screenStack.ScreenPushed += screenPushed; screenStack.ScreenExited += screenExited; @@ -166,8 +164,8 @@ namespace osu.Game.Screens.Multi private void updatePollingRate(bool idle) { - pollingComponent.TimeBetweenPolls = !this.IsCurrentScreen() || !(screenStack.CurrentScreen is LoungeSubScreen) ? 0 : (idle ? 120000 : 15000); - Logger.Log($"Polling adjusted to {pollingComponent.TimeBetweenPolls}"); + roomManager.TimeBetweenPolls = !this.IsCurrentScreen() || !(screenStack.CurrentScreen is LoungeSubScreen) ? 0 : (idle ? 120000 : 15000); + Logger.Log($"Polling adjusted to {roomManager.TimeBetweenPolls}"); } public void APIStateChanged(APIAccess api, APIState state) @@ -230,7 +228,7 @@ namespace osu.Game.Screens.Multi this.FadeOut(250); cancelLooping(); - pollingComponent.TimeBetweenPolls = 0; + roomManager.TimeBetweenPolls = 0; } private void cancelLooping() diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index ceee918586..554be0dfe2 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -2,27 +2,33 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Game.Beatmaps; +using osu.Game.Online; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets; +using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Screens.Multi { - public class RoomManager : Container, IRoomManager + public class RoomManager : PollingComponent, IRoomManager { public event Action RoomsUpdated; private readonly BindableList rooms = new BindableList(); public IBindableList Rooms => rooms; + /// + /// The to use when polling for s. + /// + public readonly Bindable Filter = new Bindable(); + public Bindable CurrentRoom { get; } = new Bindable(); private Room joinedRoom; @@ -36,6 +42,16 @@ namespace osu.Game.Screens.Multi [Resolved] private BeatmapManager beatmaps { get; set; } + public RoomManager() + { + Filter.BindValueChanged(_ => + { + if (IsLoaded) + PollImmediately(); + }); + } + + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -46,17 +62,20 @@ namespace osu.Game.Screens.Multi { room.Host.Value = api.LocalUser; + addRoom(room); + joinRoom(room); + + RoomsUpdated?.Invoke(); + + onSuccess?.Invoke(room); + + return; + var req = new CreateRoomRequest(room); req.Success += result => { - update(room, result); - addRoom(room); - joinRoom(room); - RoomsUpdated?.Invoke(); - - onSuccess?.Invoke(room); }; req.Failure += exception => @@ -108,25 +127,45 @@ namespace osu.Game.Screens.Multi joinedRoom = null; } - public void UpdateRooms(List newRooms) + private GetRoomsRequest pollReq; + + protected override Task Poll() { - // Remove past matches - foreach (var r in rooms.ToList()) + if (!api.IsLoggedIn) + return base.Poll(); + + var tcs = new TaskCompletionSource(); + + pollReq?.Cancel(); + pollReq = new GetRoomsRequest(Filter.Value.PrimaryFilter); + + pollReq.Success += result => { - if (newRooms.All(e => e.RoomID.Value != r.RoomID.Value)) - rooms.Remove(r); - } + // Remove past matches + foreach (var r in rooms.ToList()) + { + if (result.All(e => e.RoomID.Value != r.RoomID.Value)) + rooms.Remove(r); + } - for (int i = 0; i < newRooms.Count; i++) - { - var r = newRooms[i]; - r.Position.Value = i; + for (int i = 0; i < result.Count; i++) + { + var r = result[i]; + r.Position.Value = i; - update(r, r); - addRoom(r); - } + update(r, r); + addRoom(r); + } - RoomsUpdated?.Invoke(); + RoomsUpdated?.Invoke(); + tcs.SetResult(true); + }; + + pollReq.Failure += _ => tcs.SetResult(false); + + api.Queue(pollReq); + + return tcs.Task; } /// diff --git a/osu.Game/Screens/Multi/RoomPollingComponent.cs b/osu.Game/Screens/Multi/RoomPollingComponent.cs deleted file mode 100644 index 53c90d4d61..0000000000 --- a/osu.Game/Screens/Multi/RoomPollingComponent.cs +++ /dev/null @@ -1,66 +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 System; -using System.Collections.Generic; -using System.Threading.Tasks; -using osu.Framework.Allocation; -using osu.Framework.Configuration; -using osu.Game.Online; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Online.Multiplayer; -using osu.Game.Screens.Multi.Lounge.Components; - -namespace osu.Game.Screens.Multi -{ - public class RoomPollingComponent : PollingComponent - { - /// - /// Invoked when s have been retrieved from the API. - /// - public Action> RoomsRetrieved; - - /// - /// The to use when polling for s. - /// - public readonly Bindable Filter = new Bindable(); - - [Resolved] - private APIAccess api { get; set; } - - public RoomPollingComponent() - { - Filter.BindValueChanged(_ => - { - if (IsLoaded) - PollImmediately(); - }); - } - - private GetRoomsRequest pollReq; - - protected override Task Poll() - { - if (!api.IsLoggedIn) - return base.Poll(); - - var tcs = new TaskCompletionSource(); - - pollReq?.Cancel(); - pollReq = new GetRoomsRequest(Filter.Value.PrimaryFilter); - - pollReq.Success += result => - { - RoomsRetrieved?.Invoke(result); - tcs.SetResult(true); - }; - - pollReq.Failure += _ => tcs.SetResult(false); - - api.Queue(pollReq); - - return tcs.Task; - } - } -} From 7f13e3c5f788226c61c7aa98fbd0967f041ef2d5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 13:02:17 +0900 Subject: [PATCH 054/167] Directly resolve the filter bindable --- osu.Game/Screens/Multi/Multiplayer.cs | 2 -- osu.Game/Screens/Multi/RoomManager.cs | 13 +++++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 2a2927734b..032f40aa18 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -130,8 +130,6 @@ namespace osu.Game.Screens.Multi roomManager = new RoomManager() }); - roomManager.Filter.BindTo(filter); - screenStack.ScreenPushed += screenPushed; screenStack.ScreenExited += screenExited; } diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index 554be0dfe2..26bb3a8321 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -24,15 +24,13 @@ namespace osu.Game.Screens.Multi private readonly BindableList rooms = new BindableList(); public IBindableList Rooms => rooms; - /// - /// The to use when polling for s. - /// - public readonly Bindable Filter = new Bindable(); - public Bindable CurrentRoom { get; } = new Bindable(); private Room joinedRoom; + [Resolved] + private Bindable filter { get; set; } + [Resolved] private APIAccess api { get; set; } @@ -44,14 +42,13 @@ namespace osu.Game.Screens.Multi public RoomManager() { - Filter.BindValueChanged(_ => + filter.BindValueChanged(_ => { if (IsLoaded) PollImmediately(); }); } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -137,7 +134,7 @@ namespace osu.Game.Screens.Multi var tcs = new TaskCompletionSource(); pollReq?.Cancel(); - pollReq = new GetRoomsRequest(Filter.Value.PrimaryFilter); + pollReq = new GetRoomsRequest(filter.Value.PrimaryFilter); pollReq.Success += result => { From 42cd55e0d7cdd2167384ab2ae975d386addf96dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 14:57:51 +0900 Subject: [PATCH 055/167] Move current room to multiplayer --- osu.Game/Screens/Multi/IRoomManager.cs | 5 ----- .../Multi/Lounge/Components/RoomsContainer.cs | 6 +++--- osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs | 12 ------------ osu.Game/Screens/Multi/Multiplayer.cs | 7 +++++-- osu.Game/Screens/Multi/RoomManager.cs | 16 +++++++++------- 5 files changed, 17 insertions(+), 29 deletions(-) diff --git a/osu.Game/Screens/Multi/IRoomManager.cs b/osu.Game/Screens/Multi/IRoomManager.cs index a00e08a80a..980879d79a 100644 --- a/osu.Game/Screens/Multi/IRoomManager.cs +++ b/osu.Game/Screens/Multi/IRoomManager.cs @@ -19,11 +19,6 @@ namespace osu.Game.Screens.Multi /// IBindableList Rooms { get; } - /// - /// The currently-active . - /// - Bindable CurrentRoom { get; } - /// /// Creates a new . /// diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs index 12d9b2957c..baeac900ee 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomsContainer.cs @@ -19,13 +19,14 @@ namespace osu.Game.Screens.Multi.Lounge.Components { public Action JoinRequested; - private readonly Bindable currentRoom = new Bindable(); - private readonly IBindableList rooms = new BindableList(); private readonly FillFlowContainer roomFlow; public IReadOnlyList Rooms => roomFlow; + [Resolved] + private Bindable currentRoom { get; set; } + [Resolved] private IRoomManager roomManager { get; set; } @@ -46,7 +47,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components [BackgroundDependencyLoader] private void load() { - currentRoom.BindTo(roomManager.CurrentRoom); rooms.BindTo(roomManager.Rooms); rooms.ItemsAdded += addRooms; diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 7ba9382f65..1fb0675f9e 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -26,8 +24,6 @@ namespace osu.Game.Screens.Multi.Lounge private readonly Action pushGameplayScreen; private readonly ProcessingOverlay processingOverlay; - private readonly Bindable currentRoom = new Bindable(); - public LoungeSubScreen(Action pushGameplayScreen) { this.pushGameplayScreen = pushGameplayScreen; @@ -75,12 +71,6 @@ namespace osu.Game.Screens.Multi.Lounge Filter.Search.Exit += this.Exit; } - [BackgroundDependencyLoader] - private void load(IRoomManager roomManager) - { - currentRoom.BindTo(roomManager.CurrentRoom); - } - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -135,8 +125,6 @@ namespace osu.Game.Screens.Multi.Lounge if (!this.IsCurrentScreen()) return; - currentRoom.Value = room; - this.Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s))); } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 032f40aa18..ca4623a334 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -50,7 +50,10 @@ namespace osu.Game.Screens.Multi private readonly ScreenStack screenStack; [Cached] - private readonly Bindable filter = new Bindable(); + private readonly Bindable currentRoom = new Bindable(); + + [Cached] + private readonly Bindable currentFilter = new Bindable(); [Cached(Type = typeof(IRoomManager))] private RoomManager roomManager; @@ -150,7 +153,7 @@ namespace osu.Game.Screens.Multi protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); - dependencies.Model.BindTo(roomManager.CurrentRoom); + dependencies.Model.BindTo(currentRoom); return dependencies; } diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index 26bb3a8321..f517ded6dc 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -24,12 +24,13 @@ namespace osu.Game.Screens.Multi private readonly BindableList rooms = new BindableList(); public IBindableList Rooms => rooms; - public Bindable CurrentRoom { get; } = new Bindable(); - private Room joinedRoom; [Resolved] - private Bindable filter { get; set; } + private Bindable currentRoom { get; set; } + + [Resolved] + private Bindable currentFilter { get; set; } [Resolved] private APIAccess api { get; set; } @@ -40,9 +41,10 @@ namespace osu.Game.Screens.Multi [Resolved] private BeatmapManager beatmaps { get; set; } - public RoomManager() + [BackgroundDependencyLoader] + private void load() { - filter.BindValueChanged(_ => + currentFilter.BindValueChanged(_ => { if (IsLoaded) PollImmediately(); @@ -111,7 +113,7 @@ namespace osu.Game.Screens.Multi private void joinRoom(Room room) { - CurrentRoom.Value = room; + currentRoom.Value = room; joinedRoom = room; } @@ -134,7 +136,7 @@ namespace osu.Game.Screens.Multi var tcs = new TaskCompletionSource(); pollReq?.Cancel(); - pollReq = new GetRoomsRequest(filter.Value.PrimaryFilter); + pollReq = new GetRoomsRequest(currentFilter.Value.PrimaryFilter); pollReq.Success += result => { From 43240ea85ed6923444c5135967619f28d4ce03c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 15:20:11 +0900 Subject: [PATCH 056/167] Fix playlist issues with room creation --- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 7 ++++++ .../Match/Components/MatchSettingsOverlay.cs | 6 ++++- .../Screens/Multi/MultiplayerComposite.cs | 3 --- osu.Game/Screens/Multi/RoomManager.cs | 23 ++++++------------- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 1fb0675f9e..1229d071ef 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -24,6 +26,9 @@ namespace osu.Game.Screens.Multi.Lounge private readonly Action pushGameplayScreen; private readonly ProcessingOverlay processingOverlay; + [Resolved] + private Bindable currentRoom { get; set; } + public LoungeSubScreen(Action pushGameplayScreen) { this.pushGameplayScreen = pushGameplayScreen; @@ -125,6 +130,8 @@ namespace osu.Game.Screens.Multi.Lounge if (!this.IsCurrentScreen()) return; + currentRoom.Value = room; + this.Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s))); } } diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index 75cabe8b35..e962784660 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -4,6 +4,7 @@ using System; using Humanizer; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -65,6 +66,9 @@ namespace osu.Game.Screens.Multi.Match.Components [Resolved(CanBeNull = true)] private IRoomManager manager { get; set; } + [Resolved] + private Bindable currentRoom { get; set; } + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -293,7 +297,7 @@ namespace osu.Game.Screens.Multi.Match.Components Duration.Value = DurationField.Current.Value; - manager?.CreateRoom(Room, onSuccess, onError); + manager?.CreateRoom(currentRoom, onSuccess, onError); processingOverlay.Show(); } diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs index 1e68727438..1a16db97a4 100644 --- a/osu.Game/Screens/Multi/MultiplayerComposite.cs +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -17,9 +17,6 @@ namespace osu.Game.Screens.Multi { public class MultiplayerComposite : CompositeDrawable { - [Resolved] - protected Room Room { get; private set; } - [Resolved(typeof(Room))] protected Bindable RoomID { get; private set; } diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index f517ded6dc..c74787a0ed 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -61,20 +61,17 @@ namespace osu.Game.Screens.Multi { room.Host.Value = api.LocalUser; - addRoom(room); - joinRoom(room); - - RoomsUpdated?.Invoke(); - - onSuccess?.Invoke(room); - - return; - var req = new CreateRoomRequest(room); req.Success += result => { + joinedRoom = room; + update(room, result); + addRoom(room); + + RoomsUpdated?.Invoke(); + onSuccess?.Invoke(room); }; req.Failure += exception => @@ -98,7 +95,7 @@ namespace osu.Game.Screens.Multi currentJoinRoomRequest = new JoinRoomRequest(room, api.LocalUser.Value); currentJoinRoomRequest.Success += () => { - joinRoom(room); + joinedRoom = room; onSuccess?.Invoke(room); }; @@ -111,12 +108,6 @@ namespace osu.Game.Screens.Multi api.Queue(currentJoinRoomRequest); } - private void joinRoom(Room room) - { - currentRoom.Value = room; - joinedRoom = room; - } - public void PartRoom() { if (joinedRoom == null) From 99a7ccc01a37c5c13fc86ebf4937f64e37f1d212 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 15:38:05 +0900 Subject: [PATCH 057/167] Slight cleanup in multiplayer --- osu.Game/Screens/Multi/Multiplayer.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index ca4623a334..19cb550e7b 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -49,6 +49,8 @@ namespace osu.Game.Screens.Multi private readonly LoungeSubScreen loungeSubScreen; private readonly ScreenStack screenStack; + private readonly IBindable isIdle = new BindableBool(); + [Cached] private readonly Bindable currentRoom = new Bindable(); @@ -137,8 +139,6 @@ namespace osu.Game.Screens.Multi screenStack.ScreenExited += screenExited; } - private readonly IBindable isIdle = new BindableBool(); - [BackgroundDependencyLoader(true)] private void load(IdleTracker idleTracker) { @@ -148,6 +148,12 @@ namespace osu.Game.Screens.Multi isIdle.BindTo(idleTracker.IsIdle); } + protected override void LoadComplete() + { + base.LoadComplete(); + isIdle.BindValueChanged(updatePollingRate, true); + } + private CachedModelDependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) @@ -157,12 +163,6 @@ namespace osu.Game.Screens.Multi return dependencies; } - protected override void LoadComplete() - { - base.LoadComplete(); - isIdle.BindValueChanged(updatePollingRate, true); - } - private void updatePollingRate(bool idle) { roomManager.TimeBetweenPolls = !this.IsCurrentScreen() || !(screenStack.CurrentScreen is LoungeSubScreen) ? 0 : (idle ? 120000 : 15000); From 6fe1f572367dece51712199783e8d1ce19174e7b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 15:39:11 +0900 Subject: [PATCH 058/167] Fix crash when start button is repeatedly pressed --- osu.Game/Screens/Multi/Multiplayer.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 19cb550e7b..360dd1fa07 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -83,7 +83,7 @@ namespace osu.Game.Screens.Multi RelativeSizeAxes = Axes.Both, }; - screenStack = new ScreenStack(loungeSubScreen = new LoungeSubScreen(this.Push)) { RelativeSizeAxes = Axes.Both }; + screenStack = new ScreenStack(loungeSubScreen = new LoungeSubScreen(pushGameplayScreen)) { RelativeSizeAxes = Axes.Both }; Padding = new MarginPadding { Horizontal = -OsuScreen.HORIZONTAL_OVERFLOW_PADDING }; waves.AddRange(new Drawable[] @@ -169,6 +169,14 @@ namespace osu.Game.Screens.Multi Logger.Log($"Polling adjusted to {roomManager.TimeBetweenPolls}"); } + private void pushGameplayScreen(IScreen gameplayScreen) + { + if (!this.IsCurrentScreen()) + return; + + this.Push(gameplayScreen); + } + public void APIStateChanged(APIAccess api, APIState state) { if (state != APIState.Online) From 5643dc2e99904d0c1ed46c58ae98415f316dc2f8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 15:44:05 +0900 Subject: [PATCH 059/167] Remove redundant qualifiers --- osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs | 4 ++-- osu.Game/Screens/Multi/Match/Components/Header.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs index a25d49a514..3e665ab27e 100644 --- a/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs +++ b/osu.Game/Screens/Multi/Lounge/Components/RoomInspector.cs @@ -175,7 +175,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - dependencies.CacheAs(status, new CacheInfo(nameof(Online.Multiplayer.Room.Status), typeof(Room))); + dependencies.CacheAs(status, new CacheInfo(nameof(Room.Status), typeof(Room))); return dependencies; } @@ -209,7 +209,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components private class StatusText : OsuSpriteText { - [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.Status))] + [Resolved(typeof(Room), nameof(Room.Status))] private Bindable status { get; set; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 55c7c02be4..137c0aa939 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Multi.Match.Components private class BeatmapSelectButton : HeaderButton { - [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.RoomID))] + [Resolved(typeof(Room), nameof(Room.RoomID))] private Bindable roomId { get; set; } public BeatmapSelectButton() From b5cc0ec8dba1962c2429370270958f9affe534c2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 15:44:11 +0900 Subject: [PATCH 060/167] Update framework --- 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 596190fcf7..4b735f3101 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + From febcd7d7c0ac18e3a6833c87245269cb83e285fa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 15:46:16 +0900 Subject: [PATCH 061/167] Remove currentRoom from RoomManager --- osu.Game/Screens/Multi/RoomManager.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Multi/RoomManager.cs b/osu.Game/Screens/Multi/RoomManager.cs index c74787a0ed..b1f021618f 100644 --- a/osu.Game/Screens/Multi/RoomManager.cs +++ b/osu.Game/Screens/Multi/RoomManager.cs @@ -26,9 +26,6 @@ namespace osu.Game.Screens.Multi private Room joinedRoom; - [Resolved] - private Bindable currentRoom { get; set; } - [Resolved] private Bindable currentFilter { get; set; } From ee5ff283d1fb56baf630d2ef426eb645a837a09e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 16:02:00 +0900 Subject: [PATCH 062/167] Remove compiler warnings --- osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs | 6 +++++- osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs index 2b362743e8..6fa15a5917 100644 --- a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs @@ -71,7 +71,11 @@ namespace osu.Game.Tests.Visual private class TestRoomManager : IRoomManager { - public event Action RoomsUpdated; + public event Action RoomsUpdated + { + add => throw new NotImplementedException(); + remove => throw new NotImplementedException(); + } public readonly BindableList Rooms = new BindableList(); IBindableList IRoomManager.Rooms => Rooms; diff --git a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs index 54b2e8a8fc..bb29770c56 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs @@ -131,7 +131,11 @@ namespace osu.Game.Tests.Visual public Func CreateRequested; - public event Action RoomsUpdated; + public event Action RoomsUpdated + { + add => throw new NotImplementedException(); + remove => throw new NotImplementedException(); + } public IBindableList Rooms { get; } = null; From 7aca0f4a1d31cef5f119047d115545c2d7bcb8af Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 8 Feb 2019 16:02:14 +0900 Subject: [PATCH 063/167] Fix failing test cases --- .../Visual/TestCaseLoungeRoomsContainer.cs | 14 ++++---------- .../Visual/TestCaseMatchSettingsOverlay.cs | 9 ++------- osu.Game/Tests/Visual/MultiplayerTestCase.cs | 18 ++++++------------ 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs index 6fa15a5917..c87b91003b 100644 --- a/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseLoungeRoomsContainer.cs @@ -16,7 +16,7 @@ using osuTK.Graphics; namespace osu.Game.Tests.Visual { - public class TestCaseLoungeRoomsContainer : OsuTestCase + public class TestCaseLoungeRoomsContainer : MultiplayerTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0)); AddStep("select first room", () => container.Rooms.First().Action?.Invoke()); - AddAssert("first room selected", () => roomManager.CurrentRoom.Value == roomManager.Rooms.First()); + AddAssert("first room selected", () => Room == roomManager.Rooms.First()); AddStep("join first room", () => container.Rooms.First().Action?.Invoke()); AddAssert("first room joined", () => roomManager.Rooms.First().Status.Value is JoinedRoomStatus); @@ -73,15 +73,13 @@ namespace osu.Game.Tests.Visual { public event Action RoomsUpdated { - add => throw new NotImplementedException(); - remove => throw new NotImplementedException(); + add { } + remove { } } public readonly BindableList Rooms = new BindableList(); IBindableList IRoomManager.Rooms => Rooms; - public Bindable CurrentRoom { get; } = new Bindable(); - public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) => Rooms.Add(room); public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) @@ -91,10 +89,6 @@ namespace osu.Game.Tests.Visual public void PartRoom() { } - - public void Filter(FilterCriteria criteria) - { - } } private class JoinedRoomStatus : RoomStatus diff --git a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs index bb29770c56..317707a77e 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchSettingsOverlay.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; using osu.Game.Screens.Multi; -using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match.Components; namespace osu.Game.Tests.Visual @@ -133,14 +132,12 @@ namespace osu.Game.Tests.Visual public event Action RoomsUpdated { - add => throw new NotImplementedException(); - remove => throw new NotImplementedException(); + add { } + remove { } } public IBindableList Rooms { get; } = null; - public Bindable CurrentRoom { get; } = new Bindable(); - public void CreateRoom(Room room, Action onSuccess = null, Action onError = null) { if (CreateRequested == null) @@ -155,8 +152,6 @@ namespace osu.Game.Tests.Visual public void JoinRoom(Room room, Action onSuccess = null, Action onError = null) => throw new NotImplementedException(); public void PartRoom() => throw new NotImplementedException(); - - public void Filter(FilterCriteria criteria) => throw new NotImplementedException(); } } } diff --git a/osu.Game/Tests/Visual/MultiplayerTestCase.cs b/osu.Game/Tests/Visual/MultiplayerTestCase.cs index 5ed4f012c6..6efdddbfee 100644 --- a/osu.Game/Tests/Visual/MultiplayerTestCase.cs +++ b/osu.Game/Tests/Visual/MultiplayerTestCase.cs @@ -2,26 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Game.Online.Multiplayer; namespace osu.Game.Tests.Visual { public class MultiplayerTestCase : OsuTestCase { - private Room room; + [Cached] + private readonly Bindable currentRoom = new Bindable(new Room()); protected Room Room { - get => room; - set - { - if (room == value) - return; - room = value; - - if (dependencies != null) - dependencies.Model.Value = value; - } + get => currentRoom; + set => currentRoom.Value = value; } private CachedModelDependencyContainer dependencies; @@ -29,7 +23,7 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); - dependencies.Model.Value = room; + dependencies.Model.BindTo(currentRoom); return dependencies; } } From 959b1ac845e8790f111004c00534c73df5c467ac Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Fri, 8 Feb 2019 17:28:00 +0900 Subject: [PATCH 064/167] Fix potential nullref when exiting multiplayer (#4227) Fixes https://github.com/ppy/osu/issues/3954 --- osu.Game/Screens/Multi/Multiplayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 1741ac0b7b..50f190479b 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -218,7 +218,7 @@ namespace osu.Game.Screens.Multi private void cancelLooping() { - var track = beatmap.Value.Track; + var track = beatmap?.Value?.Track; if (track != null) track.Looping = false; } From e8aed6b68641b2d033267d0d0384ec2f17691229 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Feb 2019 17:38:18 +0900 Subject: [PATCH 065/167] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4b735f3101..e87b43ac93 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index e00c4fcf78..0d8a7e3a34 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,12 +105,12 @@ - - + + - + \ No newline at end of file From 7ec0b4ba773180ad8d9c73004dbf1d766df6d38e Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Fri, 8 Feb 2019 17:53:01 +0900 Subject: [PATCH 066/167] Fix nullref during room creation (#4228) --- osu.Game/Screens/Multi/Match/Components/ReadyButton.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index 1bde6270f6..102e75cfb7 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -55,6 +55,9 @@ namespace osu.Game.Screens.Multi.Match.Components private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) { + if (Beatmap.Value == null) + return; + if (model.Beatmaps.Any(b => b.OnlineBeatmapID == Beatmap.Value.OnlineBeatmapID)) Schedule(() => hasBeatmap = true); } From 6c4d289901685648287ca1cdd5d06880703cac21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Feb 2019 18:33:49 +0900 Subject: [PATCH 067/167] Pass item rather than ID --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 2 +- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 7b94de7d76..9685962f9e 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -235,7 +235,7 @@ namespace osu.Game.Screens.Multi.Match { default: case GameTypeTimeshift _: - pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(Playlist.First().ID) + pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(Playlist.First()) { Exited = () => leaderboard.RefreshScores() })); diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 6cddd235e1..71f6687cc6 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -25,14 +25,14 @@ namespace osu.Game.Screens.Multi.Play [Resolved(typeof(Room), nameof(Room.RoomID))] private Bindable roomId { get; set; } - private readonly int playlistItemId; + private readonly PlaylistItem playlistItem; [Resolved] private APIAccess api { get; set; } - public TimeshiftPlayer(int playlistItemId) + public TimeshiftPlayer(PlaylistItem playlistItem) { - this.playlistItemId = playlistItemId; + this.playlistItem = playlistItem; } private int? token; @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Multi.Play bool failed = false; - var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItemId); + var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItem.ID); req.Success += r => token = r.ID; req.Failure += e => { @@ -89,7 +89,7 @@ namespace osu.Game.Screens.Multi.Play Debug.Assert(token != null); - var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItemId, score); + var request = new SubmitRoomScoreRequest(token.Value, roomId.Value ?? 0, playlistItem.ID, score); request.Failure += e => Logger.Error(e, "Failed to submit score"); api.Queue(request); } From f52cac966b7989265b8bfd7c1c68093eab3f7200 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Feb 2019 18:45:32 +0900 Subject: [PATCH 068/167] Fix multiple blank lines --- osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs index e962784660..c3169ebe94 100644 --- a/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/Multi/Match/Components/MatchSettingsOverlay.cs @@ -281,7 +281,6 @@ namespace osu.Game.Screens.Multi.Match.Components private bool hasValidSettings => RoomID.Value == null && NameField.Text.Length > 0 && Playlist.Count > 0; - private void apply() { hideError(); From 88ffc78103ed606b51015024c1a58408535d28a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Feb 2019 19:11:34 +0900 Subject: [PATCH 069/167] Restructure --- osu.Game.Tests/Visual/TestCaseMatchResults.cs | 2 +- .../UpdateableBeatmapBackgroundSprite.cs | 2 +- osu.Game/Online/Multiplayer/Room.cs | 16 + .../Screens/Multi/Components/BeatmapTitle.cs | 12 +- .../Multi/Components/BeatmapTypeInfo.cs | 8 +- .../Screens/Multi/Components/ModeTypeInfo.cs | 11 +- .../Components/MultiplayerBackgroundSprite.cs | 2 +- .../Screens/Multi/Lounge/LoungeSubScreen.cs | 14 +- .../Screens/Multi/Match/Components/Header.cs | 2 +- .../Screens/Multi/Match/Components/Info.cs | 8 +- .../Multi/Match/Components/ReadyButton.cs | 2 +- .../Match/Components/ViewBeatmapButton.cs | 2 +- .../Screens/Multi/Match/MatchSubScreen.cs | 353 +++++++++--------- osu.Game/Screens/Multi/Multiplayer.cs | 49 +-- .../Screens/Multi/MultiplayerComposite.cs | 37 +- .../Screens/Multi/MultiplayerSubScreen.cs | 46 +-- 16 files changed, 243 insertions(+), 323 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseMatchResults.cs b/osu.Game.Tests/Visual/TestCaseMatchResults.cs index c34ad0fcbc..33469e74b7 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchResults.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchResults.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual { } - protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value, room) }; + protected override IEnumerable CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value) }; } private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index c66052052f..d1d30a7c29 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -13,7 +13,7 @@ namespace osu.Game.Beatmaps.Drawables /// public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable { - public readonly IBindable Beatmap = new Bindable(); + public readonly Bindable Beatmap = new Bindable(); [Resolved] private BeatmapManager beatmaps { get; set; } diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs index 0d5b168dcb..2dcc7369f9 100644 --- a/osu.Game/Online/Multiplayer/Room.cs +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -31,6 +31,10 @@ namespace osu.Game.Online.Multiplayer [JsonProperty("playlist")] public BindableList Playlist { get; private set; } = new BindableList(); + [Cached] + [JsonIgnore] + public Bindable CurrentItem { get; private set; } = new Bindable(); + [Cached] [JsonProperty("channel_id")] public Bindable ChannelId { get; private set; } = new Bindable(); @@ -66,6 +70,18 @@ namespace osu.Game.Online.Multiplayer [Cached] public Bindable ParticipantCount { get; private set; } = new Bindable(); + public Room() + { + Playlist.ItemsAdded += updateCurrent; + Playlist.ItemsRemoved += updateCurrent; + updateCurrent(Playlist); + } + + private void updateCurrent(IEnumerable playlist) + { + CurrentItem.Value = playlist.FirstOrDefault(); + } + // todo: TEMPORARY [JsonProperty("participant_count")] private int? participantCount diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs index dca0545035..ff1a1fb3a4 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Multi.Components [BackgroundDependencyLoader] private void load() { - CurrentBeatmap.BindValueChanged(v => updateText(), true); + CurrentItem.BindValueChanged(v => updateText(), true); } private float textSize = OsuSpriteText.FONT_SIZE; @@ -53,7 +53,9 @@ namespace osu.Game.Screens.Multi.Components textFlow.Clear(); - if (CurrentBeatmap.Value == null) + var beatmap = CurrentItem.Value?.Beatmap; + + if (beatmap == null) textFlow.AddText("No beatmap selected", s => { s.TextSize = TextSize; @@ -65,7 +67,7 @@ namespace osu.Game.Screens.Multi.Components { new OsuSpriteText { - Text = new LocalisedString((CurrentBeatmap.Value.Metadata.ArtistUnicode, CurrentBeatmap.Value.Metadata.Artist)), + Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), TextSize = TextSize, }, new OsuSpriteText @@ -75,10 +77,10 @@ namespace osu.Game.Screens.Multi.Components }, new OsuSpriteText { - Text = new LocalisedString((CurrentBeatmap.Value.Metadata.TitleUnicode, CurrentBeatmap.Value.Metadata.Title)), + Text = new LocalisedString((beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title)), TextSize = TextSize, } - }, null, LinkAction.OpenBeatmap, CurrentBeatmap.Value.OnlineBeatmapID.ToString(), "Open beatmap"); + }, null, LinkAction.OpenBeatmap, beatmap.OnlineBeatmapID.ToString(), "Open beatmap"); } } } diff --git a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs index 3904df2069..4f432a232c 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTypeInfo.cs @@ -51,14 +51,16 @@ namespace osu.Game.Screens.Multi.Components } }; - CurrentBeatmap.BindValueChanged(v => + CurrentItem.BindValueChanged(item => { beatmapAuthor.Clear(); - if (v != null) + var beatmap = item?.Beatmap; + + if (beatmap != null) { beatmapAuthor.AddText("mapped by ", s => s.Colour = OsuColour.Gray(0.8f)); - beatmapAuthor.AddLink(v.Metadata.Author.Username, null, LinkAction.OpenUserProfile, v.Metadata.Author.Id.ToString(), "View Profile"); + beatmapAuthor.AddLink(beatmap.Metadata.Author.Username, null, LinkAction.OpenUserProfile, beatmap.Metadata.Author.Id.ToString(), "View Profile"); } }, true); } diff --git a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs index 97ea1b5f36..0d49f75b46 100644 --- a/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs +++ b/osu.Game/Screens/Multi/Components/ModeTypeInfo.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.Drawables; +using osu.Game.Online.Multiplayer; using osuTK; namespace osu.Game.Screens.Multi.Components @@ -45,17 +46,17 @@ namespace osu.Game.Screens.Multi.Components }, }; - CurrentBeatmap.BindValueChanged(_ => updateBeatmap()); - CurrentRuleset.BindValueChanged(_ => updateBeatmap(), true); + CurrentItem.BindValueChanged(updateBeatmap, true); + Type.BindValueChanged(v => gameTypeContainer.Child = new DrawableGameType(v) { Size = new Vector2(height) }, true); } - private void updateBeatmap() + private void updateBeatmap(PlaylistItem item) { - if (CurrentBeatmap.Value != null) + if (item?.Beatmap != null) { rulesetContainer.FadeIn(transition_duration); - rulesetContainer.Child = new DifficultyIcon(CurrentBeatmap.Value, CurrentRuleset.Value) { Size = new Vector2(height) }; + rulesetContainer.Child = new DifficultyIcon(item.Beatmap, item.Ruleset) { Size = new Vector2(height) }; } else rulesetContainer.FadeOut(transition_duration); diff --git a/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs b/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs index 8eff7b14af..06d5e585ab 100644 --- a/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs +++ b/osu.Game/Screens/Multi/Components/MultiplayerBackgroundSprite.cs @@ -16,7 +16,7 @@ namespace osu.Game.Screens.Multi.Components InternalChild = sprite = CreateBackgroundSprite(); - sprite.Beatmap.BindTo(CurrentBeatmap); + CurrentItem.BindValueChanged(i => sprite.Beatmap.Value = i?.Beatmap, true); } protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }; diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index 1229d071ef..71205dc199 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.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.Configuration; using osu.Framework.Graphics; @@ -23,16 +22,13 @@ namespace osu.Game.Screens.Multi.Lounge protected readonly FilterControl Filter; private readonly Container content; - private readonly Action pushGameplayScreen; private readonly ProcessingOverlay processingOverlay; [Resolved] private Bindable currentRoom { get; set; } - public LoungeSubScreen(Action pushGameplayScreen) + public LoungeSubScreen() { - this.pushGameplayScreen = pushGameplayScreen; - InternalChildren = new Drawable[] { Filter = new FilterControl { Depth = -1 }, @@ -83,8 +79,8 @@ namespace osu.Game.Screens.Multi.Lounge content.Padding = new MarginPadding { Top = Filter.DrawHeight, - Left = SearchableListOverlay.WIDTH_PADDING - DrawableRoom.SELECTION_BORDER_WIDTH + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Right = SearchableListOverlay.WIDTH_PADDING + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Left = SearchableListOverlay.WIDTH_PADDING - DrawableRoom.SELECTION_BORDER_WIDTH + HORIZONTAL_OVERFLOW_PADDING, + Right = SearchableListOverlay.WIDTH_PADDING + HORIZONTAL_OVERFLOW_PADDING, }; } @@ -114,7 +110,7 @@ namespace osu.Game.Screens.Multi.Lounge private void joinRequested(Room room) { processingOverlay.Show(); - Manager?.JoinRoom(room, r => + RoomManager?.JoinRoom(room, r => { Open(room); processingOverlay.Hide(); @@ -132,7 +128,7 @@ namespace osu.Game.Screens.Multi.Lounge currentRoom.Value = room; - this.Push(new MatchSubScreen(room, s => pushGameplayScreen?.Invoke(s))); + this.Push(new MatchSubScreen(room)); } } } diff --git a/osu.Game/Screens/Multi/Match/Components/Header.cs b/osu.Game/Screens/Multi/Match/Components/Header.cs index 137c0aa939..9a0fdbd4e7 100644 --- a/osu.Game/Screens/Multi/Match/Components/Header.cs +++ b/osu.Game/Screens/Multi/Match/Components/Header.cs @@ -108,7 +108,7 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - CurrentMods.BindValueChanged(m => modDisplay.Current.Value = m, true); + CurrentItem.BindValueChanged(i => modDisplay.Current.Value = i?.RequiredMods, true); beatmapButton.Action = () => RequestBeatmapSelection?.Invoke(); } diff --git a/osu.Game/Screens/Multi/Match/Components/Info.cs b/osu.Game/Screens/Multi/Match/Components/Info.cs index ec6dbb6d12..b27c5b0ab4 100644 --- a/osu.Game/Screens/Multi/Match/Components/Info.cs +++ b/osu.Game/Screens/Multi/Match/Components/Info.cs @@ -92,8 +92,12 @@ namespace osu.Game.Screens.Multi.Match.Components }, }; - viewBeatmapButton.Beatmap.BindTo(CurrentBeatmap); - readyButton.Beatmap.BindTo(CurrentBeatmap); + CurrentItem.BindValueChanged(item => + { + viewBeatmapButton.Beatmap.Value = item?.Beatmap; + readyButton.Beatmap.Value = item?.Beatmap; + }, true); + hostInfo.Host.BindTo(Host); } } diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index 0f9c51af6a..50cf2addeb 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Multi.Match.Components { public class ReadyButton : HeaderButton { - public readonly IBindable Beatmap = new Bindable(); + public readonly Bindable Beatmap = new Bindable(); [Resolved(typeof(Room), nameof(Room.EndDate))] private Bindable endDate { get; set; } diff --git a/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs b/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs index 9970894ffc..e26a6b7e20 100644 --- a/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ViewBeatmapButton.cs @@ -11,7 +11,7 @@ namespace osu.Game.Screens.Multi.Match.Components { public class ViewBeatmapButton : HeaderButton { - public readonly IBindable Beatmap = new Bindable(); + public readonly Bindable Beatmap = new Bindable(); [Resolved(CanBeNull = true)] private OsuGame osuGame { get; set; } diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index d252ddf0b9..41c498538f 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.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; @@ -11,11 +12,12 @@ using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.GameTypes; -using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Play; using osu.Game.Screens.Play; using osu.Game.Screens.Select; +using PlaylistItem = osu.Game.Online.Multiplayer.PlaylistItem; namespace osu.Game.Screens.Multi.Match { @@ -33,223 +35,208 @@ namespace osu.Game.Screens.Multi.Match [Resolved(typeof(Room), nameof(Room.Name))] private Bindable name { get; set; } - [Resolved(typeof(Room), nameof(Room.Playlist))] - private BindableList playlist { get; set; } + [Resolved(typeof(Room), nameof(Room.Type))] + private Bindable type { get; set; } - public MatchSubScreen(Room room, Action pushGameplayScreen) + [Resolved(typeof(Room))] + protected BindableList Playlist { get; private set; } + + [Resolved(typeof(Room))] + protected Bindable CurrentItem { get; private set; } + + [Resolved] + protected Bindable> CurrentMods { get; private set; } + + public MatchSubScreen(Room room) { Title = room.RoomID.Value == null ? "New room" : room.Name; + } - InternalChild = new Match(pushGameplayScreen) - { - RelativeSizeAxes = Axes.Both, - RequestBeatmapSelection = () => this.Push(new MatchSongSelect - { - Selected = item => - { - playlist.Clear(); - playlist.Add(item); - }, - }), - RequestExit = () => - { - if (this.IsCurrentScreen()) - this.Exit(); - } - }; + private readonly Action pushGameplayScreen; + + private MatchLeaderboard leaderboard; + + [Resolved] + private BeatmapManager beatmapManager { get; set; } + + [Resolved(CanBeNull = true)] + private OsuGame game { get; set; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentItem.BindValueChanged(currentItemChanged, true); + } + + private void currentItemChanged(PlaylistItem item) + { + // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info + var localBeatmap = item?.Beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == item.Beatmap.OnlineBeatmapID); + + Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); + CurrentMods.Value = item?.RequiredMods ?? Enumerable.Empty(); + if (item?.Ruleset != null) + Ruleset.Value = item.Ruleset; } public override bool OnExiting(IScreen next) { - Manager?.PartRoom(); + RoomManager?.PartRoom(); return base.OnExiting(next); } - private class Match : MultiplayerComposite + [BackgroundDependencyLoader] + private void load() { - public Action RequestBeatmapSelection; - public Action RequestExit; + MatchChatDisplay chat; + Components.Header header; + Info info; + GridContainer bottomRow; + MatchSettingsOverlay settings; - private readonly Action pushGameplayScreen; - - private MatchLeaderboard leaderboard; - - [Resolved] - private IBindableBeatmap gameBeatmap { get; set; } - - [Resolved] - private BeatmapManager beatmapManager { get; set; } - - [Resolved(CanBeNull = true)] - private OsuGame game { get; set; } - - public Match(Action pushGameplayScreen) + InternalChildren = new Drawable[] { - this.pushGameplayScreen = pushGameplayScreen; - } - - [BackgroundDependencyLoader] - private void load() - { - MatchChatDisplay chat; - Components.Header header; - Info info; - GridContainer bottomRow; - MatchSettingsOverlay settings; - - InternalChildren = new Drawable[] + new GridContainer { - new GridContainer + RelativeSizeAxes = Axes.Both, + Content = new[] { - RelativeSizeAxes = Axes.Both, - Content = new[] + new Drawable[] { - new Drawable[] + header = new Components.Header { - header = new Components.Header + Depth = -1, + RequestBeatmapSelection = () => { - Depth = -1, - RequestBeatmapSelection = () => RequestBeatmapSelection?.Invoke() - } - }, - new Drawable[] { info = new Info { OnStart = onStart } }, - new Drawable[] - { - bottomRow = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] + this.Push(new MatchSongSelect { - new Drawable[] + Selected = item => { - leaderboard = new MatchLeaderboard + Playlist.Clear(); + Playlist.Add(item); + } + }); + } + } + }, + new Drawable[] { info = new Info { OnStart = onStart } }, + new Drawable[] + { + bottomRow = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + leaderboard = new MatchLeaderboard + { + Padding = new MarginPadding + { + Left = 10 + HORIZONTAL_OVERFLOW_PADDING, + Right = 10, + Vertical = 10, + }, + RelativeSizeAxes = Axes.Both + }, + new Container + { + Padding = new MarginPadding + { + Left = 10, + Right = 10 + HORIZONTAL_OVERFLOW_PADDING, + Vertical = 10, + }, + RelativeSizeAxes = Axes.Both, + Child = chat = new MatchChatDisplay { - Padding = new MarginPadding - { - Left = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Right = 10, - Vertical = 10, - }, RelativeSizeAxes = Axes.Both - }, - new Container - { - Padding = new MarginPadding - { - Left = 10, - Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, - Vertical = 10, - }, - RelativeSizeAxes = Axes.Both, - Child = chat = new MatchChatDisplay - { - RelativeSizeAxes = Axes.Both - } - }, + } }, }, - } - }, + }, + } }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Distributed), - } }, - new Container + RowDimensions = new[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = Components.Header.HEIGHT }, - Child = settings = new MatchSettingsOverlay { RelativeSizeAxes = Axes.Both }, - }, - }; - - header.Tabs.Current.BindValueChanged(t => - { - const float fade_duration = 500; - if (t is SettingsMatchPage) - { - settings.Show(); - info.FadeOut(fade_duration, Easing.OutQuint); - bottomRow.FadeOut(fade_duration, Easing.OutQuint); + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Distributed), } - else - { - settings.Hide(); - info.FadeIn(fade_duration, Easing.OutQuint); - bottomRow.FadeIn(fade_duration, Easing.OutQuint); - } - }, true); - - chat.Exit += () => RequestExit?.Invoke(); - - beatmapManager.ItemAdded += beatmapAdded; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - CurrentBeatmap.BindValueChanged(setBeatmap, true); - CurrentRuleset.BindValueChanged(setRuleset, true); - } - - private void setBeatmap(BeatmapInfo beatmap) - { - // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info - var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); - - game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); - } - - private void setRuleset(RulesetInfo ruleset) - { - if (ruleset == null) - return; - - game?.ForcefullySetRuleset(ruleset); - } - - private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => - { - if (gameBeatmap.Value != beatmapManager.DefaultBeatmap) - return; - - if (CurrentBeatmap.Value == null) - return; - - // Try to retrieve the corresponding local beatmap - var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == CurrentBeatmap.Value.OnlineBeatmapID); - - if (localBeatmap != null) - game?.ForcefullySetBeatmap(beatmapManager.GetWorkingBeatmap(localBeatmap)); - }); - - private void onStart() - { - gameBeatmap.Value.Mods.Value = CurrentMods.Value.ToArray(); - - switch (Type.Value) + }, + new Container { - default: - case GameTypeTimeshift _: - pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(Playlist.First()) - { - Exited = () => leaderboard.RefreshScores() - })); - break; + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = Components.Header.HEIGHT }, + Child = settings = new MatchSettingsOverlay { RelativeSizeAxes = Axes.Both }, + }, + }; + + header.Tabs.Current.BindValueChanged(t => + { + const float fade_duration = 500; + if (t is SettingsMatchPage) + { + settings.Show(); + info.FadeOut(fade_duration, Easing.OutQuint); + bottomRow.FadeOut(fade_duration, Easing.OutQuint); } - } + else + { + settings.Hide(); + info.FadeIn(fade_duration, Easing.OutQuint); + bottomRow.FadeIn(fade_duration, Easing.OutQuint); + } + }, true); - protected override void Dispose(bool isDisposing) + chat.Exit += () => { - base.Dispose(isDisposing); + if (this.IsCurrentScreen()) + this.Exit(); + }; - if (beatmapManager != null) - beatmapManager.ItemAdded -= beatmapAdded; + beatmapManager.ItemAdded += beatmapAdded; + } + + private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => + { + if (Beatmap.Value != beatmapManager.DefaultBeatmap) + return; + + if (Beatmap.Value == null) + return; + + // Try to retrieve the corresponding local beatmap + var localBeatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == CurrentItem.Value.Beatmap.OnlineBeatmapID); + + if (localBeatmap != null) + Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); + }); + + private void onStart() + { + //Beatmap.Value.Mods.Value = CurrentMods.Value.ToArray(); + + switch (type.Value) + { + default: + case GameTypeTimeshift _: + pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(CurrentItem) + { + Exited = () => leaderboard.RefreshScores() + })); + break; } } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (beatmapManager != null) + beatmapManager.ItemAdded -= beatmapAdded; + } } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 42208a2165..7ea4736b04 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Screens; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; @@ -16,9 +15,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; -using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet.Buttons; -using osu.Game.Rulesets; using osu.Game.Screens.Menu; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; @@ -28,19 +25,9 @@ using osuTK; namespace osu.Game.Screens.Multi { [Cached] - public class Multiplayer : CompositeDrawable, IOsuScreen, IOnlineComponent + public class Multiplayer : OsuScreen, IOnlineComponent { - public bool DisallowExternalBeatmapRulesetChanges => false; - - public bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true; - - public bool HideOverlaysOnEnter => false; - public OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; - - public float BackgroundParallaxAmount => 1; - - public bool ValidForResume { get; set; } = true; - public bool ValidForPush { get; set; } = true; + public override bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true; public override bool RemoveWhenNotAlive => false; @@ -70,20 +57,6 @@ namespace osu.Game.Screens.Multi [Resolved(CanBeNull = true)] private OsuLogo logo { get; set; } - public Bindable Beatmap { get; set; } - - public Bindable Ruleset { get; set; } - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var deps = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); - - Beatmap = deps.Beatmap; - Ruleset = deps.Ruleset; - - return deps; - } - public Multiplayer() { Anchor = Anchor.Centre; @@ -95,8 +68,8 @@ namespace osu.Game.Screens.Multi RelativeSizeAxes = Axes.Both, }; - screenStack = new ScreenStack(loungeSubScreen = new LoungeSubScreen(pushGameplayScreen)) { RelativeSizeAxes = Axes.Both }; - Padding = new MarginPadding { Horizontal = -OsuScreen.HORIZONTAL_OVERFLOW_PADDING }; + screenStack = new ScreenStack(loungeSubScreen = new LoungeSubScreen()) { RelativeSizeAxes = Axes.Both }; + Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING }; waves.AddRange(new Drawable[] { @@ -136,7 +109,7 @@ namespace osu.Game.Screens.Multi Margin = new MarginPadding { Top = 10, - Right = 10 + OsuScreen.HORIZONTAL_OVERFLOW_PADDING, + Right = 10 + HORIZONTAL_OVERFLOW_PADDING, }, Text = "Create room", Action = () => loungeSubScreen.Open(new Room @@ -207,14 +180,14 @@ namespace osu.Game.Screens.Multi } } - public void OnEntering(IScreen last) + public override void OnEntering(IScreen last) { this.FadeIn(); waves.Show(); } - public bool OnExiting(IScreen next) + public override bool OnExiting(IScreen next) { waves.Hide(); @@ -233,17 +206,17 @@ namespace osu.Game.Screens.Multi return false; } - public void OnResuming(IScreen last) + public override void OnResuming(IScreen last) { this.FadeIn(250); this.ScaleTo(1, 250, Easing.OutSine); - logo?.AppendAnimatingAction(() => OsuScreen.ApplyLogoArrivingDefaults(logo), true); + logo?.AppendAnimatingAction(() => ApplyLogoArrivingDefaults(logo), true); updatePollingRate(isIdle.Value); } - public void OnSuspending(IScreen next) + public override void OnSuspending(IScreen next) { this.ScaleTo(1.1f, 250, Easing.InSine); this.FadeOut(250); @@ -254,7 +227,7 @@ namespace osu.Game.Screens.Multi private void cancelLooping() { - var track = beatmap?.Value?.Track; + var track = Beatmap?.Value?.Track; if (track != null) track.Looping = false; diff --git a/osu.Game/Screens/Multi/MultiplayerComposite.cs b/osu.Game/Screens/Multi/MultiplayerComposite.cs index 1a16db97a4..245a6ac358 100644 --- a/osu.Game/Screens/Multi/MultiplayerComposite.cs +++ b/osu.Game/Screens/Multi/MultiplayerComposite.cs @@ -3,14 +3,10 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Online.Multiplayer; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Users; namespace osu.Game.Screens.Multi @@ -35,6 +31,9 @@ namespace osu.Game.Screens.Multi [Resolved(typeof(Room))] protected BindableList Playlist { get; private set; } + [Resolved(typeof(Room))] + protected Bindable CurrentItem { get; private set; } + [Resolved(typeof(Room))] protected Bindable> Participants { get; private set; } @@ -52,35 +51,5 @@ namespace osu.Game.Screens.Multi [Resolved(typeof(Room))] protected Bindable Duration { get; private set; } - - private readonly Bindable currentBeatmap = new Bindable(); - protected IBindable CurrentBeatmap => currentBeatmap; - - private readonly Bindable> currentMods = new Bindable>(); - protected IBindable> CurrentMods => currentMods; - - private readonly Bindable currentRuleset = new Bindable(); - protected IBindable CurrentRuleset => currentRuleset; - - protected override void LoadComplete() - { - base.LoadComplete(); - - Playlist.ItemsAdded += _ => updatePlaylist(); - Playlist.ItemsRemoved += _ => updatePlaylist(); - - updatePlaylist(); - } - - private void updatePlaylist() - { - // Todo: We only ever have one playlist item for now. In the future, this will be user-settable - - var playlistItem = Playlist.FirstOrDefault(); - - currentBeatmap.Value = playlistItem?.Beatmap; - currentMods.Value = playlistItem?.RequiredMods ?? Enumerable.Empty(); - currentRuleset.Value = playlistItem?.Ruleset; - } } } diff --git a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs index b0d89c9bba..ad72072981 100644 --- a/osu.Game/Screens/Multi/MultiplayerSubScreen.cs +++ b/osu.Game/Screens/Multi/MultiplayerSubScreen.cs @@ -2,57 +2,27 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Framework.Screens; -using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; -using osu.Game.Overlays; -using osu.Game.Rulesets; namespace osu.Game.Screens.Multi { - public abstract class MultiplayerSubScreen : CompositeDrawable, IMultiplayerSubScreen, IKeyBindingHandler + public abstract class MultiplayerSubScreen : OsuScreen, IMultiplayerSubScreen, IKeyBindingHandler { - public virtual bool DisallowExternalBeatmapRulesetChanges => false; - - public bool CursorVisible => true; - - public bool HideOverlaysOnEnter => false; - public OverlayActivation InitialOverlayActivationMode => OverlayActivation.All; - - public float BackgroundParallaxAmount => 1; - - public bool ValidForResume { get; set; } = true; - public bool ValidForPush { get; set; } = true; + public override bool DisallowExternalBeatmapRulesetChanges => false; public override bool RemoveWhenNotAlive => false; - public abstract string Title { get; } public virtual string ShortTitle => Title; - public Bindable Beatmap { get; set; } - - public Bindable Ruleset { get; set; } - - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var deps = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, base.CreateChildDependencies(parent)); - - Beatmap = deps.Beatmap; - Ruleset = deps.Ruleset; - - return deps; - } - [Resolved(CanBeNull = true)] protected OsuGame Game { get; private set; } [Resolved(CanBeNull = true)] - protected IRoomManager Manager { get; private set; } + protected IRoomManager RoomManager { get; private set; } protected MultiplayerSubScreen() { @@ -61,14 +31,14 @@ namespace osu.Game.Screens.Multi RelativeSizeAxes = Axes.Both; } - public virtual void OnEntering(IScreen last) + public override void OnEntering(IScreen last) { this.FadeInFromZero(WaveContainer.APPEAR_DURATION, Easing.OutQuint); this.FadeInFromZero(WaveContainer.APPEAR_DURATION, Easing.OutQuint); this.MoveToX(200).MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); } - public virtual bool OnExiting(IScreen next) + public override bool OnExiting(IScreen next) { this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); this.MoveToX(200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); @@ -76,19 +46,19 @@ namespace osu.Game.Screens.Multi return false; } - public virtual void OnResuming(IScreen last) + public override void OnResuming(IScreen last) { this.FadeIn(WaveContainer.APPEAR_DURATION, Easing.OutQuint); this.MoveToX(0, WaveContainer.APPEAR_DURATION, Easing.OutQuint); } - public virtual void OnSuspending(IScreen next) + public override void OnSuspending(IScreen next) { this.FadeOut(WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); this.MoveToX(-200, WaveContainer.DISAPPEAR_DURATION, Easing.OutQuint); } - public virtual bool OnPressed(GlobalAction action) + public override bool OnPressed(GlobalAction action) { if (!this.IsCurrentScreen()) return false; From e4422167b6a77af486eb03369ff98affdbaa4497 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 11 Feb 2019 19:13:57 +0900 Subject: [PATCH 070/167] Fix starting gameplay --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 41c498538f..7a9b040e37 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -217,13 +217,14 @@ namespace osu.Game.Screens.Multi.Match private void onStart() { - //Beatmap.Value.Mods.Value = CurrentMods.Value.ToArray(); + // todo: is this required? + Beatmap.Value.Mods.Value = CurrentMods.Value.ToArray(); switch (type.Value) { default: case GameTypeTimeshift _: - pushGameplayScreen?.Invoke(new PlayerLoader(() => new TimeshiftPlayer(CurrentItem) + this.Push(new PlayerLoader(() => new TimeshiftPlayer(CurrentItem) { Exited = () => leaderboard.RefreshScores() })); From e57409fe41623f3f1b4fac7df221bc847223559b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Feb 2019 10:51:39 +0900 Subject: [PATCH 071/167] Remove unnecessary bindable properties on mod lists --- osu.Game/Online/Multiplayer/PlaylistItem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Multiplayer/PlaylistItem.cs b/osu.Game/Online/Multiplayer/PlaylistItem.cs index ff3ae240cb..e47d497d94 100644 --- a/osu.Game/Online/Multiplayer/PlaylistItem.cs +++ b/osu.Game/Online/Multiplayer/PlaylistItem.cs @@ -1,9 +1,9 @@ // 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 System.Linq; using Newtonsoft.Json; -using osu.Framework.Configuration; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; @@ -37,10 +37,10 @@ namespace osu.Game.Online.Multiplayer public RulesetInfo Ruleset { get; set; } [JsonIgnore] - public readonly BindableList AllowedMods = new BindableList(); + public readonly List AllowedMods = new List(); [JsonIgnore] - public readonly BindableList RequiredMods = new BindableList(); + public readonly List RequiredMods = new List(); [JsonProperty("beatmap")] private APIBeatmap apiBeatmap { get; set; } From 78b47f9fe3f75768b0d099fa9dfaa99b316c01b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Feb 2019 11:19:34 +0900 Subject: [PATCH 072/167] Fix starting matches not working --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 15 ++++++++------- osu.Game/Screens/Multi/Multiplayer.cs | 13 ++++++++++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 7a9b040e37..a7a2b9003f 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.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 System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -15,7 +14,6 @@ using osu.Game.Online.Multiplayer.GameTypes; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Multi.Match.Components; using osu.Game.Screens.Multi.Play; -using osu.Game.Screens.Play; using osu.Game.Screens.Select; using PlaylistItem = osu.Game.Online.Multiplayer.PlaylistItem; @@ -52,8 +50,6 @@ namespace osu.Game.Screens.Multi.Match Title = room.RoomID.Value == null ? "New room" : room.Name; } - private readonly Action pushGameplayScreen; - private MatchLeaderboard leaderboard; [Resolved] @@ -200,6 +196,9 @@ namespace osu.Game.Screens.Multi.Match beatmapManager.ItemAdded += beatmapAdded; } + /// + /// Handle the case where a beatmap is imported (and can be used by this match). + /// private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => { if (Beatmap.Value != beatmapManager.DefaultBeatmap) @@ -215,19 +214,21 @@ namespace osu.Game.Screens.Multi.Match Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); }); + [Resolved(canBeNull: true)] + private Multiplayer multiplayer { get; set; } + private void onStart() { - // todo: is this required? Beatmap.Value.Mods.Value = CurrentMods.Value.ToArray(); switch (type.Value) { default: case GameTypeTimeshift _: - this.Push(new PlayerLoader(() => new TimeshiftPlayer(CurrentItem) + multiplayer?.Start(() => new TimeshiftPlayer(CurrentItem) { Exited = () => leaderboard.RefreshScores() - })); + }); break; } } diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 7ea4736b04..afb4955c56 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.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; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -20,6 +21,7 @@ using osu.Game.Screens.Menu; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; using osu.Game.Screens.Multi.Match; +using osu.Game.Screens.Play; using osuTK; namespace osu.Game.Screens.Multi @@ -29,7 +31,7 @@ namespace osu.Game.Screens.Multi { public override bool CursorVisible => (screenStack.CurrentScreen as IMultiplayerSubScreen)?.CursorVisible ?? true; - public override bool RemoveWhenNotAlive => false; + public override bool DisallowExternalBeatmapRulesetChanges => true; private readonly MultiplayerWaveContainer waves; @@ -292,5 +294,14 @@ namespace osu.Game.Screens.Multi FourthWaveColour = OsuColour.FromHex(@"392850"); } } + + /// + /// Push a to the main screen stack to begin gameplay. + /// Generally called from a via DI resolution. + /// + public void Start(Func player) + { + this.Push(new PlayerLoader(player)); + } } } From 272584eb796d7631d0798cff2178ec30b60a4766 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Feb 2019 13:02:33 +0900 Subject: [PATCH 073/167] Improve file layouts --- .../Screens/Multi/Match/MatchSubScreen.cs | 56 ++++---- osu.Game/Screens/Multi/Multiplayer.cs | 122 ++++++++---------- 2 files changed, 84 insertions(+), 94 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index a7a2b9003f..cd51aea575 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -45,41 +45,17 @@ namespace osu.Game.Screens.Multi.Match [Resolved] protected Bindable> CurrentMods { get; private set; } - public MatchSubScreen(Room room) - { - Title = room.RoomID.Value == null ? "New room" : room.Name; - } - - private MatchLeaderboard leaderboard; - [Resolved] private BeatmapManager beatmapManager { get; set; } [Resolved(CanBeNull = true)] private OsuGame game { get; set; } - protected override void LoadComplete() + private MatchLeaderboard leaderboard; + + public MatchSubScreen(Room room) { - base.LoadComplete(); - - CurrentItem.BindValueChanged(currentItemChanged, true); - } - - private void currentItemChanged(PlaylistItem item) - { - // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info - var localBeatmap = item?.Beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == item.Beatmap.OnlineBeatmapID); - - Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); - CurrentMods.Value = item?.RequiredMods ?? Enumerable.Empty(); - if (item?.Ruleset != null) - Ruleset.Value = item.Ruleset; - } - - public override bool OnExiting(IScreen next) - { - RoomManager?.PartRoom(); - return base.OnExiting(next); + Title = room.RoomID.Value == null ? "New room" : room.Name; } [BackgroundDependencyLoader] @@ -196,6 +172,30 @@ namespace osu.Game.Screens.Multi.Match beatmapManager.ItemAdded += beatmapAdded; } + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentItem.BindValueChanged(currentItemChanged, true); + } + + private void currentItemChanged(PlaylistItem item) + { + // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info + var localBeatmap = item?.Beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == item.Beatmap.OnlineBeatmapID); + + Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); + CurrentMods.Value = item?.RequiredMods ?? Enumerable.Empty(); + if (item?.Ruleset != null) + Ruleset.Value = item.Ruleset; + } + + public override bool OnExiting(IScreen next) + { + RoomManager?.PartRoom(); + return base.OnExiting(next); + } + /// /// Handle the case where a beatmap is imported (and can be used by this match). /// diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index afb4955c56..1103e781ff 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -64,64 +64,61 @@ namespace osu.Game.Screens.Multi Anchor = Anchor.Centre; Origin = Anchor.Centre; RelativeSizeAxes = Axes.Both; + Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING }; InternalChild = waves = new MultiplayerWaveContainer { RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"3e3a44"), + }, + new Triangles + { + RelativeSizeAxes = Axes.Both, + ColourLight = OsuColour.FromHex(@"3c3842"), + ColourDark = OsuColour.FromHex(@"393540"), + TriangleScale = 5, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = Header.HEIGHT }, + Child = screenStack = new ScreenStack(loungeSubScreen = new LoungeSubScreen()) { RelativeSizeAxes = Axes.Both } + }, + new Header(screenStack), + createButton = new HeaderButton + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.None, + Size = new Vector2(150, Header.HEIGHT - 20), + Margin = new MarginPadding + { + Top = 10, + Right = 10 + HORIZONTAL_OVERFLOW_PADDING, + }, + Text = "Create room", + Action = () => loungeSubScreen.Open(new Room + { + Name = { Value = $"{api.LocalUser}'s awesome room" } + }), + }, + roomManager = new RoomManager() + } }; - screenStack = new ScreenStack(loungeSubScreen = new LoungeSubScreen()) { RelativeSizeAxes = Axes.Both }; - Padding = new MarginPadding { Horizontal = -HORIZONTAL_OVERFLOW_PADDING }; - - waves.AddRange(new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"3e3a44"), - }, - new Triangles - { - RelativeSizeAxes = Axes.Both, - ColourLight = OsuColour.FromHex(@"3c3842"), - ColourDark = OsuColour.FromHex(@"393540"), - TriangleScale = 5, - }, - }, - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = Header.HEIGHT }, - Child = screenStack - }, - new Header(screenStack), - createButton = new HeaderButton - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.None, - Size = new Vector2(150, Header.HEIGHT - 20), - Margin = new MarginPadding - { - Top = 10, - Right = 10 + HORIZONTAL_OVERFLOW_PADDING, - }, - Text = "Create room", - Action = () => loungeSubScreen.Open(new Room - { - Name = { Value = $"{api.LocalUser}'s awesome room" } - }), - }, - roomManager = new RoomManager() - }); - screenStack.ScreenPushed += screenPushed; screenStack.ScreenExited += screenExited; } @@ -141,11 +138,9 @@ namespace osu.Game.Screens.Multi isIdle.BindValueChanged(updatePollingRate, true); } - private CachedModelDependencyContainer dependencies; - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); + var dependencies = new CachedModelDependencyContainer(base.CreateChildDependencies(parent)); dependencies.Model.BindTo(currentRoom); return dependencies; } @@ -156,12 +151,16 @@ namespace osu.Game.Screens.Multi Logger.Log($"Polling adjusted to {roomManager.TimeBetweenPolls}"); } - private void pushGameplayScreen(IScreen gameplayScreen) + /// + /// 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(gameplayScreen); + this.Push(new PlayerLoader(player)); } public void APIStateChanged(APIAccess api, APIState state) @@ -294,14 +293,5 @@ namespace osu.Game.Screens.Multi FourthWaveColour = OsuColour.FromHex(@"392850"); } } - - /// - /// Push a to the main screen stack to begin gameplay. - /// Generally called from a via DI resolution. - /// - public void Start(Func player) - { - this.Push(new PlayerLoader(player)); - } } } From d5cce850a8bd035f376912c72ec384ec6e8bd79b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Feb 2019 13:29:41 +0900 Subject: [PATCH 074/167] Revert some unnecessary complications in logo logic --- osu.Game/Screens/Multi/Match/MatchSubScreen.cs | 15 +++++++++------ osu.Game/Screens/Multi/Multiplayer.cs | 15 ++++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index cd51aea575..d97b32e54a 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -179,6 +179,15 @@ namespace osu.Game.Screens.Multi.Match CurrentItem.BindValueChanged(currentItemChanged, true); } + public override bool OnExiting(IScreen next) + { + RoomManager?.PartRoom(); + return base.OnExiting(next); + } + + /// + /// Handles propagation of the current playlist item's content to game-wide mechanisms. + /// private void currentItemChanged(PlaylistItem item) { // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info @@ -190,12 +199,6 @@ namespace osu.Game.Screens.Multi.Match Ruleset.Value = item.Ruleset; } - public override bool OnExiting(IScreen next) - { - RoomManager?.PartRoom(); - return base.OnExiting(next); - } - /// /// Handle the case where a beatmap is imported (and can be used by this match). /// diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 1103e781ff..822be0891b 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -184,7 +184,6 @@ namespace osu.Game.Screens.Multi public override void OnEntering(IScreen last) { this.FadeIn(); - waves.Show(); } @@ -201,18 +200,24 @@ namespace osu.Game.Screens.Multi updatePollingRate(isIdle.Value); - // the wave overlay transition takes longer than expected to run. - logo?.AppendAnimatingAction(() => logo.Delay(WaveContainer.DISAPPEAR_DURATION / 2).FadeOut(), false); - + base.OnExiting(next); return false; } + protected override void LogoExiting(OsuLogo logo) + { + base.LogoExiting(logo); + + // the wave overlay transition takes longer than expected to run. + logo.Delay(WaveContainer.DISAPPEAR_DURATION / 2).FadeOut(); + } + public override void OnResuming(IScreen last) { this.FadeIn(250); this.ScaleTo(1, 250, Easing.OutSine); - logo?.AppendAnimatingAction(() => ApplyLogoArrivingDefaults(logo), true); + base.OnResuming(last); updatePollingRate(isIdle.Value); } From a28689ff4c478946ab5d2cba015e58ef46058fb9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Feb 2019 17:58:36 +0900 Subject: [PATCH 075/167] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e87b43ac93..c1a77a17cd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 0d8a7e3a34..c6b3494d47 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From 2ea839c475aa970ac1c22ac904d0ec681e45c272 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Feb 2019 19:18:22 +0900 Subject: [PATCH 076/167] Fix crashes on beatmap not being set correctly in player --- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Play/ReplayPlayer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 71b7b77e5d..5a2ef688ca 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -383,7 +383,7 @@ namespace osu.Game.Screens.Play return true; } - if ((!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused != false || RulesetContainer?.HasReplayLoaded != false) && (!pauseContainer?.IsResuming ?? true)) + if ((!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused.Value != false || RulesetContainer?.HasReplayLoaded?.Value != false) && (!pauseContainer?.IsResuming ?? true)) { // In the case of replays, we may have changed the playback rate. applyRateFromMods(); diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 3317deca8b..3190139378 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Play protected override void LoadComplete() { base.LoadComplete(); - RulesetContainer.SetReplayScore(score); + RulesetContainer?.SetReplayScore(score); } protected override ScoreInfo CreateScore() => score.ScoreInfo; From b967b93b8847060064c3f8c9476200e7dc60f58a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Feb 2019 19:53:08 +0900 Subject: [PATCH 077/167] Fix regressions in tests --- osu.Game.Tests/Visual/TestCasePlayerLoader.cs | 14 ++++++++++++-- osu.Game/Tests/Visual/OsuTestCase.cs | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs index 82ce7e125a..16cb94c65e 100644 --- a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs +++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs @@ -3,6 +3,7 @@ using System.Threading; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Screens.Play; @@ -12,28 +13,37 @@ namespace osu.Game.Tests.Visual public class TestCasePlayerLoader : ManualInputManagerTestCase { private PlayerLoader loader; + private ScreenStack stack; [BackgroundDependencyLoader] private void load(OsuGameBase game) { Beatmap.Value = new DummyWorkingBeatmap(game); - AddStep("load dummy beatmap", () => Add(loader = new PlayerLoader(() => new Player + InputManager.Add(stack = new ScreenStack { RelativeSizeAxes = Axes.Both }); + + AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player { AllowPause = false, AllowLeadIn = false, AllowResults = false, }))); + AddUntilStep(() => loader.IsCurrentScreen(), "wait for current"); + AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddUntilStep(() => !loader.IsCurrentScreen(), "wait for no longer current"); + AddStep("exit loader", () => loader.Exit()); + + AddUntilStep(() => !loader.IsAlive, "wait for no longer alive"); + AddStep("load slow dummy beatmap", () => { SlowLoadPlayer slow = null; - Add(loader = new PlayerLoader(() => slow = new SlowLoadPlayer + stack.Push(loader = new PlayerLoader(() => slow = new SlowLoadPlayer { AllowPause = false, AllowLeadIn = false, diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 3dfcd0febd..74cd4684da 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures beatmap.Default = new DummyWorkingBeatmap(Dependencies.Get()); - Dependencies.CacheAs(beatmap); + Dependencies.CacheAs>(beatmap); Dependencies.CacheAs>(beatmap); Dependencies.CacheAs(Ruleset); From 11234d3c60d5b941d8487caf446ba0c85a6871f4 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Tue, 12 Feb 2019 21:20:49 +0300 Subject: [PATCH 078/167] Update direct download button state on beatmap import and removal --- osu.Game/Overlays/Direct/DownloadButton.cs | 9 +++++++++ .../Direct/DownloadTrackingComposite.cs | 20 ++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index 201a79f58a..b3372bf237 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -76,6 +76,9 @@ namespace osu.Game.Overlays.Direct { this.colours = colours; + beatmaps.ItemAdded += (set, existing, silent) => updateState(BeatmapSet.Value); + beatmaps.ItemRemoved += set => updateState(BeatmapSet.Value); + button.Action = () => { switch (State.Value) @@ -94,6 +97,12 @@ namespace osu.Game.Overlays.Direct }; } + private void updateState(BeatmapSetInfo set) + { + if (set.OnlineBeatmapSetID == BeatmapSet.Value.OnlineBeatmapSetID) + UpdateState(BeatmapSet.Value); + } + private void updateState(DownloadState state) { switch (state) diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index f255403e81..eabc02d66e 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -34,15 +34,7 @@ namespace osu.Game.Overlays.Direct { this.beatmaps = beatmaps; - BeatmapSet.BindValueChanged(set => - { - if (set == null) - attachDownload(null); - else if (beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID).Any()) - State.Value = DownloadState.LocallyAvailable; - else - attachDownload(beatmaps.GetExistingDownload(set)); - }, true); + BeatmapSet.BindValueChanged(UpdateState, true); beatmaps.BeatmapDownloadBegan += download => { @@ -53,6 +45,16 @@ namespace osu.Game.Overlays.Direct beatmaps.ItemAdded += setAdded; } + protected void UpdateState(BeatmapSetInfo set) + { + if (set == null) + attachDownload(null); + else if (this.beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any()) + State.Value = DownloadState.LocallyAvailable; + else + attachDownload(this.beatmaps.GetExistingDownload(set)); + } + #region Disposal protected override void Dispose(bool isDisposing) From 19bef01dd0d2cdbaf067a713ec9af9ec10087fce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Feb 2019 11:05:02 +0900 Subject: [PATCH 079/167] Attempt to maybe fix tests --- osu.Game/Tests/Visual/ScreenTestCase.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Tests/Visual/ScreenTestCase.cs b/osu.Game/Tests/Visual/ScreenTestCase.cs index 79c57ad9f4..c0eceb84a7 100644 --- a/osu.Game/Tests/Visual/ScreenTestCase.cs +++ b/osu.Game/Tests/Visual/ScreenTestCase.cs @@ -33,5 +33,11 @@ namespace osu.Game.Tests.Visual stack.Exit(); stack.Push(screen); } + + protected override void Dispose(bool isDisposing) + { + stack.Dispose(); + base.Dispose(isDisposing); + } } } From c3ae4d7b142ac66a6a19bb7f72ec006415f7224a Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Wed, 13 Feb 2019 11:34:48 +0900 Subject: [PATCH 080/167] Improve comment Co-Authored-By: peppy --- osu.Game/Screens/OsuScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 86f1f3a9d9..b8d6fda97d 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens protected new OsuGameBase Game => base.Game as OsuGameBase; /// - /// Disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children). + /// Whether to disallow changes to game-wise Beatmap/Ruleset bindables for this screen (and all children). /// public virtual bool DisallowExternalBeatmapRulesetChanges => false; From ab3adafafda7efedf7e36e0278b69d449fe2c302 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Feb 2019 14:13:54 +0900 Subject: [PATCH 081/167] Fix crashes after entering player --- osu.Game/Screens/Select/PlaySongSelect.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 982a44a8d3..7b3c3e0ec3 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -58,7 +58,6 @@ namespace osu.Game.Screens.Select } Beatmap.Value.Track.Looping = false; - Beatmap.Disabled = true; SampleConfirm?.Play(); From 166cdab2e83c37625ff6fb5a019873572cb63f0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Feb 2019 14:14:57 +0900 Subject: [PATCH 082/167] Remove unnecessary null check --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5a2ef688ca..2ab207e47a 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -383,7 +383,7 @@ namespace osu.Game.Screens.Play return true; } - if ((!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused.Value != false || RulesetContainer?.HasReplayLoaded?.Value != false) && (!pauseContainer?.IsResuming ?? true)) + if ((!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused.Value != false || RulesetContainer?.HasReplayLoaded.Value != false) && (!pauseContainer?.IsResuming ?? true)) { // In the case of replays, we may have changed the playback rate. applyRateFromMods(); From 1373e0fad078a73f5647ed43a94ac46dd2cb8027 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Feb 2019 14:57:40 +0900 Subject: [PATCH 083/167] Fix BeatmapTitle not always displaying --- osu.Game/Screens/Multi/Components/BeatmapTitle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs index ff1a1fb3a4..7a513a7f5f 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs @@ -48,7 +48,7 @@ namespace osu.Game.Screens.Multi.Components private void updateText() { - if (!IsLoaded) + if (LoadState < LoadState.Loading) return; textFlow.Clear(); From 43843ac5586b2fb5e25292f80c4147e0ebab213c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Feb 2019 14:58:40 +0900 Subject: [PATCH 084/167] Remove explicit dispose --- osu.Game/Tests/Visual/ScreenTestCase.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Tests/Visual/ScreenTestCase.cs b/osu.Game/Tests/Visual/ScreenTestCase.cs index c0eceb84a7..79c57ad9f4 100644 --- a/osu.Game/Tests/Visual/ScreenTestCase.cs +++ b/osu.Game/Tests/Visual/ScreenTestCase.cs @@ -33,11 +33,5 @@ namespace osu.Game.Tests.Visual stack.Exit(); stack.Push(screen); } - - protected override void Dispose(bool isDisposing) - { - stack.Dispose(); - base.Dispose(isDisposing); - } } } From 3ec94e4ab36c8ef02f4ca6dfce7127785c84ead0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Feb 2019 15:14:34 +0900 Subject: [PATCH 085/167] Remove disable setting --- osu.Game/Tests/Visual/OsuTestCase.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs index 74cd4684da..6bff4c0291 100644 --- a/osu.Game/Tests/Visual/OsuTestCase.cs +++ b/osu.Game/Tests/Visual/OsuTestCase.cs @@ -58,11 +58,7 @@ namespace osu.Game.Tests.Visual { base.Dispose(isDisposing); - if (beatmap != null) - { - beatmap.Disabled = true; - beatmap.Value.Track.Stop(); - } + beatmap?.Value.Track.Stop(); if (localStorage.IsValueCreated) { From e604806398ca26a392bea35b87e15ef0663ddca5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Feb 2019 19:43:01 +0900 Subject: [PATCH 086/167] Fix regression in screen change allowance logic --- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/IOsuScreen.cs | 6 ++++++ osu.Game/Screens/Select/MatchSongSelect.cs | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3b71324644..4dc9d71d37 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -292,7 +292,7 @@ namespace osu.Game return; } - if ((screenStack.CurrentScreen as IOsuScreen)?.DisallowExternalBeatmapRulesetChanges != false) + if ((screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false) { notifications.Post(new SimpleNotification { diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index f256760a0a..9e28de5593 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -17,6 +17,12 @@ namespace osu.Game.Screens /// bool DisallowExternalBeatmapRulesetChanges { get; } + /// + /// Whether a top-level component should be allowed to exit the current screen to, for example, + /// complete an import. + /// + bool AllowExternalScreenChange { get; } + /// /// Whether this allows the cursor to be displayed. /// diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index cfeaa1785e..d7ff95be85 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -17,6 +17,8 @@ namespace osu.Game.Screens.Select public string ShortTitle => "song selection"; public override string Title => ShortTitle.Humanize(); + public override bool AllowExternalScreenChange => false; + public MatchSongSelect() { Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; From 5318de29b4d36bba60971c12e6a8a85695e9e69e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Feb 2019 19:57:54 +0900 Subject: [PATCH 087/167] Fix import logic again --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4dc9d71d37..ad6411a2d6 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -311,7 +311,7 @@ namespace osu.Game void loadScore() { - if (!menuScreen.IsCurrentScreen()) + if (!menuScreen.IsCurrentScreen() || Beatmap.Disabled) { menuScreen.MakeCurrent(); this.Delay(500).Schedule(loadScore, out scoreLoad); From 99046f16e8edbd16a39ab49f6f1cb83b38510fc3 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Wed, 13 Feb 2019 22:04:49 +0300 Subject: [PATCH 088/167] Revert "Update direct download button state on beatmap import and removal" This reverts commit 11234d3c60d5b941d8487caf446ba0c85a6871f4. --- osu.Game/Overlays/Direct/DownloadButton.cs | 9 --------- .../Direct/DownloadTrackingComposite.cs | 20 +++++++++---------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Direct/DownloadButton.cs b/osu.Game/Overlays/Direct/DownloadButton.cs index b3372bf237..201a79f58a 100644 --- a/osu.Game/Overlays/Direct/DownloadButton.cs +++ b/osu.Game/Overlays/Direct/DownloadButton.cs @@ -76,9 +76,6 @@ namespace osu.Game.Overlays.Direct { this.colours = colours; - beatmaps.ItemAdded += (set, existing, silent) => updateState(BeatmapSet.Value); - beatmaps.ItemRemoved += set => updateState(BeatmapSet.Value); - button.Action = () => { switch (State.Value) @@ -97,12 +94,6 @@ namespace osu.Game.Overlays.Direct }; } - private void updateState(BeatmapSetInfo set) - { - if (set.OnlineBeatmapSetID == BeatmapSet.Value.OnlineBeatmapSetID) - UpdateState(BeatmapSet.Value); - } - private void updateState(DownloadState state) { switch (state) diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index eabc02d66e..f255403e81 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -34,7 +34,15 @@ namespace osu.Game.Overlays.Direct { this.beatmaps = beatmaps; - BeatmapSet.BindValueChanged(UpdateState, true); + BeatmapSet.BindValueChanged(set => + { + if (set == null) + attachDownload(null); + else if (beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID).Any()) + State.Value = DownloadState.LocallyAvailable; + else + attachDownload(beatmaps.GetExistingDownload(set)); + }, true); beatmaps.BeatmapDownloadBegan += download => { @@ -45,16 +53,6 @@ namespace osu.Game.Overlays.Direct beatmaps.ItemAdded += setAdded; } - protected void UpdateState(BeatmapSetInfo set) - { - if (set == null) - attachDownload(null); - else if (this.beatmaps.QueryBeatmapSets(s => s.OnlineBeatmapSetID == set.OnlineBeatmapSetID && !s.DeletePending).Any()) - State.Value = DownloadState.LocallyAvailable; - else - attachDownload(this.beatmaps.GetExistingDownload(set)); - } - #region Disposal protected override void Dispose(bool isDisposing) From a289cb7c6a8f8b5b47983c0320f86d21eae16f91 Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Wed, 13 Feb 2019 22:11:46 +0300 Subject: [PATCH 089/167] Handle beatmapset removal in DownloadTrackingComposite --- osu.Game/Overlays/Direct/DownloadTrackingComposite.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index f255403e81..f7d9264af0 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -51,6 +51,7 @@ namespace osu.Game.Overlays.Direct }; beatmaps.ItemAdded += setAdded; + beatmaps.ItemRemoved += setRemoved; } #region Disposal @@ -120,12 +121,16 @@ namespace osu.Game.Overlays.Direct Schedule(() => attachDownload(null)); } - private void setAdded(BeatmapSetInfo s, bool existing, bool silent) + private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => setDownloadState(s, DownloadState.Downloaded); + + private void setRemoved(BeatmapSetInfo s) => setDownloadState(s, DownloadState.NotDownloaded); + + private void setDownloadState(BeatmapSetInfo s, DownloadState state) { if (s.OnlineBeatmapSetID != BeatmapSet.Value?.OnlineBeatmapSetID) return; - Schedule(() => State.Value = DownloadState.LocallyAvailable); + Schedule(() => State.Value = state); } } } From 94ceb1e32bc38e545a4d69349b8cf6a7370996e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Feb 2019 13:28:21 +0900 Subject: [PATCH 090/167] Move screen change allowance to local usage --- osu.Game/Screens/Select/MatchSongSelect.cs | 2 -- osu.Game/Screens/Select/PlaySongSelect.cs | 2 ++ osu.Game/Screens/Select/SongSelect.cs | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs index d7ff95be85..cfeaa1785e 100644 --- a/osu.Game/Screens/Select/MatchSongSelect.cs +++ b/osu.Game/Screens/Select/MatchSongSelect.cs @@ -17,8 +17,6 @@ namespace osu.Game.Screens.Select public string ShortTitle => "song selection"; public override string Title => ShortTitle.Humanize(); - public override bool AllowExternalScreenChange => false; - public MatchSongSelect() { Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING }; diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 7b3c3e0ec3..b5e9cd5f41 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -15,6 +15,8 @@ namespace osu.Game.Screens.Select private bool removeAutoModOnResume; private OsuScreen player; + public override bool AllowExternalScreenChange => true; + [BackgroundDependencyLoader] private void load(OsuColour colours) { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index caa95ec549..3be4dd8c0b 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -45,8 +45,6 @@ namespace osu.Game.Screens.Select protected virtual bool ShowFooter => true; - public override bool AllowExternalScreenChange => true; - /// /// Can be null if is false. /// From cf66fc69242c8eeec4b3bda4a39dd6aecf29018a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Feb 2019 13:29:07 +0900 Subject: [PATCH 091/167] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c1a77a17cd..38ed6870ff 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c6b3494d47..fb8a46992e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From a5f1f9830bfc0c13d3db751d65ea39bbba1e29e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Feb 2019 16:21:01 +0900 Subject: [PATCH 092/167] Fix potential schedule race case and regression in enum setting --- .../Direct/DownloadTrackingComposite.cs | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index f7d9264af0..d9eb827834 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -11,6 +11,9 @@ using osu.Game.Online.API.Requests; namespace osu.Game.Overlays.Direct { + /// + /// A component which tracks a beatmap through potential download/import/deletion. + /// public abstract class DownloadTrackingComposite : CompositeDrawable { public readonly Bindable BeatmapSet = new Bindable(); @@ -106,31 +109,22 @@ namespace osu.Game.Overlays.Direct } } - private void onRequestSuccess(string data) - { - Schedule(() => State.Value = DownloadState.Downloaded); - } + private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Downloaded); - private void onRequestProgress(float progress) - { - Schedule(() => Progress.Value = progress); - } + private void onRequestProgress(float progress) => Schedule(() => Progress.Value = progress); - private void onRequestFailure(Exception e) - { - Schedule(() => attachDownload(null)); - } + private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); - private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => setDownloadState(s, DownloadState.Downloaded); + private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable); - private void setRemoved(BeatmapSetInfo s) => setDownloadState(s, DownloadState.NotDownloaded); + private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded); - private void setDownloadState(BeatmapSetInfo s, DownloadState state) + private void setDownloadStateFromManager(BeatmapSetInfo s, DownloadState state) => Schedule(() => { if (s.OnlineBeatmapSetID != BeatmapSet.Value?.OnlineBeatmapSetID) return; - Schedule(() => State.Value = state); - } + State.Value = state; + }); } } From f50a0be29d63f5c5b049ab54c9583d4d1ed00ecc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Feb 2019 16:22:14 +0900 Subject: [PATCH 093/167] Add osu! difficulty calculator test --- .../OsuDifficultyCalculatorTest.cs | 32 ++++ .../Testing/Beatmaps/diffcalc-test.osu | 167 ++++++++++++++++++ .../Beatmaps/DifficultyCalculatorTest.cs | 44 +++++ 3 files changed, 243 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs create mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu create mode 100644 osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs new file mode 100644 index 0000000000..b8b5e3bb36 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -0,0 +1,32 @@ +// 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 NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Osu.Difficulty; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; + + [Test] + public new void Test() + { + base.Test(6.9311449688341344, "diffcalc-test"); + } + + private void openUsingShellExecute(string path) => Process.Start(new ProcessStartInfo + { + FileName = path, + UseShellExecute = true //see https://github.com/dotnet/corefx/issues/10361 + }); + + protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap); + } +} diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu new file mode 100644 index 0000000000..4b42cd4ffd --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu @@ -0,0 +1,167 @@ +osu file format v14 + +[General] +StackLeniency: 0.3 +Mode: 0 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +62500,-500,4,2,1,50,0,0 +71000,-100,4,2,1,50,0,0 + +[HitObjects] +// Circles spaced 1 beat apart, with increasing jump distance +126,112,500,5,0,0:0:0:0: +130,155,1000,1,0,0:0:0:0: +131,269,1500,1,0,0:0:0:0: +341,269,2000,1,0,0:0:0:0: +113,95,2500,1,0,0:0:0:0: + +// Circles spaced 1/2 beat apart, with increasing jump distance +108,104,3500,5,0,0:0:0:0: +110,145,3750,1,0,0:0:0:0: +115,262,4000,1,0,0:0:0:0: +285,265,4250,1,0,0:0:0:0: +458,48,4500,1,0,0:0:0:0: +35,199,4750,1,0,0:0:0:0: +251,340,5000,1,0,0:0:0:0: +20,352,5250,1,0,0:0:0:0: +426,62,5500,1,0,0:0:0:0: + +// Circles spaced 1/4 beat apart, with increasing jump distances +211,138,6500,5,0,0:0:0:0: +99,256,6625,1,0,0:0:0:0: +68,129,6750,1,0,0:0:0:0: +371,340,6875,1,0,0:0:0:0: +241,219,7000,1,0,0:0:0:0: +252,148,7125,1,0,0:0:0:0: +434,97,7250,1,0,0:0:0:0: +40,38,7375,1,0,0:0:0:0: +114,334,7500,1,0,0:0:0:0: +301,19,7625,1,0,0:0:0:0: +441,241,7750,1,0,0:0:0:0: +121,91,7875,1,0,0:0:0:0: +270,384,8000,1,0,0:0:0:0: +488,92,8125,1,0,0:0:0:0: +332,82,8250,1,0,0:0:0:0: +108,240,8375,1,0,0:0:0:0: +281,268,8500,1,0,0:0:0:0: + +// Constant spaced circles spaced 1/2 beat apart, small jump distances, changing angles +252,191,9500,5,0,0:0:0:0: +356,191,9750,1,0,0:0:0:0: +311,268,10000,1,0,0:0:0:0: +190,270,10250,1,0,0:0:0:0: +107,199,10500,1,0,0:0:0:0: +172,105,10750,1,0,0:0:0:0: +297,102,11000,1,0,0:0:0:0: +373,178,11250,1,0,0:0:0:0: +252,195,11500,1,0,0:0:0:0: + +// Constant spaced circles spaced 1/2 beat apart, large jump distances, changing angles +140,187,12500,5,0,0:0:0:0: +451,331,12750,1,0,0:0:0:0: +46,338,13000,1,0,0:0:0:0: +204,50,13250,1,0,0:0:0:0: +464,162,13500,1,0,0:0:0:0: +252,346,13750,1,0,0:0:0:0: +13,175,14000,1,0,0:0:0:0: +488,181,14250,1,0,0:0:0:0: +251,187,14500,1,0,0:0:0:0: + +// Constant spaced circles spaced 1/4 beat apart, small jump distances, changing angles +188,192,15500,5,0,0:0:0:0: +298,194,15625,1,0,0:0:0:0: +317,84,15750,1,0,0:0:0:0: +185,85,15875,1,0,0:0:0:0: +77,200,16000,1,0,0:0:0:0: +184,303,16125,1,0,0:0:0:0: +295,225,16250,1,0,0:0:0:0: +300,84,16375,1,0,0:0:0:0: +144,82,16500,1,0,0:0:0:0: +141,215,16625,1,0,0:0:0:0: +314,184,16750,1,0,0:0:0:0: +188,192,16875,1,0,0:0:0:0: +188,192,17000,1,0,0:0:0:0: + +// Constant spaced circles spaced 1/4 beat apart, large jump distances, changing angles +97,192,18000,5,0,0:0:0:0: +336,38,18125,1,0,0:0:0:0: +440,322,18250,1,0,0:0:0:0: +39,331,18375,1,0,0:0:0:0: +98,39,18500,1,0,0:0:0:0: +460,179,18625,1,0,0:0:0:0: +245,338,18750,1,0,0:0:0:0: +12,184,18875,1,0,0:0:0:0: +250,41,19000,1,0,0:0:0:0: +265,193,19125,1,0,0:0:0:0: +486,22,19250,1,0,0:0:0:0: +411,205,19375,1,0,0:0:0:0: +107,198,19500,1,0,0:0:0:0: + +// Short sliders spaced 1 beat apart +28,108,20500,2,0,L|196:107,1,160 +25,177,21500,2,0,L|193:176,1,160 +26,308,22500,2,0,L|194:307,1,160 +320,89,23500,2,0,L|488:88,1,160 + +// Short sliders spaced 1/2 beat apart +28,108,25000,6,0,L|196:107,1,160 +27,173,25750,2,0,L|195:172,1,160 +25,292,26500,2,0,L|193:291,1,160 +340,213,27250,2,0,L|508:212,1,160 +21,44,28000,2,0,L|189:43,1,160 + +// Short sliders spaced 1/4 beat apart +28,108,29500,6,0,L|196:107,1,160 +30,169,30125,2,0,L|198:168,1,160 +35,282,30750,2,0,L|203:281,1,160 +327,286,31375,2,0,L|495:285,1,160 +51,61,32000,2,0,L|219:60,1,160 + +// Large, medium-paced slider shapes +// PerfectCurve +66,86,33500,6,0,P|246:348|427:44,1,800 +66,86,36500,2,0,P|246:348|427:44,1,800 +66,86,39500,2,0,P|246:348|427:44,1,800 +// Linear +66,72,42500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120 +66,72,46500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120 +66,72,50500,2,0,B|419:65|419:65|66:316|66:316|426:318,1,1120 +// Bezier +76,287,54500,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640 +76,287,57000,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640 +76,287,59500,2,0,B|440:325|138:128|470:302|500:30|130:85|66:82,1,640 + +// Large slow slider with many ticks +81,170,62500,6,0,P|263:78|168:268,1,480 + +// Fast slider with many repeats +102,152,71000,6,0,L|175:153,18,64 + +// Slider-circle combos, spaced 1/2 beat apart +106,204,75500,6,0,P|275:33|171:304,1,800 +255,179,78250,1,0,0:0:0:0: +106,204,78500,2,0,P|275:33|171:304,1,800 +255,179,81250,1,0,0:0:0:0: +106,204,81500,2,0,P|275:33|171:304,1,800 + +// Circle-spinner combos, spaced 1/2 beat apart +82,69,85000,5,0,0:0:0:0: +256,192,85250,8,0,86000,0:0:0:0: +83,69,86250,5,0,0:0:0:0: +256,192,86500,12,0,87000,0:0:0:0: + +// Spinner-spinner combos, spaced 1/2 beat apart +256,192,88000,12,0,89000,0:0:0:0: +256,192,89250,12,0,90250,0:0:0:0: +256,192,90500,12,0,91500,0:0:0:0: +256,192,91750,12,0,92750,0:0:0:0: +256,192,93000,12,0,94000,0:0:0:0: diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs new file mode 100644 index 0000000000..c6a7ff7cf4 --- /dev/null +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -0,0 +1,44 @@ +// 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.Reflection; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Tests.Beatmaps +{ + [TestFixture] + public abstract class DifficultyCalculatorTest + { + private const string resource_namespace = "Testing.Beatmaps"; + + protected abstract string ResourceAssembly { get; } + + protected void Test(double expected, string name, params Mod[] mods) + => Assert.AreEqual(expected, CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating); + + private WorkingBeatmap getBeatmap(string name) + { + using (var resStream = openResource($"{resource_namespace}.{name}.osu")) + using (var stream = new StreamReader(resStream)) + { + var decoder = Decoder.GetDecoder(stream); + ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; + return new TestWorkingBeatmap(decoder.Decode(stream)); + } + } + + private Stream openResource(string name) + { + var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)); + return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}"); + } + + protected abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); + } +} From 777a606b2d82d084f8f40a1c0d76326bb5ca564b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Feb 2019 17:14:58 +0900 Subject: [PATCH 094/167] Don't revert beatmap on exiting leased state --- osu.Game/Screens/OsuScreenDependencies.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs index 1c355d6320..b51ce0d33f 100644 --- a/osu.Game/Screens/OsuScreenDependencies.cs +++ b/osu.Game/Screens/OsuScreenDependencies.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens Beatmap = parent.Get>()?.GetBoundCopy(); if (Beatmap == null) { - Cache(Beatmap = parent.Get>().BeginLease(true)); + Cache(Beatmap = parent.Get>().BeginLease(false)); } Ruleset = parent.Get>()?.GetBoundCopy(); @@ -38,4 +38,4 @@ namespace osu.Game.Screens } } } -} \ No newline at end of file +} From 1b61ec4ef402aa863671c552fc7f12aad2280517 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Feb 2019 18:05:23 +0900 Subject: [PATCH 095/167] First pass clean-up --- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 46 +++++++++++++++--------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index 8235a2d6a9..c7e43dc006 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -14,38 +14,50 @@ namespace osu.Game.Rulesets.Osu.Mods internal class OsuModGrow : Mod, IApplicableToDrawableHitObjects { public override string Name => "Grow"; + public override string Acronym => "GR"; + public override FontAwesome Icon => FontAwesome.fa_arrows_v; + public override ModType Type => ModType.Fun; + public override string Description => "Hit them at the right size!"; + public override double ScoreMultiplier => 1; public void ApplyToDrawableHitObjects(IEnumerable drawables) { foreach (var drawable in drawables) { - if (drawable is DrawableSpinner spinner) - return; - drawable.ApplyCustomUpdateState += applyCustomState; + switch (drawable) + { + case DrawableSpinner _: + continue; + default: + drawable.ApplyCustomUpdateState += ApplyCustomState; + break; + } } } - protected virtual void applyCustomState(DrawableHitObject drawable, ArmedState state) + protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state) { - var hitObject = (OsuHitObject) drawable.HitObject; + var h = (OsuHitObject)drawable.HitObject; - double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1; - double scaleDuration = hitObject.TimePreempt + 1; + var scale = drawable.Scale; + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + drawable.ScaleTo(scale / 2).Then().ScaleTo(scale, h.TimePreempt, Easing.OutSine); - var originalScale = drawable.Scale; - drawable.Scale /= 2; - - using (drawable.BeginAbsoluteSequence(appearTime, true)) - drawable.ScaleTo(originalScale, scaleDuration, Easing.OutSine); - - if (drawable is DrawableHitCircle circle) - using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) - circle.ApproachCircle.Hide(); + switch (drawable) + { + case DrawableHitCircle circle: + { + // we don't want to see the approach circle + using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + circle.ApproachCircle.Hide(); + break; + } + } } } -} \ No newline at end of file +} From 810175235d17adca7f44647dfa496b1993ff48bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Feb 2019 18:47:05 +0900 Subject: [PATCH 096/167] Fix incorrect application of scaling in some cases Isolates different usages of hitcircle scale so they can't ever cause regressions. --- osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs | 21 ++++-- .../Objects/Drawables/DrawableHitCircle.cs | 67 ++++++++++++------- 2 files changed, 59 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs index c7e43dc006..65e9eb7a1d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs @@ -44,19 +44,30 @@ namespace osu.Game.Rulesets.Osu.Mods { var h = (OsuHitObject)drawable.HitObject; - var scale = drawable.Scale; - using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) - drawable.ScaleTo(scale / 2).Then().ScaleTo(scale, h.TimePreempt, Easing.OutSine); + // apply grow effect + switch (drawable) + { + case DrawableSliderHead _: + case DrawableSliderTail _: + // special cases we should *not* be scaling. + break; + case DrawableSlider _: + case DrawableHitCircle _: + { + using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) + drawable.ScaleTo(0.5f).Then().ScaleTo(1, h.TimePreempt, Easing.OutSine); + break; + } + } + // remove approach circles switch (drawable) { case DrawableHitCircle circle: - { // we don't want to see the approach circle using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) circle.ApproachCircle.Hide(); break; - } } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index df0769982d..7dd2fa69ce 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osuTK; @@ -27,40 +28,58 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly IBindable stackHeightBindable = new Bindable(); private readonly IBindable scaleBindable = new Bindable(); + private readonly Container explodeContainer; + + private readonly Container scaleContainer; + public DrawableHitCircle(HitCircle h) : base(h) { Origin = Anchor.Centre; Position = HitObject.StackedPosition; - Scale = new Vector2(h.Scale); InternalChildren = new Drawable[] { - glow = new GlowPiece(), - circle = new CirclePiece + scaleContainer = new Container { - Hit = () => + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Child = explodeContainer = new Container { - if (AllJudged) - return false; + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Children = new Drawable[] + { + glow = new GlowPiece(), + circle = new CirclePiece + { + Hit = () => + { + if (AllJudged) + return false; - UpdateResult(true); - return true; - }, + UpdateResult(true); + return true; + }, + }, + number = new NumberPiece + { + Text = (HitObject.IndexInCurrentCombo + 1).ToString(), + }, + ring = new RingPiece(), + flash = new FlashPiece(), + explode = new ExplodePiece(), + ApproachCircle = new ApproachCircle + { + Alpha = 0, + Scale = new Vector2(4), + } + } + } }, - number = new NumberPiece - { - Text = (HitObject.IndexInCurrentCombo + 1).ToString(), - }, - ring = new RingPiece(), - flash = new FlashPiece(), - explode = new ExplodePiece(), - ApproachCircle = new ApproachCircle - { - Alpha = 0, - Scale = new Vector2(4), - } }; //may not be so correct @@ -72,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); - scaleBindable.BindValueChanged(v => Scale = new Vector2(v)); + scaleBindable.BindValueChanged(v => scaleContainer.Scale = new Vector2(v), true); positionBindable.BindTo(HitObject.PositionBindable); stackHeightBindable.BindTo(HitObject.StackHeightBindable); @@ -156,8 +175,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables circle.FadeOut(); number.FadeOut(); - this.FadeOut(800) - .ScaleTo(Scale * 1.5f, 400, Easing.OutQuad); + this.FadeOut(800); + explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad); } Expire(); From 1550908edbe5ea8e46ba2a7acca95060f14915cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Feb 2019 11:56:33 +0900 Subject: [PATCH 097/167] Add tooltip to key configuration button --- .../Graphics/UserInterface/TriangleButton.cs | 2 +- .../Sections/Input/KeyboardSettings.cs | 1 + osu.Game/Overlays/Settings/SettingsButton.cs | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs index 31fe29fc3a..2375017878 100644 --- a/osu.Game/Graphics/UserInterface/TriangleButton.cs +++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs @@ -27,7 +27,7 @@ namespace osu.Game.Graphics.UserInterface }); } - public IEnumerable FilterTerms => new[] { Text }; + public virtual IEnumerable FilterTerms => new[] { Text }; public bool MatchingFilter { diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs index efe3d782ae..3f1e77d482 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs @@ -16,6 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input new SettingsButton { Text = "Key configuration", + TooltipText = "Change global shortcut keys and gameplay bindings", Action = keyConfig.ToggleVisibility }, }; diff --git a/osu.Game/Overlays/Settings/SettingsButton.cs b/osu.Game/Overlays/Settings/SettingsButton.cs index 73cf855e18..ef98c28285 100644 --- a/osu.Game/Overlays/Settings/SettingsButton.cs +++ b/osu.Game/Overlays/Settings/SettingsButton.cs @@ -1,17 +1,33 @@ // 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 System.Linq; using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { - public class SettingsButton : TriangleButton + public class SettingsButton : TriangleButton, IHasTooltip { public SettingsButton() { RelativeSizeAxes = Axes.X; Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS }; } + + public string TooltipText { get; set; } + + public override IEnumerable FilterTerms + { + get + { + if (TooltipText != null) + return base.FilterTerms.Append(TooltipText); + + return base.FilterTerms; + } + } } } From 8becd7ff92dbc82b451100ea7862e58bcb7badb4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 Feb 2019 12:49:48 +0900 Subject: [PATCH 098/167] Add a slider-spinner test case --- .../OsuDifficultyCalculatorTest.cs | 2 +- .../Resources/Testing/Beatmaps/diffcalc-test.osu | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index b8b5e3bb36..5f290886c2 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public new void Test() { - base.Test(6.9311449688341344, "diffcalc-test"); + base.Test(6.931145117263422d, "diffcalc-test"); } private void openUsingShellExecute(string path) => Process.Start(new ProcessStartInfo diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu index 4b42cd4ffd..bf345811a2 100644 --- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu @@ -156,7 +156,7 @@ SliderTickRate:1 // Circle-spinner combos, spaced 1/2 beat apart 82,69,85000,5,0,0:0:0:0: 256,192,85250,8,0,86000,0:0:0:0: -83,69,86250,5,0,0:0:0:0: +384,189,86250,5,0,0:0:0:0: 256,192,86500,12,0,87000,0:0:0:0: // Spinner-spinner combos, spaced 1/2 beat apart @@ -165,3 +165,15 @@ SliderTickRate:1 256,192,90500,12,0,91500,0:0:0:0: 256,192,91750,12,0,92750,0:0:0:0: 256,192,93000,12,0,94000,0:0:0:0: + +// Slider-spinner combos, spaced 1/2 beat apart +49,89,95000,6,0,L|214:87,1,160 +256,192,95625,12,0,96500,0:0:0:0: +12,299,96625,6,0,L|177:297,1,160 +256,192,97250,12,0,98125,0:0:0:0: +295,107,98250,6,0,L|460:105,1,160 +256,192,98875,12,0,99750,0:0:0:0: +279,325,99875,6,0,L|444:323,1,160 +256,192,100500,12,0,101375,0:0:0:0: +197,197,101500,6,0,L|362:195,1,160 +256,192,102125,12,0,103000,0:0:0:0: From 490d48fa5e106b88b5aac1952812689ec444c889 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Feb 2019 13:47:26 +0900 Subject: [PATCH 099/167] Fix MultiplayerTestCase not being abstract --- osu.Game/Tests/Visual/MultiplayerTestCase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/MultiplayerTestCase.cs b/osu.Game/Tests/Visual/MultiplayerTestCase.cs index 6efdddbfee..7e2f915179 100644 --- a/osu.Game/Tests/Visual/MultiplayerTestCase.cs +++ b/osu.Game/Tests/Visual/MultiplayerTestCase.cs @@ -7,7 +7,7 @@ using osu.Game.Online.Multiplayer; namespace osu.Game.Tests.Visual { - public class MultiplayerTestCase : OsuTestCase + public abstract class MultiplayerTestCase : OsuTestCase { [Cached] private readonly Bindable currentRoom = new Bindable(new Room()); From 280081d58938cd3294a59d63fc6645aed26eaad2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 Feb 2019 14:42:42 +0900 Subject: [PATCH 100/167] Fix beatmap ruleset not being set --- .../OsuDifficultyCalculatorTest.cs | 2 ++ osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 5f290886c2..7f6591ffcf 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -28,5 +28,7 @@ namespace osu.Game.Rulesets.Osu.Tests }); protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap); + + protected override Ruleset CreateRuleset() => new OsuRuleset(); } } diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index c6a7ff7cf4..108fa8ff71 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -7,6 +7,7 @@ using System.Reflection; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -29,7 +30,11 @@ namespace osu.Game.Tests.Beatmaps { var decoder = Decoder.GetDecoder(stream); ((LegacyBeatmapDecoder)decoder).ApplyOffsets = false; - return new TestWorkingBeatmap(decoder.Decode(stream)); + + var working = new TestWorkingBeatmap(decoder.Decode(stream)); + working.BeatmapInfo.Ruleset = CreateRuleset().RulesetInfo; + + return working; } } @@ -40,5 +45,7 @@ namespace osu.Game.Tests.Beatmaps } protected abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); + + protected abstract Ruleset CreateRuleset(); } } From c3138db390580eec48530eaf67aa1fa9e16c80a3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 Feb 2019 14:42:52 +0900 Subject: [PATCH 101/167] Cleanup osu difficulty test --- .../OsuDifficultyCalculatorTest.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 7f6591ffcf..4926a81034 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.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.Diagnostics; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; @@ -15,18 +14,12 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [Test] - public new void Test() + [TestCase(6.931145117263422, "diffcalc-test")] + public void Test(double expected, string name) { - base.Test(6.931145117263422d, "diffcalc-test"); + base.Test(expected, name); } - private void openUsingShellExecute(string path) => Process.Start(new ProcessStartInfo - { - FileName = path, - UseShellExecute = true //see https://github.com/dotnet/corefx/issues/10361 - }); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap); protected override Ruleset CreateRuleset() => new OsuRuleset(); From aa0bb7ca1109e965ddd19f0bb3d59221a70d386f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 Feb 2019 14:44:26 +0900 Subject: [PATCH 102/167] Add taiko difficulty calculator tests --- .../TaikoDifficultyCalculatorTest.cs | 27 ++ .../Testing/Beatmaps/diffcalc-test-strong.osu | 257 ++++++++++++++++ .../Testing/Beatmaps/diffcalc-test.osu | 285 ++++++++++++++++++ 3 files changed, 569 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs create mode 100644 osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test-strong.osu create mode 100644 osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test.osu diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs new file mode 100644 index 0000000000..112834e84c --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Taiko.Difficulty; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TaikoDifficultyCalculatorTest : DifficultyCalculatorTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; + + [TestCase(2.9811336589467095, "diffcalc-test")] + [TestCase(2.9811336589467095, "diffcalc-test-strong")] + public void Test(double expected, string name) + { + base.Test(expected, name); + } + + protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap); + + protected override Ruleset CreateRuleset() => new TaikoRuleset(); + } +} diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test-strong.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test-strong.osu new file mode 100644 index 0000000000..33510eceb7 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test-strong.osu @@ -0,0 +1,257 @@ +osu file format v14 + +[General] +Mode: 1 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +62500,-500,4,2,1,50,0,0 +71000,-100,4,2,1,50,0,0 + +[HitObjects] +// Same as diffcalc-test with finishers on every note +142,122,0,5,4,0:0:0:0: +142,122,125,1,4,0:0:0:0: +142,122,250,1,4,0:0:0:0: +142,122,375,1,4,0:0:0:0: +142,122,500,1,4,0:0:0:0: +142,122,625,1,4,0:0:0:0: +142,122,750,1,4,0:0:0:0: +142,122,875,1,4,0:0:0:0: +142,122,1000,1,4,0:0:0:0: +142,122,1125,1,4,0:0:0:0: +142,122,1250,1,4,0:0:0:0: +142,122,1375,1,4,0:0:0:0: +142,122,1500,1,4,0:0:0:0: +119,106,2500,1,6,0:0:0:0: +119,106,2625,1,6,0:0:0:0: +119,106,2750,1,6,0:0:0:0: +119,106,2875,1,6,0:0:0:0: +119,106,3000,1,6,0:0:0:0: +119,106,3125,1,6,0:0:0:0: +119,106,3250,1,6,0:0:0:0: +119,106,3375,1,6,0:0:0:0: +119,106,3500,1,6,0:0:0:0: +119,106,3625,1,6,0:0:0:0: +119,106,3750,1,6,0:0:0:0: +119,106,3875,1,6,0:0:0:0: +119,106,4000,1,6,0:0:0:0: +136,90,5000,1,4,0:0:0:0: +136,90,5125,1,6,0:0:0:0: +136,90,5250,1,4,0:0:0:0: +136,90,5375,1,6,0:0:0:0: +136,90,5500,1,4,0:0:0:0: +136,90,5625,1,6,0:0:0:0: +136,90,5750,1,4,0:0:0:0: +136,90,5875,1,6,0:0:0:0: +136,90,6000,1,4,0:0:0:0: +136,90,6125,1,6,0:0:0:0: +136,90,6250,1,4,0:0:0:0: +136,90,6375,1,6,0:0:0:0: +136,90,6500,1,4,0:0:0:0: +86,113,7500,1,4,0:0:0:0: +86,113,7625,1,4,0:0:0:0: +86,113,7750,1,6,0:0:0:0: +86,113,7875,1,6,0:0:0:0: +86,113,8000,1,4,0:0:0:0: +86,113,8125,1,4,0:0:0:0: +86,113,8250,1,6,0:0:0:0: +86,113,8375,1,6,0:0:0:0: +86,113,8500,1,4,0:0:0:0: +86,113,8625,1,4,0:0:0:0: +86,113,8750,1,6,0:0:0:0: +86,113,8875,1,6,0:0:0:0: +86,113,9000,1,4,0:0:0:0: +146,90,10000,1,4,0:0:0:0: +146,90,10125,1,4,0:0:0:0: +146,90,10250,1,4,0:0:0:0: +146,90,10375,1,6,0:0:0:0: +146,90,10500,1,6,0:0:0:0: +146,90,10625,1,6,0:0:0:0: +146,90,10750,1,4,0:0:0:0: +146,90,10875,1,4,0:0:0:0: +146,90,11000,1,4,0:0:0:0: +146,90,11125,1,6,0:0:0:0: +146,90,11250,1,6,0:0:0:0: +146,90,11375,1,6,0:0:0:0: +146,90,11500,1,4,0:0:0:0: +146,90,11625,1,4,0:0:0:0: +146,90,11750,1,4,0:0:0:0: +146,90,11875,1,6,0:0:0:0: +146,90,12000,1,6,0:0:0:0: +146,90,12125,1,6,0:0:0:0: +146,90,12250,1,4,0:0:0:0: +146,90,12375,1,4,0:0:0:0: +146,90,12500,1,4,0:0:0:0: +69,99,13500,1,4,0:0:0:0: +69,99,13625,1,4,0:0:0:0: +69,99,13750,1,4,0:0:0:0: +69,99,13875,1,6,0:0:0:0: +69,99,14000,1,4,0:0:0:0: +69,99,14125,1,4,0:0:0:0: +69,99,14250,1,4,0:0:0:0: +69,99,14375,1,6,0:0:0:0: +69,99,14500,1,4,0:0:0:0: +69,99,14625,1,4,0:0:0:0: +69,99,14750,1,4,0:0:0:0: +69,99,14875,1,6,0:0:0:0: +69,99,15000,1,4,0:0:0:0: +69,99,15125,1,4,0:0:0:0: +69,99,15250,1,4,0:0:0:0: +69,99,15375,1,6,0:0:0:0: +69,99,15500,1,4,0:0:0:0: +83,89,16500,1,4,0:0:0:0: +83,89,16625,1,6,0:0:0:0: +83,89,16750,1,6,0:0:0:0: +83,89,16875,1,4,0:0:0:0: +83,89,17000,1,4,0:0:0:0: +83,89,17125,1,4,0:0:0:0: +83,89,17250,1,6,0:0:0:0: +83,89,17375,1,6,0:0:0:0: +83,89,17500,1,6,0:0:0:0: +83,89,17625,1,6,0:0:0:0: +83,89,17750,1,4,0:0:0:0: +83,89,17875,1,4,0:0:0:0: +83,89,18000,1,4,0:0:0:0: +83,89,18125,1,4,0:0:0:0: +83,89,18250,1,4,0:0:0:0: +83,89,18375,1,6,0:0:0:0: +83,89,18500,1,6,0:0:0:0: +83,89,18625,1,6,0:0:0:0: +83,89,18750,1,6,0:0:0:0: +83,89,18875,1,4,0:0:0:0: +83,89,19000,1,4,0:0:0:0: +83,89,19125,1,4,0:0:0:0: +83,89,19250,1,4,0:0:0:0: +83,89,19375,1,6,0:0:0:0: +83,89,19500,1,6,0:0:0:0: +83,89,19625,1,4,0:0:0:0: +84,122,20500,1,4,0:0:0:0: +84,122,20625,2,4,L|217:123,1,120 +84,122,21125,1,4,0:0:0:0: +84,122,21250,2,4,L|217:123,1,120 +84,122,21750,1,4,0:0:0:0: +84,122,21875,2,4,L|217:123,1,120 +84,122,22375,1,4,0:0:0:0: +84,122,22500,2,4,L|217:123,1,120 +84,122,23000,1,4,0:0:0:0: +84,122,23125,2,4,L|217:123,1,120 +99,106,24500,1,4,0:0:0:0: +99,106,24625,1,4,0:0:0:0: +99,106,24750,2,4,L|194:107,1,80 +99,106,25125,1,4,0:0:0:0: +99,106,25250,1,4,0:0:0:0: +99,106,25375,2,4,L|194:107,1,80 +99,106,25750,1,4,0:0:0:0: +99,106,25875,1,4,0:0:0:0: +99,106,26000,2,4,L|194:107,1,80 +99,106,26375,1,4,0:0:0:0: +99,106,26500,1,4,0:0:0:0: +99,106,26625,2,4,L|194:107,1,80 +99,106,27000,1,4,0:0:0:0: +99,106,27125,1,4,0:0:0:0: +99,106,27250,2,4,L|194:107,1,80 +121,103,28500,1,4,0:0:0:0: +121,103,28625,1,4,0:0:0:0: +121,103,28750,1,4,0:0:0:0: +121,103,28875,2,4,L|190:103,1,40 +121,103,29125,1,4,0:0:0:0: +121,103,29250,1,4,0:0:0:0: +121,103,29375,1,4,0:0:0:0: +121,103,29500,2,4,L|190:103,1,40 +121,103,29750,1,4,0:0:0:0: +121,103,29875,1,4,0:0:0:0: +121,103,30000,1,4,0:0:0:0: +121,103,30125,2,4,L|190:103,1,40 +121,103,30375,1,4,0:0:0:0: +121,103,30500,1,4,0:0:0:0: +121,103,30625,1,4,0:0:0:0: +121,103,30750,2,4,L|190:103,1,40 +121,103,31000,1,4,0:0:0:0: +121,103,31125,1,4,0:0:0:0: +121,103,31250,1,4,0:0:0:0: +121,103,31375,2,4,L|190:103,1,40 +121,103,32500,1,4,0:0:0:0: +121,103,32625,1,6,0:0:0:0: +121,103,32750,1,4,0:0:0:0: +121,103,32875,2,4,L|190:103,1,40 +121,103,33125,1,4,0:0:0:0: +121,103,33250,1,6,0:0:0:0: +121,103,33375,1,4,0:0:0:0: +121,103,33500,2,4,L|190:103,1,40 +121,103,33750,1,4,0:0:0:0: +121,103,33875,1,6,0:0:0:0: +121,103,34000,1,4,0:0:0:0: +121,103,34125,2,4,L|190:103,1,40 +121,103,34375,1,4,0:0:0:0: +121,103,34500,1,6,0:0:0:0: +121,103,34625,1,4,0:0:0:0: +121,103,34750,2,4,L|190:103,1,40 +121,103,35000,1,4,0:0:0:0: +121,103,35125,1,6,0:0:0:0: +121,103,35250,1,4,0:0:0:0: +121,103,35375,2,4,L|190:103,1,40 +121,103,36500,1,4,0:0:0:0: +121,103,36625,1,4,0:0:0:0: +121,103,36750,1,6,0:0:0:0: +121,103,36875,2,4,L|190:103,1,40 +121,103,37125,1,4,0:0:0:0: +121,103,37250,1,4,0:0:0:0: +121,103,37375,1,6,0:0:0:0: +121,103,37500,2,4,L|190:103,1,40 +121,103,37750,1,4,0:0:0:0: +121,103,37875,1,4,0:0:0:0: +121,103,38000,1,6,0:0:0:0: +121,103,38125,2,4,L|190:103,1,40 +121,103,38375,1,4,0:0:0:0: +121,103,38500,1,4,0:0:0:0: +121,103,38625,1,6,0:0:0:0: +121,103,38750,2,4,L|190:103,1,40 +121,103,39000,1,4,0:0:0:0: +121,103,39125,1,4,0:0:0:0: +121,103,39250,1,6,0:0:0:0: +121,103,39375,2,4,L|190:103,1,40 +107,106,40500,1,4,0:0:0:0: +107,106,40625,1,4,0:0:0:0: +107,106,40750,1,6,0:0:0:0: +107,106,40875,1,6,0:0:0:0: +46,112,41000,2,4,L|214:112,1,160 +107,106,41625,1,4,0:0:0:0: +107,106,41750,1,4,0:0:0:0: +107,106,41875,1,6,0:0:0:0: +107,106,42000,1,6,0:0:0:0: +46,112,42125,2,4,L|214:112,1,160 +107,106,42750,1,4,0:0:0:0: +107,106,42875,1,4,0:0:0:0: +107,106,43000,1,6,0:0:0:0: +107,106,43125,1,6,0:0:0:0: +46,112,43250,2,4,L|214:112,1,160 +107,106,43875,1,4,0:0:0:0: +107,106,44000,1,4,0:0:0:0: +107,106,44125,1,6,0:0:0:0: +107,106,44250,1,6,0:0:0:0: +46,112,44375,2,4,L|214:112,1,160 +107,106,45000,1,4,0:0:0:0: +107,106,45125,1,4,0:0:0:0: +107,106,45250,1,6,0:0:0:0: +107,106,45375,1,6,0:0:0:0: +46,112,45500,2,4,L|214:112,1,160 +256,192,47000,12,4,47500,0:0:0:0: +256,192,47625,12,4,48000,0:0:0:0: +256,192,48125,12,4,48500,0:0:0:0: +256,192,48625,12,4,49000,0:0:0:0: +256,192,50000,12,4,50500,0:0:0:0: +183,143,50625,5,4,0:0:0:0: +256,192,50750,12,4,51250,0:0:0:0: +114,106,51375,5,4,0:0:0:0: +256,192,51625,12,4,52125,0:0:0:0: +154,143,52250,5,4,0:0:0:0: +256,192,52375,12,4,52875,0:0:0:0: +116,111,53000,5,4,0:0:0:0: diff --git a/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test.osu new file mode 100644 index 0000000000..15326162ea --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Resources/Testing/Beatmaps/diffcalc-test.osu @@ -0,0 +1,285 @@ +osu file format v14 + +[General] +Mode: 1 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +62500,-500,4,2,1,50,0,0 +71000,-100,4,2,1,50,0,0 + +[HitObjects] +// dd, spaced 1/4 beat apart +142,122,0,5,0,0:0:0:0: +142,122,125,1,0,0:0:0:0: +142,122,250,1,0,0:0:0:0: +142,122,375,1,0,0:0:0:0: +142,122,500,1,0,0:0:0:0: +142,122,625,1,0,0:0:0:0: +142,122,750,1,0,0:0:0:0: +142,122,875,1,0,0:0:0:0: +142,122,1000,1,0,0:0:0:0: +142,122,1125,1,0,0:0:0:0: +142,122,1250,1,0,0:0:0:0: +142,122,1375,1,0,0:0:0:0: +142,122,1500,1,0,0:0:0:0: + +// kk, spaced 1/4 beat apart +119,106,2500,1,2,0:0:0:0: +119,106,2625,1,2,0:0:0:0: +119,106,2750,1,2,0:0:0:0: +119,106,2875,1,2,0:0:0:0: +119,106,3000,1,2,0:0:0:0: +119,106,3125,1,2,0:0:0:0: +119,106,3250,1,2,0:0:0:0: +119,106,3375,1,2,0:0:0:0: +119,106,3500,1,2,0:0:0:0: +119,106,3625,1,2,0:0:0:0: +119,106,3750,1,2,0:0:0:0: +119,106,3875,1,2,0:0:0:0: +119,106,4000,1,2,0:0:0:0: + +// dk, spaced 1/4 beat apart +136,90,5000,1,0,0:0:0:0: +136,90,5125,1,2,0:0:0:0: +136,90,5250,1,0,0:0:0:0: +136,90,5375,1,2,0:0:0:0: +136,90,5500,1,0,0:0:0:0: +136,90,5625,1,2,0:0:0:0: +136,90,5750,1,0,0:0:0:0: +136,90,5875,1,2,0:0:0:0: +136,90,6000,1,0,0:0:0:0: +136,90,6125,1,2,0:0:0:0: +136,90,6250,1,0,0:0:0:0: +136,90,6375,1,2,0:0:0:0: +136,90,6500,1,0,0:0:0:0: + +// ddkk, spaced 1/4 beat apart +86,113,7500,1,0,0:0:0:0: +86,113,7625,1,0,0:0:0:0: +86,113,7750,1,2,0:0:0:0: +86,113,7875,1,2,0:0:0:0: +86,113,8000,1,0,0:0:0:0: +86,113,8125,1,0,0:0:0:0: +86,113,8250,1,2,0:0:0:0: +86,113,8375,1,2,0:0:0:0: +86,113,8500,1,0,0:0:0:0: +86,113,8625,1,0,0:0:0:0: +86,113,8750,1,2,0:0:0:0: +86,113,8875,1,2,0:0:0:0: +86,113,9000,1,0,0:0:0:0: + +// dddkkk, spaced 1/4 beat apart +146,90,10000,1,0,0:0:0:0: +146,90,10125,1,0,0:0:0:0: +146,90,10250,1,0,0:0:0:0: +146,90,10375,1,2,0:0:0:0: +146,90,10500,1,2,0:0:0:0: +146,90,10625,1,2,0:0:0:0: +146,90,10750,1,0,0:0:0:0: +146,90,10875,1,0,0:0:0:0: +146,90,11000,1,0,0:0:0:0: +146,90,11125,1,2,0:0:0:0: +146,90,11250,1,2,0:0:0:0: +146,90,11375,1,2,0:0:0:0: +146,90,11500,1,0,0:0:0:0: +146,90,11625,1,0,0:0:0:0: +146,90,11750,1,0,0:0:0:0: +146,90,11875,1,2,0:0:0:0: +146,90,12000,1,2,0:0:0:0: +146,90,12125,1,2,0:0:0:0: +146,90,12250,1,0,0:0:0:0: +146,90,12375,1,0,0:0:0:0: +146,90,12500,1,0,0:0:0:0: + +// dddk, spaced 1/4 beat apart +69,99,13500,1,0,0:0:0:0: +69,99,13625,1,0,0:0:0:0: +69,99,13750,1,0,0:0:0:0: +69,99,13875,1,2,0:0:0:0: +69,99,14000,1,0,0:0:0:0: +69,99,14125,1,0,0:0:0:0: +69,99,14250,1,0,0:0:0:0: +69,99,14375,1,2,0:0:0:0: +69,99,14500,1,0,0:0:0:0: +69,99,14625,1,0,0:0:0:0: +69,99,14750,1,0,0:0:0:0: +69,99,14875,1,2,0:0:0:0: +69,99,15000,1,0,0:0:0:0: +69,99,15125,1,0,0:0:0:0: +69,99,15250,1,0,0:0:0:0: +69,99,15375,1,2,0:0:0:0: +69,99,15500,1,0,0:0:0:0: + +// arbitrary pattern, spaced 1/4 beat apart +83,89,16500,1,0,0:0:0:0: +83,89,16625,1,2,0:0:0:0: +83,89,16750,1,2,0:0:0:0: +83,89,16875,1,0,0:0:0:0: +83,89,17000,1,0,0:0:0:0: +83,89,17125,1,0,0:0:0:0: +83,89,17250,1,2,0:0:0:0: +83,89,17375,1,2,0:0:0:0: +83,89,17500,1,2,0:0:0:0: +83,89,17625,1,2,0:0:0:0: +83,89,17750,1,0,0:0:0:0: +83,89,17875,1,0,0:0:0:0: +83,89,18000,1,0,0:0:0:0: +83,89,18125,1,0,0:0:0:0: +83,89,18250,1,0,0:0:0:0: +83,89,18375,1,2,0:0:0:0: +83,89,18500,1,2,0:0:0:0: +83,89,18625,1,2,0:0:0:0: +83,89,18750,1,2,0:0:0:0: +83,89,18875,1,0,0:0:0:0: +83,89,19000,1,0,0:0:0:0: +83,89,19125,1,0,0:0:0:0: +83,89,19250,1,0,0:0:0:0: +83,89,19375,1,2,0:0:0:0: +83,89,19500,1,2,0:0:0:0: +83,89,19625,1,0,0:0:0:0: + +// d-slider pattern, spaced 1/4 beat apart +84,122,20500,1,0,0:0:0:0: +84,122,20625,2,0,L|217:123,1,120 +84,122,21125,1,0,0:0:0:0: +84,122,21250,2,0,L|217:123,1,120 +84,122,21750,1,0,0:0:0:0: +84,122,21875,2,0,L|217:123,1,120 +84,122,22375,1,0,0:0:0:0: +84,122,22500,2,0,L|217:123,1,120 +84,122,23000,1,0,0:0:0:0: +84,122,23125,2,0,L|217:123,1,120 + +// dd-slider pattern, spaced 1/4 beat apart +99,106,24500,1,0,0:0:0:0: +99,106,24625,1,0,0:0:0:0: +99,106,24750,2,0,L|194:107,1,80 +99,106,25125,1,0,0:0:0:0: +99,106,25250,1,0,0:0:0:0: +99,106,25375,2,0,L|194:107,1,80 +99,106,25750,1,0,0:0:0:0: +99,106,25875,1,0,0:0:0:0: +99,106,26000,2,0,L|194:107,1,80 +99,106,26375,1,0,0:0:0:0: +99,106,26500,1,0,0:0:0:0: +99,106,26625,2,0,L|194:107,1,80 +99,106,27000,1,0,0:0:0:0: +99,106,27125,1,0,0:0:0:0: +99,106,27250,2,0,L|194:107,1,80 + +// ddd-slider pattern, spaced 1/4 beat apart +121,103,28500,1,0,0:0:0:0: +121,103,28625,1,0,0:0:0:0: +121,103,28750,1,0,0:0:0:0: +121,103,28875,2,0,L|190:103,1,40 +121,103,29125,1,0,0:0:0:0: +121,103,29250,1,0,0:0:0:0: +121,103,29375,1,0,0:0:0:0: +121,103,29500,2,0,L|190:103,1,40 +121,103,29750,1,0,0:0:0:0: +121,103,29875,1,0,0:0:0:0: +121,103,30000,1,0,0:0:0:0: +121,103,30125,2,0,L|190:103,1,40 +121,103,30375,1,0,0:0:0:0: +121,103,30500,1,0,0:0:0:0: +121,103,30625,1,0,0:0:0:0: +121,103,30750,2,0,L|190:103,1,40 +121,103,31000,1,0,0:0:0:0: +121,103,31125,1,0,0:0:0:0: +121,103,31250,1,0,0:0:0:0: +121,103,31375,2,0,L|190:103,1,40 + +// dkd-slider pattern, spaced 1/4 beat apart +121,103,32500,1,0,0:0:0:0: +121,103,32625,1,2,0:0:0:0: +121,103,32750,1,0,0:0:0:0: +121,103,32875,2,0,L|190:103,1,40 +121,103,33125,1,0,0:0:0:0: +121,103,33250,1,2,0:0:0:0: +121,103,33375,1,0,0:0:0:0: +121,103,33500,2,0,L|190:103,1,40 +121,103,33750,1,0,0:0:0:0: +121,103,33875,1,2,0:0:0:0: +121,103,34000,1,0,0:0:0:0: +121,103,34125,2,0,L|190:103,1,40 +121,103,34375,1,0,0:0:0:0: +121,103,34500,1,2,0:0:0:0: +121,103,34625,1,0,0:0:0:0: +121,103,34750,2,0,L|190:103,1,40 +121,103,35000,1,0,0:0:0:0: +121,103,35125,1,2,0:0:0:0: +121,103,35250,1,0,0:0:0:0: +121,103,35375,2,0,L|190:103,1,40 + +//ddk-slider pattern, spaced 1/4 beat apart +121,103,36500,1,0,0:0:0:0: +121,103,36625,1,0,0:0:0:0: +121,103,36750,1,2,0:0:0:0: +121,103,36875,2,0,L|190:103,1,40 +121,103,37125,1,0,0:0:0:0: +121,103,37250,1,0,0:0:0:0: +121,103,37375,1,2,0:0:0:0: +121,103,37500,2,0,L|190:103,1,40 +121,103,37750,1,0,0:0:0:0: +121,103,37875,1,0,0:0:0:0: +121,103,38000,1,2,0:0:0:0: +121,103,38125,2,0,L|190:103,1,40 +121,103,38375,1,0,0:0:0:0: +121,103,38500,1,0,0:0:0:0: +121,103,38625,1,2,0:0:0:0: +121,103,38750,2,0,L|190:103,1,40 +121,103,39000,1,0,0:0:0:0: +121,103,39125,1,0,0:0:0:0: +121,103,39250,1,2,0:0:0:0: +121,103,39375,2,0,L|190:103,1,40 + +//ddkk-slider pattern, spaced 1/4 beat apart +107,106,40500,1,0,0:0:0:0: +107,106,40625,1,0,0:0:0:0: +107,106,40750,1,2,0:0:0:0: +107,106,40875,1,2,0:0:0:0: +46,112,41000,2,0,L|214:112,1,160 +107,106,41625,1,0,0:0:0:0: +107,106,41750,1,0,0:0:0:0: +107,106,41875,1,2,0:0:0:0: +107,106,42000,1,2,0:0:0:0: +46,112,42125,2,0,L|214:112,1,160 +107,106,42750,1,0,0:0:0:0: +107,106,42875,1,0,0:0:0:0: +107,106,43000,1,2,0:0:0:0: +107,106,43125,1,2,0:0:0:0: +46,112,43250,2,0,L|214:112,1,160 +107,106,43875,1,0,0:0:0:0: +107,106,44000,1,0,0:0:0:0: +107,106,44125,1,2,0:0:0:0: +107,106,44250,1,2,0:0:0:0: +46,112,44375,2,0,L|214:112,1,160 +107,106,45000,1,0,0:0:0:0: +107,106,45125,1,0,0:0:0:0: +107,106,45250,1,2,0:0:0:0: +107,106,45375,1,2,0:0:0:0: +46,112,45500,2,0,L|214:112,1,160 + +// spinner-spinner pattern, spaced 1/4 beat apart +256,192,47000,12,0,47500,0:0:0:0: +256,192,47625,12,0,48000,0:0:0:0: +256,192,48125,12,0,48500,0:0:0:0: +256,192,48625,12,0,49000,0:0:0:0: + +// spinner-d pattern, spaced 1/4 beat apart +256,192,50000,12,0,50500,0:0:0:0: +183,143,50625,5,0,0:0:0:0: +256,192,50750,12,0,51250,0:0:0:0: +114,106,51375,5,0,0:0:0:0: +256,192,51625,12,0,52125,0:0:0:0: +154,143,52250,5,0,0:0:0:0: +256,192,52375,12,0,52875,0:0:0:0: +116,111,53000,5,0,0:0:0:0: From 09e717d2198925d79aaffcccad8d408bd1c2c18e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 Feb 2019 15:49:51 +0900 Subject: [PATCH 103/167] Add catch difficulty calculator tests --- .../CatchDifficultyCalculatorTest.cs | 24 +++ .../Testing/Beatmaps/diffcalc-test.osu | 138 ++++++++++++++++++ .../OsuDifficultyCalculatorTest.cs | 4 +- .../TaikoDifficultyCalculatorTest.cs | 4 +- 4 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs new file mode 100644 index 0000000000..91bc537902 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -0,0 +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 NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Difficulty; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class CatchDifficultyCalculatorTest : DifficultyCalculatorTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; + + [TestCase(3.8664391043534758, "diffcalc-test")] + public void Test(double expected, string name) + => base.Test(expected, name); + + protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap); + + protected override Ruleset CreateRuleset() => new CatchRuleset(); + } +} diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu new file mode 100644 index 0000000000..ebad654404 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu @@ -0,0 +1,138 @@ +osu file format v14 + +[General] +StackLeniency: 0.3 +Mode: 2 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +34500,-50,4,2,1,50,0,0 + +[HitObjects] +// fruits spaced 1/1 beat apart +32,128,0,5,0,0:0:0:0: +96,128,500,1,0,0:0:0:0: +160,128,1000,1,0,0:0:0:0: +224,128,1500,1,0,0:0:0:0: +288,128,2000,1,0,0:0:0:0: +352,128,2500,1,0,0:0:0:0: +416,128,3000,1,0,0:0:0:0: +480,128,3500,1,0,0:0:0:0: + +// fruits spaced 1/2 beat apart +32,160,4500,1,0,0:0:0:0: +64,160,4750,1,0,0:0:0:0: +96,160,5000,1,0,0:0:0:0: +128,160,5250,1,0,0:0:0:0: +160,160,5500,1,0,0:0:0:0: +192,160,5750,1,0,0:0:0:0: +224,160,6000,1,0,0:0:0:0: +256,160,6250,1,0,0:0:0:0: +288,160,6500,1,0,0:0:0:0: + +// fruits spaced 1/4 beat apart +96,128,7500,1,0,0:0:0:0: +128,128,7625,1,0,0:0:0:0: +160,128,7750,1,0,0:0:0:0: +192,128,7875,1,0,0:0:0:0: +224,128,8000,1,0,0:0:0:0: +256,128,8125,1,0,0:0:0:0: +288,128,8250,1,0,0:0:0:0: +320,128,8375,1,0,0:0:0:0: +352,128,8500,1,0,0:0:0:0: + +// fruit hyperdashes, spaced 1/2 beat apart +32,160,9500,1,0,0:0:0:0: +480,160,9750,1,0,0:0:0:0: +32,160,10000,1,0,0:0:0:0: +480,160,10250,1,0,0:0:0:0: +32,160,10500,1,0,0:0:0:0: +480,160,10750,1,0,0:0:0:0: +32,160,11000,1,0,0:0:0:0: + +// fruit hyperdashes, spaced 1/4 beat apart +32,192,12000,1,0,0:0:0:0: +480,192,12125,1,0,0:0:0:0: +32,192,12250,1,0,0:0:0:0: +480,192,12375,1,0,0:0:0:0: +32,192,12500,1,0,0:0:0:0: +480,192,12625,1,0,0:0:0:0: +32,192,12750,1,0,0:0:0:0: +480,192,12875,1,0,0:0:0:0: +32,192,13000,1,0,0:0:0:0: + +// stream + hyperdash + stream, spaced 1/4 beat apart +32,192,14000,1,0,0:0:0:0: +64,192,14125,1,0,0:0:0:0: +96,192,14250,1,0,0:0:0:0: +128,192,14375,1,0,0:0:0:0: +480,192,14500,1,0,0:0:0:0: +448,192,14625,1,0,0:0:0:0: +416,192,14750,1,0,0:0:0:0: +384,192,14875,1,0,0:0:0:0: +32,192,15000,1,0,0:0:0:0: + +// basic sliders +32,192,16000,2,0,L|192:192,1,160 +224,192,17000,2,0,L|384:192,1,160 +416,192,17875,2,0,L|480:192,1,40 + +// slider hyperdashes, spaced 1/4 beat apart +32,192,19000,2,0,L|128:192,1,80 +480,192,19375,2,0,L|384:192,1,80 +352,192,19750,2,0,L|256:192,1,80 +0,192,20125,2,0,L|128:192,1,120 + +// stream + slider hyperdashes, spaced 1/4 beat apart +32,192,21500,1,0,0:0:0:0: +64,192,21625,1,0,0:0:0:0: +96,192,21750,1,0,0:0:0:0: +512,192,21875,2,0,L|320:192,1,160 +320,192,22500,1,0,0:0:0:0: +288,192,22625,1,0,0:0:0:0: +256,192,22750,1,0,0:0:0:0: +0,192,22875,2,0,L|64:192,1,40 + +// streams, spaced 1/4 beat apart +64,192,24000,1,0,0:0:0:0: +160,192,24125,1,0,0:0:0:0: +64,192,24250,1,0,0:0:0:0: +160,192,24375,1,0,0:0:0:0: +64,192,24500,1,0,0:0:0:0: +160,192,24625,1,0,0:0:0:0: +64,192,24750,1,0,0:0:0:0: +160,192,24875,1,0,0:0:0:0: +64,192,25000,1,0,0:0:0:0: +160,192,25125,1,0,0:0:0:0: +64,192,25250,1,0,0:0:0:0: +160,192,25375,1,0,0:0:0:0: +64,192,25500,1,0,0:0:0:0: + +// stream + spinner combo, spaced 1/4 beat apart +256,192,26500,12,0,27000,0:0:0:0: +128,192,27250,5,0,0:0:0:0: +128,192,27375,1,0,0:0:0:0: +160,192,27500,1,0,0:0:0:0: +192,192,27625,1,0,0:0:0:0: +256,192,27750,12,0,28500,0:0:0:0: +192,192,28625,5,0,0:0:0:0: +224,192,28750,1,0,0:0:0:0: +256,192,28875,1,0,0:0:0:0: +256,192,29000,1,0,0:0:0:0: +256,192,29125,12,0,29500,0:0:0:0: + +// long slow slider +0,192,30500,6,0,B|480:192|480:192|0:192,2,960 + +// long fast slider +0,192,37500,6,0,B|480:192|480:192|0:192,2,960 + +// long hyperdash slider +0,192,41500,2,0,P|544:192|544:192,5,480 diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 4926a81034..e55dc1f902 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -16,9 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(6.931145117263422, "diffcalc-test")] public void Test(double expected, string name) - { - base.Test(expected, name); - } + => base.Test(expected, name); protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 112834e84c..299f84fb1f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -16,9 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Tests [TestCase(2.9811336589467095, "diffcalc-test")] [TestCase(2.9811336589467095, "diffcalc-test-strong")] public void Test(double expected, string name) - { - base.Test(expected, name); - } + => base.Test(expected, name); protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap); From e319a760b807ba9795355ab200c11a59c8cc0941 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 Feb 2019 16:25:44 +0900 Subject: [PATCH 104/167] Add mania difficulty calculator test --- .../ManiaDifficultyCalculatorTest.cs | 24 +++ .../Testing/Beatmaps/diffcalc-test.osu | 180 ++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs create mode 100644 osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs new file mode 100644 index 0000000000..05e2df796c --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -0,0 +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 NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Mania.Difficulty; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Mania.Tests +{ + public class ManiaDifficultyCalculatorTest : DifficultyCalculatorTest + { + protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + + [TestCase(2.2676066895468976, "diffcalc-test")] + public void Test(double expected, string name) + => base.Test(expected, name); + + protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap); + + protected override Ruleset CreateRuleset() => new ManiaRuleset(); + } +} diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu new file mode 100644 index 0000000000..4c877c6193 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu @@ -0,0 +1,180 @@ +osu file format v14 + +[General] +Mode: 3 + +[Difficulty] +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +37500,-50,4,2,1,50,0,0 +41500,-25,4,2,1,50,0,0 + +[HitObjects] +// jacks spaced 1/1 beat apart +64,192,0,1,0,0:0:0:0: +64,192,500,1,0,0:0:0:0: +64,192,1000,1,0,0:0:0:0: +64,192,1500,1,0,0:0:0:0: +64,192,2000,1,0,0:0:0:0: +64,192,2500,1,0,0:0:0:0: + +// jacks spaced 1/2 beat apart +64,192,3500,1,0,0:0:0:0: +64,192,3750,1,0,0:0:0:0: +64,192,4000,1,0,0:0:0:0: +64,192,4250,1,0,0:0:0:0: +64,192,4500,1,0,0:0:0:0: +64,192,4750,1,0,0:0:0:0: +64,192,5000,1,0,0:0:0:0: +64,192,6000,1,0,0:0:0:0: + +// doubles jacks spaced 1/2 beat apart +192,192,6000,1,0,0:0:0:0: +64,192,6250,1,0,0:0:0:0: +192,192,6250,1,0,0:0:0:0: +64,192,6500,1,0,0:0:0:0: +192,192,6500,1,0,0:0:0:0: +64,192,6750,1,0,0:0:0:0: +192,192,6750,1,0,0:0:0:0: +64,192,7000,1,0,0:0:0:0: +192,192,7000,1,0,0:0:0:0: +64,192,7250,1,0,0:0:0:0: +192,192,7250,1,0,0:0:0:0: +64,192,7500,1,0,0:0:0:0: +192,192,7500,1,0,0:0:0:0: + +// trill spaced 1/2 beat apart +64,192,8500,1,0,0:0:0:0: +192,192,8750,1,0,0:0:0:0: +64,192,9000,1,0,0:0:0:0: +192,192,9250,1,0,0:0:0:0: +64,192,9500,1,0,0:0:0:0: +192,192,9750,1,0,0:0:0:0: +64,192,10000,1,0,0:0:0:0: +192,192,10250,1,0,0:0:0:0: +64,192,10500,1,0,0:0:0:0: + +// stair spaced 1/4 apart +64,192,11500,1,0,0:0:0:0: +192,192,11625,1,0,0:0:0:0: +320,192,11750,1,0,0:0:0:0: +448,192,11875,1,0,0:0:0:0: +320,192,12000,1,0,0:0:0:0: +192,192,12125,1,0,0:0:0:0: +64,192,12250,1,0,0:0:0:0: +192,192,12375,1,0,0:0:0:0: +320,192,12500,1,0,0:0:0:0: +448,192,12625,1,0,0:0:0:0: + +// jumpstreams? +64,192,13500,1,0,0:0:0:0: +192,192,13625,1,0,0:0:0:0: +320,192,13750,1,0,0:0:0:0: +448,192,13875,1,0,0:0:0:0: +320,192,14000,1,0,0:0:0:0: +192,192,14000,1,0,0:0:0:0: +64,192,14125,1,0,0:0:0:0: +192,192,14250,1,0,0:0:0:0: +320,192,14250,1,0,0:0:0:0: +448,192,14250,1,0,0:0:0:0: +64,192,14375,1,0,0:0:0:0: +64,192,14500,1,0,0:0:0:0: +320,192,14625,1,0,0:0:0:0: +448,192,14625,1,0,0:0:0:0: +192,192,14625,1,0,0:0:0:0: +192,192,14750,1,0,0:0:0:0: +64,192,14875,1,0,0:0:0:0: +192,192,15000,1,0,0:0:0:0: +320,192,15125,1,0,0:0:0:0: +448,192,15125,1,0,0:0:0:0: + +// double... jumps? +64,192,16000,1,0,0:0:0:0: +64,192,16250,1,0,0:0:0:0: +192,192,16250,1,0,0:0:0:0: +192,192,16500,1,0,0:0:0:0: +320,192,16500,1,0,0:0:0:0: +320,192,16750,1,0,0:0:0:0: +448,192,16750,1,0,0:0:0:0: +448,192,17000,1,0,0:0:0:0: + +// notes alongside hold +64,192,18000,128,0,18500:0:0:0:0: +192,192,18000,1,0,0:0:0:0: +192,192,18250,1,0,0:0:0:0: +192,192,18500,1,0,0:0:0:0: + +// notes overlapping hold +64,192,19500,1,0,0:0:0:0: +192,192,19625,128,0,20875:0:0:0:0: +64,192,19750,1,0,0:0:0:0: +64,192,20000,1,0,0:0:0:0: +64,192,20250,1,0,0:0:0:0: +64,192,20500,1,0,0:0:0:0: +64,192,20750,1,0,0:0:0:0: +64,192,21000,1,0,0:0:0:0: + +// simultaneous holds +64,192,22000,128,0,23000:0:0:0:0: +192,192,22000,128,0,23000:0:0:0:0: +320,192,22000,128,0,23000:0:0:0:0: +448,192,22000,128,0,23000:0:0:0:0: + +// hold stairs +64,192,24500,128,0,25500:0:0:0:0: +192,192,24625,128,0,25375:0:0:0:0: +320,192,24750,128,0,25250:0:0:0:0: +448,192,24875,128,0,25125:0:0:0:0: +448,192,25375,128,0,26375:0:0:0:0: +320,192,25500,128,0,26250:0:0:0:0: +192,192,25625,128,0,26125:0:0:0:0: +64,192,25750,128,0,26000:0:0:0:0: + +// quads +64,192,26500,1,0,0:0:0:0: +64,192,27500,1,0,0:0:0:0: +192,192,27500,1,0,0:0:0:0: +320,192,27500,1,0,0:0:0:0: +448,192,27500,1,0,0:0:0:0: +64,192,27750,1,0,0:0:0:0: +192,192,27750,1,0,0:0:0:0: +320,192,27750,1,0,0:0:0:0: +448,192,27750,1,0,0:0:0:0: +64,192,28000,1,0,0:0:0:0: +192,192,28000,1,0,0:0:0:0: +320,192,28000,1,0,0:0:0:0: +448,192,28000,1,0,0:0:0:0: +64,192,28250,1,0,0:0:0:0: +192,192,28250,1,0,0:0:0:0: +320,192,28250,1,0,0:0:0:0: +448,192,28250,1,0,0:0:0:0: +64,192,28500,1,0,0:0:0:0: +192,192,28500,1,0,0:0:0:0: +320,192,28500,1,0,0:0:0:0: +448,192,28500,1,0,0:0:0:0: + +// double-trills +64,192,29500,1,0,0:0:0:0: +192,192,29500,1,0,0:0:0:0: +320,192,29625,1,0,0:0:0:0: +448,192,29625,1,0,0:0:0:0: +64,192,29750,1,0,0:0:0:0: +192,192,29750,1,0,0:0:0:0: +320,192,29875,1,0,0:0:0:0: +448,192,29875,1,0,0:0:0:0: +64,192,30000,1,0,0:0:0:0: +192,192,30000,1,0,0:0:0:0: +320,192,30125,1,0,0:0:0:0: +448,192,30125,1,0,0:0:0:0: +64,192,30250,1,0,0:0:0:0: +192,192,30250,1,0,0:0:0:0: +320,192,30375,1,0,0:0:0:0: +448,192,30375,1,0,0:0:0:0: +64,192,30500,1,0,0:0:0:0: +192,192,30500,1,0,0:0:0:0: From 3ea13b1ade846820c94bcf70cd72d8bc1f12d0f7 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 15 Feb 2019 16:55:39 +0900 Subject: [PATCH 105/167] Fix mouse cursor appearing prematurely during startup --- osu.Game/OsuGame.cs | 5 +++++ osu.Game/Screens/Loader.cs | 2 ++ 2 files changed, 7 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index ad6411a2d6..7b4ff3d295 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -337,6 +337,11 @@ namespace osu.Game { base.LoadComplete(); + // The next time this is updated is in UpdateAfterChildren, which occurs too late and results + // in the cursor being shown for a few frames during the intro. + // This prevents the cursor from showing until we have a screen with CursorVisible = true + MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false; + // todo: all archive managers should be able to be looped here. SkinManager.PostNotification = n => notifications?.Post(n); SkinManager.GetStableStorage = GetStorageForStableInstall; diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 9703d79442..0db0f1a1a3 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -21,6 +21,8 @@ namespace osu.Game.Screens public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; + public override bool CursorVisible => false; + protected override bool AllowBackButton => false; public Loader() From 65721a01aba53c4baefcc4d0f5aa3b429b46c99b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Feb 2019 17:01:06 +0900 Subject: [PATCH 106/167] Fix regressed screen test cases --- osu.Game.Tests/Visual/TestCaseDisclaimer.cs | 15 ++------------- osu.Game.Tests/Visual/TestCaseDrawings.cs | 4 ++-- osu.Game.Tests/Visual/TestCaseMatchResults.cs | 4 ++-- osu.Game.Tests/Visual/TestCaseMultiScreen.cs | 4 ++-- .../Visual/TestCaseParallaxContainer.cs | 7 ++++++- osu.Game.Tests/Visual/TestCasePlaySongSelect.cs | 6 ++++-- osu.Game.Tests/Visual/TestCaseResults.cs | 4 ++-- osu.Game/Tests/OsuTestBrowser.cs | 8 +++++--- osu.Game/Tests/Visual/MultiplayerTestCase.cs | 2 +- 9 files changed, 26 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseDisclaimer.cs b/osu.Game.Tests/Visual/TestCaseDisclaimer.cs index d3a53e4a05..3ceb3eb4bd 100644 --- a/osu.Game.Tests/Visual/TestCaseDisclaimer.cs +++ b/osu.Game.Tests/Visual/TestCaseDisclaimer.cs @@ -2,27 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; using osu.Game.Screens.Menu; -using osuTK.Graphics; namespace osu.Game.Tests.Visual { - public class TestCaseDisclaimer : OsuTestCase + public class TestCaseDisclaimer : ScreenTestCase { [BackgroundDependencyLoader] private void load() { - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }, - new Disclaimer() - }; + LoadScreen(new Disclaimer()); } } } diff --git a/osu.Game.Tests/Visual/TestCaseDrawings.cs b/osu.Game.Tests/Visual/TestCaseDrawings.cs index 51f34d54db..aad135b71f 100644 --- a/osu.Game.Tests/Visual/TestCaseDrawings.cs +++ b/osu.Game.Tests/Visual/TestCaseDrawings.cs @@ -9,11 +9,11 @@ using osu.Game.Screens.Tournament.Teams; namespace osu.Game.Tests.Visual { [Description("for tournament use")] - public class TestCaseDrawings : OsuTestCase + public class TestCaseDrawings : ScreenTestCase { public TestCaseDrawings() { - Add(new Drawings + LoadScreen(new Drawings { TeamList = new TestTeamList(), }); diff --git a/osu.Game.Tests/Visual/TestCaseMatchResults.cs b/osu.Game.Tests/Visual/TestCaseMatchResults.cs index 33469e74b7..582c035e82 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchResults.cs +++ b/osu.Game.Tests/Visual/TestCaseMatchResults.cs @@ -40,10 +40,10 @@ namespace osu.Game.Tests.Visual Room.RoomID.Value = 1; Room.Name.Value = "an awesome room"; - Child = new TestMatchResults(new ScoreInfo + LoadScreen(new TestMatchResults(new ScoreInfo { User = new User { Id = 10 }, - }); + })); } private class TestMatchResults : MatchResults diff --git a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs index d83b90d6e1..fc4037f58b 100644 --- a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs +++ b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs @@ -12,7 +12,7 @@ using osu.Game.Screens.Multi.Lounge.Components; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseMultiScreen : OsuTestCase + public class TestCaseMultiScreen : ScreenTestCase { public override IReadOnlyList RequiredTypes => new[] { @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual { Multiplayer multi = new Multiplayer(); - AddStep(@"show", () => Add(multi)); + AddStep(@"show", () => LoadScreen(multi)); AddWaitStep(5); AddStep(@"exit", multi.Exit); } diff --git a/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs b/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs index f9804655ea..41b029d69e 100644 --- a/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseParallaxContainer.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; +using osu.Framework.Screens; using osu.Game.Graphics.Containers; using osu.Game.Screens.Backgrounds; @@ -14,7 +16,10 @@ namespace osu.Game.Tests.Visual Add(parallax = new ParallaxContainer { - Child = new BackgroundScreenDefault { Alpha = 0.8f } + Child = new ScreenStack(new BackgroundScreenDefault { Alpha = 0.8f }) + { + RelativeSizeAxes = Axes.Both, + } }); AddStep("default parallax", () => parallax.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT); diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index dedcc24e53..c5cc1776f8 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -12,6 +12,7 @@ using osu.Framework.Configuration; using osu.Framework.Extensions; using osu.Framework.MathUtils; using osu.Framework.Platform; +using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Rulesets; @@ -25,7 +26,7 @@ using osu.Game.Screens.Select.Filter; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCasePlaySongSelect : OsuTestCase + public class TestCasePlaySongSelect : ScreenTestCase { private BeatmapManager manager; @@ -107,7 +108,8 @@ namespace osu.Game.Tests.Visual Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); - Child = songSelect = new TestSongSelect(); + LoadScreen(songSelect = new TestSongSelect()); + AddUntilStep(() => songSelect.IsPresent, "wait for present"); }); } diff --git a/osu.Game.Tests/Visual/TestCaseResults.cs b/osu.Game.Tests/Visual/TestCaseResults.cs index 403742a7b5..c2880c1ea2 100644 --- a/osu.Game.Tests/Visual/TestCaseResults.cs +++ b/osu.Game.Tests/Visual/TestCaseResults.cs @@ -16,7 +16,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseResults : OsuTestCase + public class TestCaseResults : ScreenTestCase { private BeatmapManager beatmaps; @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual if (beatmapInfo != null) Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); - Add(new SoloResults(new ScoreInfo + LoadScreen(new SoloResults(new ScoreInfo { TotalScore = 2845370, Accuracy = 0.98, diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index ae347965a9..71b0b02fa6 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; using osu.Framework.Platform; +using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Screens.Backgrounds; @@ -14,10 +16,10 @@ namespace osu.Game.Tests { base.LoadComplete(); - LoadComponentAsync(new BackgroundScreenDefault + LoadComponentAsync(new ScreenStack(new BackgroundScreenDefault { Colour = OsuColour.Gray(0.5f) }) { - Colour = OsuColour.Gray(0.5f), - Depth = 10 + Depth = 10, + RelativeSizeAxes = Axes.Both, }, AddInternal); // Have to construct this here, rather than in the constructor, because diff --git a/osu.Game/Tests/Visual/MultiplayerTestCase.cs b/osu.Game/Tests/Visual/MultiplayerTestCase.cs index 7e2f915179..578ef6632c 100644 --- a/osu.Game/Tests/Visual/MultiplayerTestCase.cs +++ b/osu.Game/Tests/Visual/MultiplayerTestCase.cs @@ -7,7 +7,7 @@ using osu.Game.Online.Multiplayer; namespace osu.Game.Tests.Visual { - public abstract class MultiplayerTestCase : OsuTestCase + public abstract class MultiplayerTestCase : ScreenTestCase { [Cached] private readonly Bindable currentRoom = new Bindable(new Room()); From 5d502250d8cbdc97c8795252ea7e5049a0b76479 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Feb 2019 17:01:51 +0900 Subject: [PATCH 107/167] Fix account creation overlay not working Regressed with screen stack changes. --- osu.Game/Overlays/AccountCreationOverlay.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/AccountCreationOverlay.cs b/osu.Game/Overlays/AccountCreationOverlay.cs index dab960db25..bc780538d5 100644 --- a/osu.Game/Overlays/AccountCreationOverlay.cs +++ b/osu.Game/Overlays/AccountCreationOverlay.cs @@ -70,7 +70,10 @@ namespace osu.Game.Overlays Colour = Color4.Black, Alpha = 0.9f, }, - welcomeScreen = new ScreenWelcome(), + new ScreenStack(welcomeScreen = new ScreenWelcome()) + { + RelativeSizeAxes = Axes.Both, + }, } } } From fc583590d3aa4a6daf27b5cabde29b3ecbbd9715 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Feb 2019 17:40:49 +0900 Subject: [PATCH 108/167] Fix OsuGame testcase --- osu.Game.Tests/Visual/TestCaseOsuGame.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseOsuGame.cs b/osu.Game.Tests/Visual/TestCaseOsuGame.cs index 16087b5ad8..c527bce683 100644 --- a/osu.Game.Tests/Visual/TestCaseOsuGame.cs +++ b/osu.Game.Tests/Visual/TestCaseOsuGame.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Framework.Screens; using osu.Game.Screens; using osu.Game.Screens.Menu; using osuTK.Graphics; @@ -29,7 +30,10 @@ namespace osu.Game.Tests.Visual RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - new Loader() + new ScreenStack(new Loader()) + { + RelativeSizeAxes = Axes.Both, + } }; } } From 38cf5a1ea4670d6eaa62fc8b08eaec22a02c95de Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 15 Feb 2019 21:03:06 +0900 Subject: [PATCH 109/167] Add support for the HitCircleOverlap property in legacy skins --- osu.Game/Skinning/LegacySkin.cs | 9 ++++++++- osu.Game/Skinning/LegacySkinDecoder.cs | 3 +++ osu.Game/Skinning/SkinConfiguration.cs | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 80f79129c9..5dfefcb777 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -58,7 +58,14 @@ namespace osu.Game.Skinning componentName = "hit300"; break; case "Play/osu/number-text": - return !hasFont(Configuration.HitCircleFont) ? null : new LegacySpriteText(Textures, Configuration.HitCircleFont) { Scale = new Vector2(0.96f) }; + return !hasFont(Configuration.HitCircleFont) + ? null + : new LegacySpriteText(Textures, Configuration.HitCircleFont) + { + Scale = new Vector2(0.96f), + // Spacing value was reverse-engineered from the ratio of the rendered sprite size in the visual inspector vs the actual texture size + Spacing = new Vector2(-Configuration.HitCircleOverlap * 0.89f, 0) + }; } var texture = GetTexture(componentName); diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 44bb9c3f41..96a9116c51 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -46,6 +46,9 @@ namespace osu.Game.Skinning case "HitCirclePrefix": skin.HitCircleFont = pair.Value; break; + case "HitCircleOverlap": + skin.HitCircleOverlap = int.Parse(pair.Value); + break; } break; diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index a8091d1f36..5b832e15a3 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -22,6 +22,7 @@ namespace osu.Game.Skinning public Dictionary CustomColours { get; set; } = new Dictionary(); public string HitCircleFont { get; set; } = "default"; + public int HitCircleOverlap { get; set; } public bool? CursorExpand { get; set; } = true; } From 1c8212d510e6bb218570a2ad2fa1afb5a3abc383 Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 15 Feb 2019 21:03:55 +0900 Subject: [PATCH 110/167] Add a TestCase for looong combos --- .../TestCaseHitCircleLongCombo.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleLongCombo.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleLongCombo.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleLongCombo.cs new file mode 100644 index 0000000000..f5fe36b56a --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleLongCombo.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestCaseHitCircleLongCombo : Game.Tests.Visual.TestCasePlayer + { + public TestCaseHitCircleLongCombo() + : base(new OsuRuleset()) + { + } + + protected override IBeatmap CreateBeatmap(Ruleset ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 }, + Ruleset = ruleset.RulesetInfo + } + }; + + for (int i = 0; i < 512; i++) + beatmap.HitObjects.Add(new HitCircle { Position = new Vector2(256, 192), StartTime = i * 100 }); + + return beatmap; + } + } +} From 90e462309f61e30e718552829e26940c6b9733a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Feb 2019 21:16:54 +0900 Subject: [PATCH 111/167] Add newline --- osu.Game/Skinning/SkinConfiguration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index 5b832e15a3..82faec4e9d 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -22,6 +22,7 @@ namespace osu.Game.Skinning public Dictionary CustomColours { get; set; } = new Dictionary(); public string HitCircleFont { get; set; } = "default"; + public int HitCircleOverlap { get; set; } public bool? CursorExpand { get; set; } = true; From c607b8c979ec4743a7ad69c58118fd7193bbc155 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Feb 2019 21:28:59 +0900 Subject: [PATCH 112/167] Update framework --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 38ed6870ff..6b94fa4f98 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index fb8a46992e..d677ef4e21 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -105,8 +105,8 @@ - - + + From aceb8c4cb08804f1f92fd47c6ae603c66c4c7a51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Feb 2019 21:50:40 +0900 Subject: [PATCH 113/167] Fix TestCasePlaySongSelect --- .../Visual/TestCasePlaySongSelect.cs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs index c5cc1776f8..78e90987b1 100644 --- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs @@ -103,22 +103,16 @@ namespace osu.Game.Tests.Visual } [SetUp] - public virtual void SetUp() - { - Schedule(() => - { - manager?.Delete(manager.GetAllUsableBeatmapSets()); - LoadScreen(songSelect = new TestSongSelect()); - AddUntilStep(() => songSelect.IsPresent, "wait for present"); - }); - } + public virtual void SetUp() => + Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); }); [Test] public void TestDummy() { + createSongSelect(); AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap); - AddAssert("dummy shown on wedge", () => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap); + AddUntilStep(() => songSelect.CurrentBeatmapDetailsBeatmap == defaultBeatmap, "dummy shown on wedge"); addManyTestMaps(); AddWaitStep(3); @@ -129,6 +123,7 @@ namespace osu.Game.Tests.Visual [Test] public void TestSorting() { + createSongSelect(); addManyTestMaps(); AddWaitStep(3); @@ -144,6 +139,7 @@ namespace osu.Game.Tests.Visual [Ignore("needs fixing")] public void TestImportUnderDifferentRuleset() { + createSongSelect(); changeRuleset(2); importForRuleset(0); AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection"); @@ -152,6 +148,7 @@ namespace osu.Game.Tests.Visual [Test] public void TestImportUnderCurrentRuleset() { + createSongSelect(); changeRuleset(2); importForRuleset(2); importForRuleset(1); @@ -167,6 +164,7 @@ namespace osu.Game.Tests.Visual [Test] public void TestRulesetChangeResetsMods() { + createSongSelect(); changeRuleset(0); changeMods(new OsuModHardRock()); @@ -196,6 +194,7 @@ namespace osu.Game.Tests.Visual [Test] public void TestStartAfterUnMatchingFilterDoesNotStart() { + createSongSelect(); addManyTestMaps(); AddUntilStep(() => songSelect.Carousel.SelectedBeatmap != null, "has selection"); @@ -223,6 +222,12 @@ namespace osu.Game.Tests.Visual private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id)); + private void createSongSelect() + { + AddStep("create song select", () => LoadScreen(songSelect = new TestSongSelect())); + AddUntilStep(() => songSelect.IsCurrentScreen(), "wait for present"); + } + private void addManyTestMaps() { AddStep("import test maps", () => From 57fcdf9a0b44081e1c77a572f1e606e26b5c0ace Mon Sep 17 00:00:00 2001 From: miterosan Date: Fri, 15 Feb 2019 23:47:22 +0100 Subject: [PATCH 114/167] Improve the buildscript --- .gitignore | 5 +++-- .vscode/launch.json | 14 ++++++++++++++ build.ps1 | 13 ++++++++----- build.cake => build/build.cake | 20 +++++++------------- {tools => build}/cakebuild.csproj | 0 5 files changed, 32 insertions(+), 20 deletions(-) rename build.cake => build/build.cake (77%) rename {tools => build}/cakebuild.csproj (100%) diff --git a/.gitignore b/.gitignore index f95a04e517..6186cb870d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,8 +11,9 @@ *.userprefs ### Cake ### -tools/* -!tools/cakebuild.csproj +tools/** +build/tools/** + # Build results bin/[Dd]ebug/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 10c6f02314..c3306c2db7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -68,6 +68,20 @@ } }, "console": "internalConsole" + }, + { + "name": "Cake: Debug Script", + "type": "coreclr", + "request": "launch", + "program": "${workspaceRoot}/build/tools/Cake.CoreCLR/0.30.0/Cake.dll", + "args": [ + "${workspaceRoot}/build/build.cake", + "--debug", + "--verbosity=diagnostic" + ], + "cwd": "${workspaceRoot}/build", + "stopAtEntry": true, + "externalConsole": false } ] } diff --git a/build.ps1 b/build.ps1 index 9968673c90..c6a0bf6d4a 100644 --- a/build.ps1 +++ b/build.ps1 @@ -41,27 +41,28 @@ Param( [switch]$ShowDescription, [Alias("WhatIf", "Noop")] [switch]$DryRun, - [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [Parameter(Position = 0, Mandatory = $false, ValueFromRemainingArguments = $true)] [string[]]$ScriptArgs ) Write-Host "Preparing to run build script..." # Determine the script root for resolving other paths. -if(!$PSScriptRoot){ +if(!$PSScriptRoot) { $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } # Resolve the paths for resources used for debugging. -$TOOLS_DIR = Join-Path $PSScriptRoot "tools" -$CAKE_CSPROJ = Join-Path $TOOLS_DIR "cakebuild.csproj" +$BUILD_DIR = Join-Path $PSScriptRoot "build" +$TOOLS_DIR = Join-Path $BUILD_DIR "tools" +$CAKE_CSPROJ = Join-Path $BUILD_DIR "cakebuild.csproj" # Install the required tools locally. Write-Host "Restoring cake tools..." Invoke-Expression "dotnet restore `"$CAKE_CSPROJ`" --packages `"$TOOLS_DIR`"" | Out-Null # Find the Cake executable -$CAKE_EXECUTABLE = (Get-ChildItem -Path ./tools/cake.coreclr/ -Filter Cake.dll -Recurse).FullName +$CAKE_EXECUTABLE = (Get-ChildItem -Path "$TOOLS_DIR/cake.coreclr/" -Filter Cake.dll -Recurse).FullName # Build Cake arguments $cakeArguments = @("$Script"); @@ -75,5 +76,7 @@ $cakeArguments += $ScriptArgs # Start Cake Write-Host "Running build script..." +Push-Location -Path $BUILD_DIR Invoke-Expression "dotnet `"$CAKE_EXECUTABLE`" $cakeArguments" +Pop-Location exit $LASTEXITCODE diff --git a/build.cake b/build/build.cake similarity index 77% rename from build.cake rename to build/build.cake index bc7dfafb8c..411adea7dd 100644 --- a/build.cake +++ b/build/build.cake @@ -1,6 +1,7 @@ #addin "nuget:?package=CodeFileSanity&version=0.0.21" #addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.2" #tool "nuget:?package=NVika.MSBuild&version=1.0.1" +var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First(); /////////////////////////////////////////////////////////////////////////////// // ARGUMENTS @@ -9,30 +10,25 @@ var target = Argument("target", "Build"); var configuration = Argument("configuration", "Release"); -var osuSolution = new FilePath("./osu.sln"); +var rootDirectory = new DirectoryPath(".."); +var desktopProject = rootDirectory.CombineWithFilePath("osu.Desktop/osu.Desktop.csproj"); +var solution = rootDirectory.CombineWithFilePath("osu.sln"); /////////////////////////////////////////////////////////////////////////////// // TASKS /////////////////////////////////////////////////////////////////////////////// -Task("Restore") - .Does(() => { - DotNetCoreRestore(osuSolution.FullPath); - }); - Task("Compile") - .IsDependentOn("Restore") .Does(() => { - DotNetCoreBuild(osuSolution.FullPath, new DotNetCoreBuildSettings { + DotNetCoreBuild(solution.FullPath, new DotNetCoreBuildSettings { Configuration = configuration, - NoRestore = true, }); }); Task("Test") .IsDependentOn("Compile") .Does(() => { - var testAssemblies = GetFiles("**/*.Tests/bin/**/*.Tests.dll"); + var testAssemblies = GetFiles(rootDirectory + "/**/*.Tests/bin/**/*.Tests.dll"); DotNetCoreVSTest(testAssemblies, new DotNetCoreVSTestSettings { Logger = AppVeyor.IsRunningOnAppVeyor ? "Appveyor" : $"trx", @@ -46,9 +42,7 @@ Task("InspectCode") .WithCriteria(IsRunningOnWindows()) .IsDependentOn("Compile") .Does(() => { - var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First(); - - InspectCode(osuSolution, new InspectCodeSettings { + InspectCode(solution, new InspectCodeSettings { CachesHome = "inspectcode", OutputFile = "inspectcodereport.xml", }); diff --git a/tools/cakebuild.csproj b/build/cakebuild.csproj similarity index 100% rename from tools/cakebuild.csproj rename to build/cakebuild.csproj From ed67b580fa3bef31005fdb946e0e9a4ffc4e7845 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sat, 16 Feb 2019 00:02:08 +0100 Subject: [PATCH 115/167] Also apply the changes to build.sh --- build.sh | 1 + build/build.cake | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.sh b/build.sh index caf1702f41..d2b88c65ae 100755 --- a/build.sh +++ b/build.sh @@ -6,6 +6,7 @@ echo "Preparing to run build script..." +cd build SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) TOOLS_DIR=$SCRIPT_DIR/tools CAKE_BINARY_PATH=$TOOLS_DIR/"cake.coreclr" diff --git a/build/build.cake b/build/build.cake index 411adea7dd..f27f443ee9 100644 --- a/build/build.cake +++ b/build/build.cake @@ -53,7 +53,7 @@ Task("InspectCode") Task("CodeFileSanity") .Does(() => { ValidateCodeSanity(new ValidateCodeSanitySettings { - RootDirectory = ".", + RootDirectory = rootDirectory.FullPath, IsAppveyorBuild = AppVeyor.IsRunningOnAppVeyor }); }); From 134840f118f9c5730f3736bfa7b3d5d6d857909f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 Feb 2019 11:18:17 +0900 Subject: [PATCH 116/167] Fix nullref --- osu.Game/Screens/Multi/Multiplayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 822be0891b..32eea88fbc 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Multi private readonly Bindable currentRoom = new Bindable(); [Cached] - private readonly Bindable currentFilter = new Bindable(); + private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); [Cached(Type = typeof(IRoomManager))] private RoomManager roomManager; From c09346080f492714f850aac2f004553e24c76d27 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sat, 16 Feb 2019 14:51:20 +0100 Subject: [PATCH 117/167] Remove the unused desktop project variable --- build/build.cake | 1 - 1 file changed, 1 deletion(-) diff --git a/build/build.cake b/build/build.cake index f27f443ee9..81deeb3bc7 100644 --- a/build/build.cake +++ b/build/build.cake @@ -11,7 +11,6 @@ var target = Argument("target", "Build"); var configuration = Argument("configuration", "Release"); var rootDirectory = new DirectoryPath(".."); -var desktopProject = rootDirectory.CombineWithFilePath("osu.Desktop/osu.Desktop.csproj"); var solution = rootDirectory.CombineWithFilePath("osu.sln"); /////////////////////////////////////////////////////////////////////////////// From 97086342324789b333dcc01b5cad5bd4c170ce48 Mon Sep 17 00:00:00 2001 From: miterosan Date: Sun, 17 Feb 2019 13:57:14 +0100 Subject: [PATCH 118/167] Correct the path to the cakebuild.csproj. --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index d2b88c65ae..8f1ef5b455 100755 --- a/build.sh +++ b/build.sh @@ -12,7 +12,7 @@ TOOLS_DIR=$SCRIPT_DIR/tools CAKE_BINARY_PATH=$TOOLS_DIR/"cake.coreclr" SCRIPT="build.cake" -CAKE_CSPROJ=$TOOLS_DIR/"cakebuild.csproj" +CAKE_CSPROJ=$SCRIPT_DIR/"cakebuild.csproj" # Parse arguments. CAKE_ARGUMENTS=() From a8faa942a6f074e1f0b5e16886e309e648ed37b5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Feb 2019 16:01:25 +0900 Subject: [PATCH 119/167] Implement new difficulty calculator structure --- .../CatchDifficultyCalculatorTest.cs | 2 +- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- ....cs => CatchLegacyDifficultyCalculator.cs} | 4 +- .../ManiaDifficultyCalculatorTest.cs | 2 +- ....cs => ManiaLegacyDifficultyCalculator.cs} | 4 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../OsuDifficultyCalculatorTest.cs | 2 +- ...or.cs => OsuLegacyDifficultyCalculator.cs} | 4 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../TaikoDifficultyCalculatorTest.cs | 2 +- ....cs => TaikoLegacyDifficultyCalculator.cs} | 4 +- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- ...DifficultyAdjustmentModCombinationsTest.cs | 16 +-- osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 +- .../Difficulty/DifficultyAttributes.cs | 8 +- .../Difficulty/DifficultyCalculator.cs | 104 +++++++++++------ .../Difficulty/LegacyDifficultyCalculator.cs | 107 ++++++++++++++++++ .../Preprocessing/DifficultyHitObject.cs | 32 ++++++ osu.Game/Rulesets/Difficulty/Skills/Skill.cs | 103 +++++++++++++++++ osu.Game/Rulesets/Difficulty/Utils/History.cs | 86 ++++++++++++++ osu.Game/Rulesets/Ruleset.cs | 2 +- .../Beatmaps/DifficultyCalculatorTest.cs | 2 +- 22 files changed, 430 insertions(+), 64 deletions(-) rename osu.Game.Rulesets.Catch/Difficulty/{CatchDifficultyCalculator.cs => CatchLegacyDifficultyCalculator.cs} (97%) rename osu.Game.Rulesets.Mania/Difficulty/{ManiaDifficultyCalculator.cs => ManiaLegacyDifficultyCalculator.cs} (97%) rename osu.Game.Rulesets.Osu/Difficulty/{OsuDifficultyCalculator.cs => OsuLegacyDifficultyCalculator.cs} (95%) rename osu.Game.Rulesets.Taiko/Difficulty/{TaikoDifficultyCalculator.cs => TaikoLegacyDifficultyCalculator.cs} (97%) create mode 100644 osu.Game/Rulesets/Difficulty/LegacyDifficultyCalculator.cs create mode 100644 osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs create mode 100644 osu.Game/Rulesets/Difficulty/Skills/Skill.cs create mode 100644 osu.Game/Rulesets/Difficulty/Utils/History.cs diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 91bc537902..84f4fd9c99 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchLegacyDifficultyCalculator(new CatchRuleset(), beatmap); protected override Ruleset CreateRuleset() => new CatchRuleset(); } diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index a69070e93e..9b2bbc9bf7 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Catch public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchLegacyDifficultyCalculator(this, beatmap); public override int? LegacyID => 2; diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs similarity index 97% rename from osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs rename to osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs index a0b813478d..0a0897d97b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.Catch.UI; namespace osu.Game.Rulesets.Catch.Difficulty { - public class CatchDifficultyCalculator : DifficultyCalculator + public class CatchLegacyDifficultyCalculator : LegacyDifficultyCalculator { /// /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty private const double star_scaling_factor = 0.145; - public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public CatchLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs index 05e2df796c..ef660b9ea8 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaLegacyDifficultyCalculator(new ManiaRuleset(), beatmap); protected override Ruleset CreateRuleset() => new ManiaRuleset(); } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs similarity index 97% rename from osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs rename to osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs index b8588dbce2..02b03aca5d 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Difficulty { - internal class ManiaDifficultyCalculator : DifficultyCalculator + internal class ManiaLegacyDifficultyCalculator : LegacyDifficultyCalculator { private const double star_scaling_factor = 0.018; @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty private readonly bool isForCurrentRuleset; - public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public ManiaLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) { isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 57728dd134..7a2a539a9d 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Mania public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaLegacyDifficultyCalculator(this, beatmap); public override int? LegacyID => 3; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index e55dc1f902..cc46ec7be3 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuLegacyDifficultyCalculator(new OsuRuleset(), beatmap); protected override Ruleset CreateRuleset() => new OsuRuleset(); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs similarity index 95% rename from osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs rename to osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs index 3d0a1dc266..d01f75df6b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs @@ -13,12 +13,12 @@ using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty { - public class OsuDifficultyCalculator : DifficultyCalculator + public class OsuLegacyDifficultyCalculator : LegacyDifficultyCalculator { private const int section_length = 400; private const double difficulty_multiplier = 0.0675; - public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public OsuLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 12d0a28a8f..6fa1532580 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuLegacyDifficultyCalculator(this, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new OsuPerformanceCalculator(this, beatmap, score); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 299f84fb1f..e00f3da0b7 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoLegacyDifficultyCalculator(new TaikoRuleset(), beatmap); protected override Ruleset CreateRuleset() => new TaikoRuleset(); } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs similarity index 97% rename from osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs rename to osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs index 2322446666..650b367e34 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty { - internal class TaikoDifficultyCalculator : DifficultyCalculator + internal class TaikoLegacyDifficultyCalculator : LegacyDifficultyCalculator { private const double star_scaling_factor = 0.04125; @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty /// private const double decay_weight = 0.9; - public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + public TaikoLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) { } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 7851a2f919..77a53858fe 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Taiko public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoLegacyDifficultyCalculator(this, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new TaikoPerformanceCalculator(this, beatmap, score); diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 795c2244a2..f57f25e1ff 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -15,7 +15,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestNoMods() { - var combinations = new TestDifficultyCalculator().CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator().CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(1, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -24,7 +24,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestSingleMod() { - var combinations = new TestDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(2, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -34,7 +34,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleMod() { - var combinations = new TestDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(4, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -49,7 +49,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleMods() { - var combinations = new TestDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModIncompatibleWithA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -60,7 +60,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestDoubleIncompatibleMods() { - var combinations = new TestDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModA(), new ModB(), new ModIncompatibleWithA(), new ModIncompatibleWithAAndB()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(8, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -83,7 +83,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIncompatibleThroughBaseType() { - var combinations = new TestDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations(); + var combinations = new TestLegacyDifficultyCalculator(new ModAofA(), new ModIncompatibleWithAofA()).CreateDifficultyAdjustmentModCombinations(); Assert.AreEqual(3, combinations.Length); Assert.IsTrue(combinations[0] is ModNoMod); @@ -136,9 +136,9 @@ namespace osu.Game.Tests.NonVisual public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) }; } - private class TestDifficultyCalculator : DifficultyCalculator + private class TestLegacyDifficultyCalculator : LegacyDifficultyCalculator { - public TestDifficultyCalculator(params Mod[] mods) + public TestLegacyDifficultyCalculator(params Mod[] mods) : base(null, null) { DifficultyAdjustmentMods = mods; diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 0aa1697bf8..eb9e221ca4 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap }; - public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => null; + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => null; public override string Description => "dummy"; diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index d7654462d5..b1a88b8abd 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -8,7 +8,13 @@ namespace osu.Game.Rulesets.Difficulty public class DifficultyAttributes { public readonly Mod[] Mods; - public readonly double StarRating; + + public double StarRating; + + public DifficultyAttributes(Mod[] mods) + { + Mods = mods; + } public DifficultyAttributes(Mod[] mods, double starRating) { diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 818d24508a..d7ae527bb1 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -1,56 +1,67 @@ -// 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.Collections.Generic; using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Timing; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Difficulty { - public abstract class DifficultyCalculator + public abstract class DifficultyCalculator : LegacyDifficultyCalculator { - private readonly Ruleset ruleset; - private readonly WorkingBeatmap beatmap; + /// + /// The length of each strain section. + /// + protected virtual int SectionLength => 400; protected DifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) { - this.ruleset = ruleset; - this.beatmap = beatmap; } - /// - /// Calculates the difficulty of the beatmap using a specific mod combination. - /// - /// The mods that should be applied to the beatmap. - /// A structure describing the difficulty of the beatmap. - public DifficultyAttributes Calculate(params Mod[] mods) + protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) { - beatmap.Mods.Value = mods; - IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo); + var attributes = CreateDifficultyAttributes(mods); - var clock = new StopwatchClock(); - mods.OfType().ForEach(m => m.ApplyToClock(clock)); + if (!beatmap.HitObjects.Any()) + return attributes; - return Calculate(playableBeatmap, mods, clock.Rate); - } + var difficultyHitObjects = CreateDifficultyHitObjects(beatmap, timeRate).OrderBy(h => h.BaseObject.StartTime).ToList(); + var skills = CreateSkills(); - /// - /// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap. - /// - /// A collection of structures describing the difficulty of the beatmap for each mod combination. - public IEnumerable CalculateAll() - { - foreach (var combination in CreateDifficultyAdjustmentModCombinations()) + double sectionLength = SectionLength * timeRate; + + // The first object doesn't generate a strain, so we begin with an incremented section end + double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength; + + foreach (DifficultyHitObject h in difficultyHitObjects) { - if (combination is MultiMod multi) - yield return Calculate(multi.Mods); - else - yield return Calculate(combination); + while (h.BaseObject.StartTime > currentSectionEnd) + { + foreach (Skill s in skills) + { + s.SaveCurrentPeak(); + s.StartNewSectionFrom(currentSectionEnd); + } + + currentSectionEnd += sectionLength; + } + + foreach (Skill s in skills) + s.Process(h); } + + // The peak strain will not be saved for the last section in the above loop + foreach (Skill s in skills) + s.SaveCurrentPeak(); + + PopulateAttributes(attributes, beatmap, skills, timeRate); + + return attributes; } /// @@ -96,12 +107,33 @@ namespace osu.Game.Rulesets.Difficulty protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); /// - /// Calculates the difficulty of a using a specific combination. + /// Populates after difficulty has been processed. /// - /// The to compute the difficulty for. - /// The s that should be applied. + /// The to populate with information about the difficulty of . + /// The whose difficulty was processed. + /// The skills which processed the difficulty. /// The rate of time in . - /// A structure containing the difficulty attributes. - protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate); + protected abstract void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate); + + /// + /// Enumerates s to be processed from s in the . + /// + /// The providing the s to enumerate. + /// The rate of time in . + /// The enumerated s. + protected abstract IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate); + + /// + /// Creates the s to calculate the difficulty of s. + /// + /// The s. + protected abstract Skill[] CreateSkills(); + + /// + /// Creates an empty . + /// + /// The s which difficulty is being processed with. + /// The empty . + protected abstract DifficultyAttributes CreateDifficultyAttributes(Mod[] mods); } } diff --git a/osu.Game/Rulesets/Difficulty/LegacyDifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/LegacyDifficultyCalculator.cs new file mode 100644 index 0000000000..15565ef847 --- /dev/null +++ b/osu.Game/Rulesets/Difficulty/LegacyDifficultyCalculator.cs @@ -0,0 +1,107 @@ +// 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.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Difficulty +{ + public abstract class LegacyDifficultyCalculator + { + private readonly Ruleset ruleset; + private readonly WorkingBeatmap beatmap; + + protected LegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + { + this.ruleset = ruleset; + this.beatmap = beatmap; + } + + /// + /// Calculates the difficulty of the beatmap using a specific mod combination. + /// + /// The mods that should be applied to the beatmap. + /// A structure describing the difficulty of the beatmap. + public DifficultyAttributes Calculate(params Mod[] mods) + { + beatmap.Mods.Value = mods; + IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo); + + var clock = new StopwatchClock(); + mods.OfType().ForEach(m => m.ApplyToClock(clock)); + + return Calculate(playableBeatmap, mods, clock.Rate); + } + + /// + /// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap. + /// + /// A collection of structures describing the difficulty of the beatmap for each mod combination. + public IEnumerable CalculateAll() + { + foreach (var combination in CreateDifficultyAdjustmentModCombinations()) + { + if (combination is MultiMod multi) + yield return Calculate(multi.Mods); + else + yield return Calculate(combination); + } + } + + /// + /// Creates all combinations which adjust the difficulty. + /// + public Mod[] CreateDifficultyAdjustmentModCombinations() + { + return createDifficultyAdjustmentModCombinations(Enumerable.Empty(), DifficultyAdjustmentMods).ToArray(); + + IEnumerable createDifficultyAdjustmentModCombinations(IEnumerable currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0) + { + switch (currentSetCount) + { + case 0: + // Initial-case: Empty current set + yield return new ModNoMod(); + break; + case 1: + yield return currentSet.Single(); + break; + default: + yield return new MultiMod(currentSet.ToArray()); + break; + } + + // Apply mods in the adjustment set recursively. Using the entire adjustment set would result in duplicate multi-mod mod + // combinations in further recursions, so a moving subset is used to eliminate this effect + for (int i = adjustmentSetStart; i < adjustmentSet.Length; i++) + { + var adjustmentMod = adjustmentSet[i]; + if (currentSet.Any(c => c.IncompatibleMods.Any(m => m.IsInstanceOfType(adjustmentMod)))) + continue; + + foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(adjustmentMod), adjustmentSet, currentSetCount + 1, i + 1)) + yield return combo; + } + } + } + + /// + /// Retrieves all s which adjust the difficulty. + /// + protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); + + /// + /// Calculates the difficulty of a using a specific combination. + /// + /// The to compute the difficulty for. + /// The s that should be applied. + /// The rate of time in . + /// A structure containing the difficulty attributes. + protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate); + } +} diff --git a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs new file mode 100644 index 0000000000..77c9b7e47f --- /dev/null +++ b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Difficulty.Preprocessing +{ + public class DifficultyHitObject + { + /// + /// Milliseconds elapsed since the of the previous . + /// + public double DeltaTime { get; private set; } + + /// + /// The this refers to. + /// + public readonly HitObject BaseObject; + + /// + /// The previous to . + /// + public readonly HitObject LastObject; + + public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate) + { + BaseObject = hitObject; + LastObject = lastObject; + DeltaTime = (hitObject.StartTime - lastObject.StartTime) / timeRate; + } + } +} diff --git a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs new file mode 100644 index 0000000000..fa7aa8f637 --- /dev/null +++ b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs @@ -0,0 +1,103 @@ +// 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 osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Utils; + +namespace osu.Game.Rulesets.Difficulty.Skills +{ + /// + /// Used to processes strain values of s, keep track of strain levels caused by the processed objects + /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects. + /// + public abstract class Skill + { + /// + /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. + /// + protected abstract double SkillMultiplier { get; } + + /// + /// Determines how quickly strain decays for the given skill. + /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second. + /// + protected abstract double StrainDecayBase { get; } + + /// + /// The weight by which each strain value decays. + /// + protected virtual double DecayWeight => 0.9; + + /// + /// s that were processed previously. They can affect the strain values of the following objects. + /// + protected readonly History Previous = new History(2); // Contained objects not used yet + + private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap. + private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. + private readonly List strainPeaks = new List(); + + /// + /// Process a and update current strain values accordingly. + /// + public void Process(DifficultyHitObject current) + { + currentStrain *= strainDecay(current.DeltaTime); + currentStrain += StrainValueOf(current) * SkillMultiplier; + + currentSectionPeak = Math.Max(currentStrain, currentSectionPeak); + + Previous.Push(current); + } + + /// + /// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty. + /// + public void SaveCurrentPeak() + { + if (Previous.Count > 0) + strainPeaks.Add(currentSectionPeak); + } + + /// + /// Sets the initial strain level for a new section. + /// + /// The beginning of the new section in milliseconds. + public void StartNewSectionFrom(double offset) + { + // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries. + // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level. + if (Previous.Count > 0) + currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime); + } + + /// + /// Returns the calculated difficulty value representing all processed s. + /// + public double DifficultyValue() + { + strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + double difficulty = 0; + double weight = 1; + + // Difficulty is the weighted sum of the highest strains from every section. + foreach (double strain in strainPeaks) + { + difficulty += strain * weight; + weight *= DecayWeight; + } + + return difficulty; + } + + /// + /// Calculates the strain value of a . This value is affected by previously processed objects. + /// + protected abstract double StrainValueOf(DifficultyHitObject current); + + private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000); + } +} diff --git a/osu.Game/Rulesets/Difficulty/Utils/History.cs b/osu.Game/Rulesets/Difficulty/Utils/History.cs new file mode 100644 index 0000000000..d6647d5119 --- /dev/null +++ b/osu.Game/Rulesets/Difficulty/Utils/History.cs @@ -0,0 +1,86 @@ +// 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; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Difficulty.Utils +{ + /// + /// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full. + /// Indexing starts at the top of the stack. + /// + public class History : IEnumerable + { + public int Count { get; private set; } + + private readonly T[] array; + private readonly int capacity; + private int marker; // Marks the position of the most recently added item. + + /// + /// Initializes a new instance of the History class that is empty and has the specified capacity. + /// + /// The number of items the History can hold. + public History(int capacity) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException(); + + this.capacity = capacity; + array = new T[capacity]; + marker = capacity; // Set marker to the end of the array, outside of the indexed range by one. + } + + /// + /// The most recently added item is returned at index 0. + /// + public T this[int i] + { + get + { + if (i < 0 || i > Count - 1) + throw new IndexOutOfRangeException(); + + i += marker; + if (i > capacity - 1) + i -= capacity; + + return array[i]; + } + } + + /// + /// Adds the item as the most recent one in the history. + /// The oldest item is disposed if the history is full. + /// + public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition. + { + if (marker == 0) + marker = capacity - 1; + else + --marker; + + array[marker] = item; + + if (Count < capacity) + ++Count; + } + + /// + /// Returns an enumerator which enumerates items in the history starting from the most recently added one. + /// + public IEnumerator GetEnumerator() + { + for (int i = marker; i < capacity; ++i) + yield return array[i]; + + if (Count == capacity) + for (int i = 0; i < marker; ++i) + yield return array[i]; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index ffab0abebf..75643a85dc 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets /// The . public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null; - public abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); + public abstract LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); public virtual PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => null; diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index 108fa8ff71..85ae1958ef 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}"); } - protected abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); + protected abstract LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); protected abstract Ruleset CreateRuleset(); } From 8eba94e8c9a31300877fd7b5b8aa895571a35369 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 14:46:32 +0900 Subject: [PATCH 120/167] Implement new difficulty calculator for Rulesets.Catch --- .../CatchDifficultyCalculatorTest.cs | 2 +- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- .../Difficulty/CatchDifficultyAttributes.cs | 4 +- .../Difficulty/CatchDifficultyCalculator.cs | 81 ++++++++++ .../Difficulty/CatchDifficultyHitObject.cs | 130 --------------- .../CatchLegacyDifficultyCalculator.cs | 148 ------------------ .../Preprocessing/CatchDifficultyHitObject.cs | 68 ++++++++ .../Difficulty/Skills/Movement.cs | 63 ++++++++ .../Objects/CatchHitObject.cs | 5 + 9 files changed, 221 insertions(+), 282 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs delete mode 100644 osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs delete mode 100644 osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs create mode 100644 osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs create mode 100644 osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 84f4fd9c99..61fb4ca5d1 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchLegacyDifficultyCalculator(new CatchRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap); protected override Ruleset CreateRuleset() => new CatchRuleset(); } diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 9b2bbc9bf7..c539a47897 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Catch public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; - public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchLegacyDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap); public override int? LegacyID => 2; diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index c6518cbf8a..8669543841 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty public double ApproachRate; public int MaxCombo; - public CatchDifficultyAttributes(Mod[] mods, double starRating) - : base(mods, starRating) + public CatchDifficultyAttributes(Mod[] mods) + : base(mods) { } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs new file mode 100644 index 0000000000..99702235d5 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -0,0 +1,81 @@ +// 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.Beatmaps; +using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; +using osu.Game.Rulesets.Catch.Difficulty.Skills; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Catch.Difficulty +{ + public class CatchDifficultyCalculator : DifficultyCalculator + { + private const double star_scaling_factor = 0.145; + + protected override int SectionLength => 750; + + private readonly float halfCatchWidth; + + public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) + { + var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty); + halfCatchWidth = catcher.CatchWidth * 0.5f; + } + + protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate) + { + var catchAttributes = (CatchDifficultyAttributes)attributes; + + // this is the same as osu!, so there's potential to share the implementation... maybe + double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; + + catchAttributes.StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor; + catchAttributes.ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0; + catchAttributes.MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)); + } + + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) + { + CatchHitObject lastObject = null; + + foreach (var hitObject in beatmap.HitObjects.OfType()) + { + if (lastObject == null) + { + lastObject = hitObject; + continue; + } + + switch (hitObject) + { + // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations. + case Fruit fruit: + yield return new CatchDifficultyHitObject(fruit, lastObject, timeRate, halfCatchWidth); + break; + case JuiceStream _: + foreach (var nested in hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet))) + yield return new CatchDifficultyHitObject(nested, lastObject, timeRate, halfCatchWidth); + break; + } + + lastObject = hitObject; + } + } + + protected override Skill[] CreateSkills() => new Skill[] + { + new Movement(), + }; + + protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new CatchDifficultyAttributes(mods); + } +} diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs deleted file mode 100644 index fb16e117b1..0000000000 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyHitObject.cs +++ /dev/null @@ -1,130 +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 System; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; -using osuTK; - -namespace osu.Game.Rulesets.Catch.Difficulty -{ - public class CatchDifficultyHitObject - { - internal static readonly double DECAY_BASE = 0.20; - private const float normalized_hitobject_radius = 41.0f; - private const float absolute_player_positioning_error = 16f; - private readonly float playerPositioningError; - - internal CatchHitObject BaseHitObject; - - /// - /// Measures jump difficulty. CtB doesn't have something like button pressing speed or accuracy - /// - internal double Strain = 1; - - /// - /// This is required to keep track of lazy player movement (always moving only as far as necessary) - /// Without this quick repeat sliders / weirdly shaped streams might become ridiculously overrated - /// - internal float PlayerPositionOffset; - internal float LastMovement; - - internal float NormalizedPosition; - internal float ActualNormalizedPosition => NormalizedPosition + PlayerPositionOffset; - - internal CatchDifficultyHitObject(CatchHitObject baseHitObject, float catcherWidthHalf) - { - BaseHitObject = baseHitObject; - - // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. - float scalingFactor = normalized_hitobject_radius / catcherWidthHalf; - - playerPositioningError = absolute_player_positioning_error; // * scalingFactor; - NormalizedPosition = baseHitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; - } - - private const double direction_change_bonus = 12.5; - internal void CalculateStrains(CatchDifficultyHitObject previousHitObject, double timeRate) - { - // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make. - // See Taiko feedback thread. - double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; - double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000); - - // Update new position with lazy movement. - PlayerPositionOffset = - MathHelper.Clamp( - previousHitObject.ActualNormalizedPosition, - NormalizedPosition - (normalized_hitobject_radius - playerPositioningError), - NormalizedPosition + (normalized_hitobject_radius - playerPositioningError)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player. - - NormalizedPosition; // Subtract HitObject position to obtain offset - - LastMovement = DistanceTo(previousHitObject); - double addition = spacingWeight(LastMovement); - - if (NormalizedPosition < previousHitObject.NormalizedPosition) - { - LastMovement = -LastMovement; - } - - CatchHitObject previousHitCircle = previousHitObject.BaseHitObject; - - double additionBonus = 0; - double sqrtTime = Math.Sqrt(Math.Max(timeElapsed, 25)); - - // Direction changes give an extra point! - if (Math.Abs(LastMovement) > 0.1) - { - if (Math.Abs(previousHitObject.LastMovement) > 0.1 && Math.Sign(LastMovement) != Math.Sign(previousHitObject.LastMovement)) - { - double bonus = direction_change_bonus / sqrtTime; - - // Weight bonus by how - double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError; - - // We want time to play a role twice here! - addition += bonus * bonusFactor; - - // Bonus for tougher direction switches and "almost" hyperdashes at this point - if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) - { - additionBonus += 0.3 * bonusFactor; - } - } - - // Base bonus for every movement, giving some weight to streams. - addition += 7.5 * Math.Min(Math.Abs(LastMovement), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtTime; - } - - // Bonus for "almost" hyperdashes at corner points - if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) - { - if (!previousHitCircle.HyperDash) - { - additionBonus += 1.0; - } - else - { - // After a hyperdash we ARE in the correct position. Always! - PlayerPositionOffset = 0; - } - - addition *= 1.0 + additionBonus * ((10 - previousHitCircle.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10); - } - - addition *= 850.0 / Math.Max(timeElapsed, 25); - - Strain = previousHitObject.Strain * decay + addition; - } - - private static double spacingWeight(float distance) - { - return Math.Pow(distance, 1.3) / 500; - } - - internal float DistanceTo(CatchDifficultyHitObject other) - { - return Math.Abs(ActualNormalizedPosition - other.ActualNormalizedPosition); - } - } -} diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs deleted file mode 100644 index 0a0897d97b..0000000000 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs +++ /dev/null @@ -1,148 +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 System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.UI; - -namespace osu.Game.Rulesets.Catch.Difficulty -{ - public class CatchLegacyDifficultyCalculator : LegacyDifficultyCalculator - { - /// - /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. - /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. - /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. - /// - private const double strain_step = 750; - - /// - /// The weighting of each strain value decays to this number * it's previous value - /// - private const double decay_weight = 0.94; - - private const double star_scaling_factor = 0.145; - - public CatchLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) - { - } - - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) - { - if (!beatmap.HitObjects.Any()) - return new CatchDifficultyAttributes(mods, 0); - - var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty); - float halfCatchWidth = catcher.CatchWidth * 0.5f; - - var difficultyHitObjects = new List(); - - foreach (var hitObject in beatmap.HitObjects) - { - switch (hitObject) - { - // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations. - case Fruit fruit: - difficultyHitObjects.Add(new CatchDifficultyHitObject(fruit, halfCatchWidth)); - break; - case JuiceStream _: - difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth))); - break; - } - } - - difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); - - if (!calculateStrainValues(difficultyHitObjects, timeRate)) - return new CatchDifficultyAttributes(mods, 0); - - // this is the same as osu!, so there's potential to share the implementation... maybe - double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; - double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor; - - return new CatchDifficultyAttributes(mods, starRating) - { - ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0, - MaxCombo = difficultyHitObjects.Count - }; - } - - private bool calculateStrainValues(List objects, double timeRate) - { - CatchDifficultyHitObject lastObject = null; - - if (!objects.Any()) return false; - - // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - foreach (var currentObject in objects) - { - if (lastObject != null) - currentObject.CalculateStrains(lastObject, timeRate); - - lastObject = currentObject; - } - - return true; - } - - private double calculateDifficulty(List objects, double timeRate) - { - // The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods - double actualStrainStep = strain_step * timeRate; - - // Find the highest strain value within each strain step - var highestStrains = new List(); - double intervalEndTime = actualStrainStep; - double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval - - CatchDifficultyHitObject previousHitObject = null; - foreach (CatchDifficultyHitObject hitObject in objects) - { - // While we are beyond the current interval push the currently available maximum to our strain list - while (hitObject.BaseHitObject.StartTime > intervalEndTime) - { - highestStrains.Add(maximumStrain); - - // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay - // until the beginning of the next interval. - if (previousHitObject == null) - { - maximumStrain = 0; - } - else - { - double decay = Math.Pow(CatchDifficultyHitObject.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); - maximumStrain = previousHitObject.Strain * decay; - } - - // Go to the next time interval - intervalEndTime += actualStrainStep; - } - - // Obtain maximum strain - maximumStrain = Math.Max(hitObject.Strain, maximumStrain); - - previousHitObject = hitObject; - } - - // Build the weighted sum over the highest strains for each interval - double difficulty = 0; - double weight = 1; - highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - foreach (double strain in highestStrains) - { - difficulty += weight * strain; - weight *= decay_weight; - } - - return difficulty; - } - } -} diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs new file mode 100644 index 0000000000..5dbd60d700 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -0,0 +1,68 @@ +// 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.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing +{ + public class CatchDifficultyHitObject : DifficultyHitObject + { + private const float normalized_hitobject_radius = 41.0f; + private const float absolute_player_positioning_error = 16f; + + public new CatchHitObject BaseObject => (CatchHitObject)base.BaseObject; + + public float MovementDistance { get; private set; } + /// + /// Milliseconds elapsed since the start time of the previous , with a minimum of 25ms. + /// + public readonly double StrainTime; + + private readonly float scalingFactor; + + private readonly CatchHitObject lastObject; + + public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate, float halfCatcherWidth) + : base(hitObject, lastObject, timeRate) + { + this.lastObject = (CatchHitObject)lastObject; + + // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. + scalingFactor = normalized_hitobject_radius / halfCatcherWidth; + + setDistances(); + + // Every strain interval is hard capped at the equivalent of 600 BPM streaming speed as a safety measure + StrainTime = Math.Max(25, DeltaTime); + } + + private void setDistances() + { + float lastPosition = getNormalizedPosition(lastObject); + float currentPosition = getNormalizedPosition(BaseObject); + + // After a hyperdash the player is assumed to be in the correct position + if (lastObject.LazyMovementDistance != null && !lastObject.HyperDash) + lastPosition += lastObject.LazyMovementDistance.Value; + + BaseObject.LazyMovementDistance = + MathHelper.Clamp( + lastPosition, + currentPosition - (normalized_hitobject_radius - absolute_player_positioning_error), + currentPosition + (normalized_hitobject_radius - absolute_player_positioning_error)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player. + - currentPosition; // Subtract HitObject position to obtain offset + + MovementDistance = Math.Abs(currentPosition - lastPosition + BaseObject.LazyMovementDistance.Value); + + if (currentPosition < lastPosition) + MovementDistance *= -1; + } + + private float getNormalizedPosition(CatchHitObject hitObject) => hitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; + } +} diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs new file mode 100644 index 0000000000..1626a9be1d --- /dev/null +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -0,0 +1,63 @@ +// 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.Game.Rulesets.Catch.Difficulty.Preprocessing; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; + +namespace osu.Game.Rulesets.Catch.Difficulty.Skills +{ + public class Movement : Skill + { + private const float absolute_player_positioning_error = 16f; + private const float normalized_hitobject_radius = 41.0f; + private const double direction_change_bonus = 12.5; + + protected override double SkillMultiplier => 850; + protected override double StrainDecayBase => 0.2; + + protected override double DecayWeight => 0.94; + + protected override double StrainValueOf(DifficultyHitObject current) + { + var catchCurrent = (CatchDifficultyHitObject)current; + var catchPrevious = (CatchDifficultyHitObject)Previous[0]; + + var sqrtStrain = Math.Sqrt(catchCurrent.StrainTime); + + double distanceAddition = Math.Pow(Math.Abs(catchCurrent.MovementDistance), 1.3) / 500; + + double bonus = 0; + + if (Math.Abs(catchCurrent.MovementDistance) > 0.1) + { + if (catchPrevious.MovementDistance > 0.1 && Math.Sign(catchCurrent.MovementDistance) != Math.Sign(catchPrevious.MovementDistance)) + { + double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(catchCurrent.MovementDistance)) / absolute_player_positioning_error; + + distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor; + + // Bonus for tougher direction switches and "almost" hyperdashes at this point + if (catchPrevious.BaseObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH) + bonus = 0.3 * bonusFactor; + } + + // Base bonus for every movement, giving some weight to streams. + distanceAddition += 7.5 * Math.Min(Math.Abs(catchCurrent.MovementDistance), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; + } + + // Bonus for "almost" hyperdashes at corner points + if (catchPrevious.BaseObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) + { + if (!catchPrevious.BaseObject.HyperDash) + bonus += 1.0; + + distanceAddition *= 1.0 + bonus * ((10 - catchPrevious.BaseObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10); + } + + return distanceAddition / catchCurrent.StrainTime; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 2153b8dc85..aff18619fb 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -50,6 +50,11 @@ namespace osu.Game.Rulesets.Catch.Objects /// public CatchHitObject HyperDashTarget; + /// + /// The minimum distance to be moved from the last to hit this . + /// + internal float? LazyMovementDistance; + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); From e02ae927b31f6eccbe673164cb7367d4515e8175 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 14:47:31 +0900 Subject: [PATCH 121/167] Fix nullrefs --- osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 1626a9be1d..8137437959 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -3,6 +3,7 @@ using System; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; @@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { var catchCurrent = (CatchDifficultyHitObject)current; - var catchPrevious = (CatchDifficultyHitObject)Previous[0]; + var catchPrevious = Previous.Count > 0 ? (CatchDifficultyHitObject)Previous[0] : null; var sqrtStrain = Math.Sqrt(catchCurrent.StrainTime); @@ -33,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills if (Math.Abs(catchCurrent.MovementDistance) > 0.1) { - if (catchPrevious.MovementDistance > 0.1 && Math.Sign(catchCurrent.MovementDistance) != Math.Sign(catchPrevious.MovementDistance)) + if (catchPrevious != null && catchPrevious.MovementDistance > 0.1 && Math.Sign(catchCurrent.MovementDistance) != Math.Sign(catchPrevious.MovementDistance)) { double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(catchCurrent.MovementDistance)) / absolute_player_positioning_error; @@ -49,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills } // Bonus for "almost" hyperdashes at corner points - if (catchPrevious.BaseObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) + if (catchPrevious?.BaseObject is Fruit && catchPrevious.BaseObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) { if (!catchPrevious.BaseObject.HyperDash) bonus += 1.0; From f6b13ca79dd271141ba6de8d18ec1fd1b69a599b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 16 Feb 2019 11:11:31 +0900 Subject: [PATCH 122/167] Rewrite catch diffcalc for readability + attempt to fix --- .../Preprocessing/CatchDifficultyHitObject.cs | 41 +++--------------- .../Difficulty/Skills/Movement.cs | 42 +++++++++++++------ 2 files changed, 36 insertions(+), 47 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index 5dbd60d700..6ce45bbbde 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -6,63 +6,34 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Objects; -using osuTK; namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing { public class CatchDifficultyHitObject : DifficultyHitObject { private const float normalized_hitobject_radius = 41.0f; - private const float absolute_player_positioning_error = 16f; public new CatchHitObject BaseObject => (CatchHitObject)base.BaseObject; - public float MovementDistance { get; private set; } + public new CatchHitObject LastObject => (CatchHitObject)base.LastObject; + + public readonly float NormalizedPosition; + /// /// Milliseconds elapsed since the start time of the previous , with a minimum of 25ms. /// public readonly double StrainTime; - private readonly float scalingFactor; - - private readonly CatchHitObject lastObject; - public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate, float halfCatcherWidth) : base(hitObject, lastObject, timeRate) { - this.lastObject = (CatchHitObject)lastObject; - // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. - scalingFactor = normalized_hitobject_radius / halfCatcherWidth; + var scalingFactor = normalized_hitobject_radius / halfCatcherWidth; - setDistances(); + NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; // Every strain interval is hard capped at the equivalent of 600 BPM streaming speed as a safety measure StrainTime = Math.Max(25, DeltaTime); } - - private void setDistances() - { - float lastPosition = getNormalizedPosition(lastObject); - float currentPosition = getNormalizedPosition(BaseObject); - - // After a hyperdash the player is assumed to be in the correct position - if (lastObject.LazyMovementDistance != null && !lastObject.HyperDash) - lastPosition += lastObject.LazyMovementDistance.Value; - - BaseObject.LazyMovementDistance = - MathHelper.Clamp( - lastPosition, - currentPosition - (normalized_hitobject_radius - absolute_player_positioning_error), - currentPosition + (normalized_hitobject_radius - absolute_player_positioning_error)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player. - - currentPosition; // Subtract HitObject position to obtain offset - - MovementDistance = Math.Abs(currentPosition - lastPosition + BaseObject.LazyMovementDistance.Value); - - if (currentPosition < lastPosition) - MovementDistance *= -1; - } - - private float getNormalizedPosition(CatchHitObject hitObject) => hitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor; } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs index 8137437959..e06ca08fbe 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs @@ -3,10 +3,10 @@ using System; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; -using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; +using osuTK; namespace osu.Game.Rulesets.Catch.Difficulty.Skills { @@ -21,43 +21,61 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills protected override double DecayWeight => 0.94; + private float lastPlayerPosition; + private float lastDistanceMoved; + protected override double StrainValueOf(DifficultyHitObject current) { var catchCurrent = (CatchDifficultyHitObject)current; - var catchPrevious = Previous.Count > 0 ? (CatchDifficultyHitObject)Previous[0] : null; - var sqrtStrain = Math.Sqrt(catchCurrent.StrainTime); + float playerPosition = MathHelper.Clamp( + lastPlayerPosition, + catchCurrent.NormalizedPosition - (normalized_hitobject_radius - absolute_player_positioning_error), + catchCurrent.NormalizedPosition + (normalized_hitobject_radius - absolute_player_positioning_error) + ); - double distanceAddition = Math.Pow(Math.Abs(catchCurrent.MovementDistance), 1.3) / 500; + float distanceMoved = playerPosition - lastPlayerPosition; + + double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 500; + double sqrtStrain = Math.Sqrt(catchCurrent.StrainTime); double bonus = 0; - if (Math.Abs(catchCurrent.MovementDistance) > 0.1) + // Direction changes give an extra point! + if (Math.Abs(distanceMoved) > 0.1) { - if (catchPrevious != null && catchPrevious.MovementDistance > 0.1 && Math.Sign(catchCurrent.MovementDistance) != Math.Sign(catchPrevious.MovementDistance)) + if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved)) { - double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(catchCurrent.MovementDistance)) / absolute_player_positioning_error; + double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(distanceMoved)) / absolute_player_positioning_error; distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor; // Bonus for tougher direction switches and "almost" hyperdashes at this point - if (catchPrevious.BaseObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH) + if (catchCurrent.LastObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH) bonus = 0.3 * bonusFactor; } // Base bonus for every movement, giving some weight to streams. - distanceAddition += 7.5 * Math.Min(Math.Abs(catchCurrent.MovementDistance), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; + distanceAddition += 7.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain; } // Bonus for "almost" hyperdashes at corner points - if (catchPrevious?.BaseObject is Fruit && catchPrevious.BaseObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) + if (catchCurrent.LastObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH) { - if (!catchPrevious.BaseObject.HyperDash) + if (!catchCurrent.LastObject.HyperDash) bonus += 1.0; + else + { + // After a hyperdash we ARE in the correct position. Always! + playerPosition = catchCurrent.NormalizedPosition; + } - distanceAddition *= 1.0 + bonus * ((10 - catchPrevious.BaseObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10); + distanceAddition *= 1.0 + bonus * ((10 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10); } + lastPlayerPosition = playerPosition; + lastDistanceMoved = distanceMoved; + return distanceAddition / catchCurrent.StrainTime; } } From 83cab2ba8ab23a476132993afb21c41253bd95f1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 14:48:19 +0900 Subject: [PATCH 123/167] Fix incorrect hitobject being used as the last hitobject --- .../Difficulty/CatchDifficultyCalculator.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 99702235d5..d4110ab1e1 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -60,14 +60,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations. case Fruit fruit: yield return new CatchDifficultyHitObject(fruit, lastObject, timeRate, halfCatchWidth); + lastObject = hitObject; break; case JuiceStream _: foreach (var nested in hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet))) + { yield return new CatchDifficultyHitObject(nested, lastObject, timeRate, halfCatchWidth); + lastObject = nested; + } break; } - - lastObject = hitObject; } } From 9463475202be48b011e955532de1596bcd1eb2b9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 14:51:30 +0900 Subject: [PATCH 124/167] Remove now unused member --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index aff18619fb..2153b8dc85 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -50,11 +50,6 @@ namespace osu.Game.Rulesets.Catch.Objects /// public CatchHitObject HyperDashTarget; - /// - /// The minimum distance to be moved from the last to hit this . - /// - internal float? LazyMovementDistance; - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); From 25d85b6eb4a53f32ff6687556d123a806e130365 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 14:54:21 +0900 Subject: [PATCH 125/167] Implement new difficulty calculator for Rulesets.Taiko --- .../TaikoDifficultyCalculatorTest.cs | 2 +- .../Preprocessing/TaikoDifficultyHitObject.cs | 20 +++ .../Difficulty/Skills/Strain.cs | 90 +++++++++++ .../Difficulty/TaikoDifficultyAttributes.cs | 4 +- .../Difficulty/TaikoDifficultyCalculator.cs | 56 +++++++ .../TaikoLegacyDifficultyCalculator.cs | 144 ------------------ .../Objects/TaikoHitObjectDifficulty.cs | 127 --------------- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- .../osu.Game.Rulesets.Taiko.csproj | 3 + 9 files changed, 173 insertions(+), 275 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs create mode 100644 osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs delete mode 100644 osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs delete mode 100644 osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index e00f3da0b7..16130c2c3d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoLegacyDifficultyCalculator(new TaikoRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap); protected override Ruleset CreateRuleset() => new TaikoRuleset(); } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs new file mode 100644 index 0000000000..4d63d81663 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing +{ + public class TaikoDifficultyHitObject : DifficultyHitObject + { + public readonly bool HasTypeChange; + + public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate) + : base(hitObject, lastObject, timeRate) + { + HasTypeChange = lastObject is RimHit != hitObject is RimHit; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs new file mode 100644 index 0000000000..c77e6a3afd --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs @@ -0,0 +1,90 @@ +// 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.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Difficulty.Skills +{ + public class Strain : Skill + { + private const double rhythm_change_base_threshold = 0.2; + private const double rhythm_change_base = 2.0; + + protected override double SkillMultiplier => 1; + protected override double StrainDecayBase => 0.3; + + private ColourSwitch lastColourSwitch = ColourSwitch.None; + + private int sameTypeCount; + + protected override double StrainValueOf(DifficultyHitObject current) + { + double addition = 1; + + // We get an extra addition if we are not a slider or spinner + if (Previous[0].BaseObject is Hit && current.BaseObject is Hit && current.BaseObject.StartTime - Previous[0].BaseObject.StartTime < 1000) + { + if (hasRhythmChange(current)) + addition += 1; + + if (hasColourChange(current)) + addition += 0.75; + } + + double additionFactor = 1; + + // Scale the addition factor linearly from 0.4 to 1 for DeltaTime from 0 to 50 + if (current.DeltaTime < 50) + additionFactor = 0.4 + 0.6 * current.DeltaTime / 50; + + return additionFactor * addition; + } + + private bool hasRhythmChange(DifficultyHitObject current) + { + // We don't want a division by zero if some random mapper decides to put two HitObjects at the same time. + if (current.DeltaTime == 0 || Previous[0].DeltaTime == 0) + return false; + + double timeElapsedRatio = Math.Max(Previous[0].DeltaTime / current.DeltaTime, current.DeltaTime / Previous[0].DeltaTime); + + if (timeElapsedRatio >= 8) + return false; + + double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0; + + return difference > rhythm_change_base_threshold && difference < 1 - rhythm_change_base_threshold; + } + + private bool hasColourChange(DifficultyHitObject current) + { + var taikoCurrent = (TaikoDifficultyHitObject)current; + + if (!taikoCurrent.HasTypeChange) + { + sameTypeCount++; + return false; + } + + var oldColourSwitch = lastColourSwitch; + var newColourSwitch = sameTypeCount % 2 == 0 ? ColourSwitch.Even : ColourSwitch.Odd; + + lastColourSwitch = newColourSwitch; + sameTypeCount = 1; + + // We only want a bonus if the parity of the color switch changes + return oldColourSwitch != ColourSwitch.None && oldColourSwitch != newColourSwitch; + } + + private enum ColourSwitch + { + None, + Even, + Odd + } + } +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index 3770f9601a..07721e2ac5 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public double GreatHitWindow; public int MaxCombo; - public TaikoDifficultyAttributes(Mod[] mods, double starRating) - : base(mods, starRating) + public TaikoDifficultyAttributes(Mod[] mods) + : base(mods) { } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs new file mode 100644 index 0000000000..3d18274bba --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -0,0 +1,56 @@ +// 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 System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing; +using osu.Game.Rulesets.Taiko.Difficulty.Skills; +using osu.Game.Rulesets.Taiko.Mods; +using osu.Game.Rulesets.Taiko.Objects; + +namespace osu.Game.Rulesets.Taiko.Difficulty +{ + public class TaikoDifficultyCalculator : DifficultyCalculator + { + private const double star_scaling_factor = 0.018; + + public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) + { + } + + protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate) + { + var taikoAttributes = (TaikoDifficultyAttributes)attributes; + + taikoAttributes.StarRating = skills.Single().DifficultyValue() * star_scaling_factor; + + // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future + taikoAttributes.GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; + taikoAttributes.MaxCombo = beatmap.HitObjects.Count(h => h is Hit); + } + + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) + { + for (int i = 1; i < beatmap.HitObjects.Count; i++) + yield return new TaikoDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], timeRate); + } + + protected override Skill[] CreateSkills() => new Skill[] { new Strain() }; + + protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new TaikoDifficultyAttributes(mods); + + protected override Mod[] DifficultyAdjustmentMods => new Mod[] + { + new TaikoModDoubleTime(), + new TaikoModHalfTime(), + new TaikoModEasy(), + new TaikoModHardRock(), + }; + } +} diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs deleted file mode 100644 index 650b367e34..0000000000 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs +++ /dev/null @@ -1,144 +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 System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Taiko.Mods; -using osu.Game.Rulesets.Taiko.Objects; - -namespace osu.Game.Rulesets.Taiko.Difficulty -{ - internal class TaikoLegacyDifficultyCalculator : LegacyDifficultyCalculator - { - private const double star_scaling_factor = 0.04125; - - /// - /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. - /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. - /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. - /// - private const double strain_step = 400; - - /// - /// The weighting of each strain value decays to this number * it's previous value - /// - private const double decay_weight = 0.9; - - public TaikoLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) - { - } - - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) - { - if (!beatmap.HitObjects.Any()) - return new TaikoDifficultyAttributes(mods, 0); - - var difficultyHitObjects = new List(); - - foreach (var hitObject in beatmap.HitObjects) - difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject)); - - // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. - difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); - - if (!calculateStrainValues(difficultyHitObjects, timeRate)) - return new TaikoDifficultyAttributes(mods, 0); - - double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor; - - return new TaikoDifficultyAttributes(mods, starRating) - { - // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future - GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate, - MaxCombo = beatmap.HitObjects.Count(h => h is Hit) - }; - } - - private bool calculateStrainValues(List objects, double timeRate) - { - // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - using (var hitObjectsEnumerator = objects.GetEnumerator()) - { - if (!hitObjectsEnumerator.MoveNext()) return false; - - TaikoHitObjectDifficulty current = hitObjectsEnumerator.Current; - - // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. - while (hitObjectsEnumerator.MoveNext()) - { - var next = hitObjectsEnumerator.Current; - next?.CalculateStrains(current, timeRate); - current = next; - } - - return true; - } - } - - private double calculateDifficulty(List objects, double timeRate) - { - double actualStrainStep = strain_step * timeRate; - - // Find the highest strain value within each strain step - List highestStrains = new List(); - double intervalEndTime = actualStrainStep; - double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval - - TaikoHitObjectDifficulty previousHitObject = null; - foreach (var hitObject in objects) - { - // While we are beyond the current interval push the currently available maximum to our strain list - while (hitObject.BaseHitObject.StartTime > intervalEndTime) - { - highestStrains.Add(maximumStrain); - - // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay - // until the beginning of the next interval. - if (previousHitObject == null) - { - maximumStrain = 0; - } - else - { - double decay = Math.Pow(TaikoHitObjectDifficulty.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); - maximumStrain = previousHitObject.Strain * decay; - } - - // Go to the next time interval - intervalEndTime += actualStrainStep; - } - - // Obtain maximum strain - maximumStrain = Math.Max(hitObject.Strain, maximumStrain); - - previousHitObject = hitObject; - } - - // Build the weighted sum over the highest strains for each interval - double difficulty = 0; - double weight = 1; - highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - foreach (double strain in highestStrains) - { - difficulty += weight * strain; - weight *= decay_weight; - } - - return difficulty; - } - - protected override Mod[] DifficultyAdjustmentMods => new Mod[] - { - new TaikoModDoubleTime(), - new TaikoModHalfTime(), - new TaikoModEasy(), - new TaikoModHardRock(), - }; - } -} diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs deleted file mode 100644 index 46dd7aa87f..0000000000 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObjectDifficulty.cs +++ /dev/null @@ -1,127 +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 System; - -namespace osu.Game.Rulesets.Taiko.Objects -{ - internal class TaikoHitObjectDifficulty - { - /// - /// Factor by how much individual / overall strain decays per second. - /// - /// - /// These values are results of tweaking a lot and taking into account general feedback. - /// - internal const double DECAY_BASE = 0.30; - - private const double type_change_bonus = 0.75; - private const double rhythm_change_bonus = 1.0; - private const double rhythm_change_base_threshold = 0.2; - private const double rhythm_change_base = 2.0; - - internal TaikoHitObject BaseHitObject; - - /// - /// Measures note density in a way - /// - internal double Strain = 1; - - private double timeElapsed; - private int sameTypeSince = 1; - - private bool isRim => BaseHitObject is RimHit; - - public TaikoHitObjectDifficulty(TaikoHitObject baseHitObject) - { - BaseHitObject = baseHitObject; - } - - internal void CalculateStrains(TaikoHitObjectDifficulty previousHitObject, double timeRate) - { - // Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make. - // See Taiko feedback thread. - timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; - double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000); - - double addition = 1; - - // Only if we are no slider or spinner we get an extra addition - if (previousHitObject.BaseHitObject is Hit && BaseHitObject is Hit - && BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime < 1000) // And we only want to check out hitobjects which aren't so far in the past - { - addition += typeChangeAddition(previousHitObject); - addition += rhythmChangeAddition(previousHitObject); - } - - double additionFactor = 1.0; - // Scale AdditionFactor linearly from 0.4 to 1 for TimeElapsed from 0 to 50 - if (timeElapsed < 50.0) - additionFactor = 0.4 + 0.6 * timeElapsed / 50.0; - - Strain = previousHitObject.Strain * decay + addition * additionFactor; - } - - private TypeSwitch lastTypeSwitchEven = TypeSwitch.None; - private double typeChangeAddition(TaikoHitObjectDifficulty previousHitObject) - { - // If we don't have the same hit type, trigger a type change! - if (previousHitObject.isRim != isRim) - { - lastTypeSwitchEven = previousHitObject.sameTypeSince % 2 == 0 ? TypeSwitch.Even : TypeSwitch.Odd; - - // We only want a bonus if the parity of the type switch changes! - switch (previousHitObject.lastTypeSwitchEven) - { - case TypeSwitch.Even: - if (lastTypeSwitchEven == TypeSwitch.Odd) - return type_change_bonus; - break; - case TypeSwitch.Odd: - if (lastTypeSwitchEven == TypeSwitch.Even) - return type_change_bonus; - break; - } - } - // No type change? Increment counter and keep track of last type switch - else - { - lastTypeSwitchEven = previousHitObject.lastTypeSwitchEven; - sameTypeSince = previousHitObject.sameTypeSince + 1; - } - - return 0; - } - - private double rhythmChangeAddition(TaikoHitObjectDifficulty previousHitObject) - { - // We don't want a division by zero if some random mapper decides to put 2 HitObjects at the same time. - if (timeElapsed == 0 || previousHitObject.timeElapsed == 0) - return 0; - - double timeElapsedRatio = Math.Max(previousHitObject.timeElapsed / timeElapsed, timeElapsed / previousHitObject.timeElapsed); - - if (timeElapsedRatio >= 8) - return 0; - - double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0; - - if (isWithinChangeThreshold(difference)) - return rhythm_change_bonus; - - return 0; - } - - private bool isWithinChangeThreshold(double value) - { - return value > rhythm_change_base_threshold && value < 1 - rhythm_change_base_threshold; - } - - private enum TypeSwitch - { - None, - Even, - Odd - } - } -} diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 77a53858fe..b4becae7c2 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Taiko public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; - public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoLegacyDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new TaikoPerformanceCalculator(this, beatmap, score); diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 002d6a7e8d..563ed2e7ca 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -10,4 +10,7 @@ + + + \ No newline at end of file From cb17cbcdc45d949a5ac610a15aa088bd7a828d9c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Feb 2019 14:06:48 +0900 Subject: [PATCH 126/167] Fix taiko nullrefing --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs index c77e6a3afd..7ff5684b86 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills double addition = 1; // We get an extra addition if we are not a slider or spinner - if (Previous[0].BaseObject is Hit && current.BaseObject is Hit && current.BaseObject.StartTime - Previous[0].BaseObject.StartTime < 1000) + if (current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000) { if (hasRhythmChange(current)) addition += 1; @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills private bool hasRhythmChange(DifficultyHitObject current) { // We don't want a division by zero if some random mapper decides to put two HitObjects at the same time. - if (current.DeltaTime == 0 || Previous[0].DeltaTime == 0) + if (current.DeltaTime == 0 || Previous.Count == 0 || Previous[0].DeltaTime == 0) return false; double timeElapsedRatio = Math.Max(Previous[0].DeltaTime / current.DeltaTime, current.DeltaTime / Previous[0].DeltaTime); From 46b979a412b68ca2eb2016176575fb1bbc554941 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 14:55:20 +0900 Subject: [PATCH 127/167] Fix colour changes not being reset --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs | 13 +++++++++---- .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs index 7ff5684b86..2465143b2b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills private ColourSwitch lastColourSwitch = ColourSwitch.None; - private int sameTypeCount; + private int sameTypeCount = 1; protected override double StrainValueOf(DifficultyHitObject current) { @@ -28,11 +28,16 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills // We get an extra addition if we are not a slider or spinner if (current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000) { - if (hasRhythmChange(current)) - addition += 1; - if (hasColourChange(current)) addition += 0.75; + + if (hasRhythmChange(current)) + addition += 1; + } + else + { + lastColourSwitch = ColourSwitch.None; + sameTypeCount = 1; } double additionFactor = 1; diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 3d18274bba..1cdb3495ae 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public class TaikoDifficultyCalculator : DifficultyCalculator { - private const double star_scaling_factor = 0.018; + private const double star_scaling_factor = 0.04125; public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) From 7f4643a83d58e01021beae9413be8a4c61a05fc3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 14:55:39 +0900 Subject: [PATCH 128/167] Adjust naming --- osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs index 2465143b2b..c6fe273b50 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills private ColourSwitch lastColourSwitch = ColourSwitch.None; - private int sameTypeCount = 1; + private int sameColourCount = 1; protected override double StrainValueOf(DifficultyHitObject current) { @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills else { lastColourSwitch = ColourSwitch.None; - sameTypeCount = 1; + sameColourCount = 1; } double additionFactor = 1; @@ -71,15 +71,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills if (!taikoCurrent.HasTypeChange) { - sameTypeCount++; + sameColourCount++; return false; } var oldColourSwitch = lastColourSwitch; - var newColourSwitch = sameTypeCount % 2 == 0 ? ColourSwitch.Even : ColourSwitch.Odd; + var newColourSwitch = sameColourCount % 2 == 0 ? ColourSwitch.Even : ColourSwitch.Odd; lastColourSwitch = newColourSwitch; - sameTypeCount = 1; + sameColourCount = 1; // We only want a bonus if the parity of the color switch changes return oldColourSwitch != ColourSwitch.None && oldColourSwitch != newColourSwitch; From fd702690218dbb43ae81cf33f35dddc04da0cddd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Feb 2019 16:03:28 +0900 Subject: [PATCH 129/167] Implement new difficulty calculator for Rulesets.Osu --- .../OsuDifficultyCalculatorTest.cs | 2 +- .../Difficulty/OsuDifficultyAttributes.cs | 4 +- .../Difficulty/OsuDifficultyCalculator.cs | 82 ++++++++++++++ .../OsuLegacyDifficultyCalculator.cs | 94 ---------------- .../Preprocessing/OsuDifficultyBeatmap.cs | 50 --------- .../Preprocessing/OsuDifficultyHitObject.cs | 56 +++------- .../Difficulty/Skills/Aim.cs | 34 +++--- .../Difficulty/Skills/Skill.cs | 103 ------------------ .../Difficulty/Skills/Speed.cs | 22 ++-- .../Difficulty/Utils/History.cs | 86 --------------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- 11 files changed, 137 insertions(+), 398 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs delete mode 100644 osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs delete mode 100644 osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs delete mode 100644 osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs delete mode 100644 osu.Game.Rulesets.Osu/Difficulty/Utils/History.cs diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index cc46ec7be3..edf3f35304 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuLegacyDifficultyCalculator(new OsuRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap); protected override Ruleset CreateRuleset() => new OsuRuleset(); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index fd54dc0260..9a9e72a056 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -14,8 +14,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty public double OverallDifficulty; public int MaxCombo; - public OsuDifficultyAttributes(Mod[] mods, double starRating) - : base(mods, starRating) + public OsuDifficultyAttributes(Mod[] mods) + : base(mods) { } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs new file mode 100644 index 0000000000..97a925360e --- /dev/null +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -0,0 +1,82 @@ +// 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.Beatmaps; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Difficulty.Skills; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Difficulty +{ + public class OsuDifficultyCalculator : DifficultyCalculator + { + private const double difficulty_multiplier = 0.0675; + + public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) + { + } + + protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate) + { + var osuAttributes = (OsuDifficultyAttributes)attributes; + + double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; + double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; + double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; + + // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future + double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; + double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; + + int maxCombo = beatmap.HitObjects.Count; + // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) + maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); + + osuAttributes.StarRating = starRating; + osuAttributes.AimStrain = aimRating; + osuAttributes.SpeedStrain = speedRating; + osuAttributes.ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5; + osuAttributes.OverallDifficulty = (80 - hitWindowGreat) / 6; + osuAttributes.MaxCombo = maxCombo; + } + + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) + { + // The first jump is formed by the first two hitobjects of the map. + // If the map has less than two OsuHitObjects, the enumerator will not return anything. + for (int i = 1; i < beatmap.HitObjects.Count; i++) + { + var lastLast = i > 1 ? beatmap.HitObjects[i - 2] : null; + var last = beatmap.HitObjects[i - 1]; + var current = beatmap.HitObjects[i]; + + yield return new OsuDifficultyHitObject(lastLast, last, current, timeRate); + } + } + + protected override Skill[] CreateSkills() => new Skill[] + { + new Aim(), + new Speed() + }; + + protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new OsuDifficultyAttributes(mods); + + protected override Mod[] DifficultyAdjustmentMods => new Mod[] + { + new OsuModDoubleTime(), + new OsuModHalfTime(), + new OsuModEasy(), + new OsuModHardRock(), + }; + } +} diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs deleted file mode 100644 index d01f75df6b..0000000000 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs +++ /dev/null @@ -1,94 +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 System; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; -using osu.Game.Rulesets.Osu.Difficulty.Skills; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.Difficulty -{ - public class OsuLegacyDifficultyCalculator : LegacyDifficultyCalculator - { - private const int section_length = 400; - private const double difficulty_multiplier = 0.0675; - - public OsuLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) - { - } - - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) - { - if (!beatmap.HitObjects.Any()) - return new OsuDifficultyAttributes(mods, 0); - - OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast().ToList(), timeRate); - Skill[] skills = - { - new Aim(), - new Speed() - }; - - double sectionLength = section_length * timeRate; - - // The first object doesn't generate a strain, so we begin with an incremented section end - double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength; - - foreach (OsuDifficultyHitObject h in difficultyBeatmap) - { - while (h.BaseObject.StartTime > currentSectionEnd) - { - foreach (Skill s in skills) - { - s.SaveCurrentPeak(); - s.StartNewSectionFrom(currentSectionEnd); - } - - currentSectionEnd += sectionLength; - } - - foreach (Skill s in skills) - s.Process(h); - } - - // The peak strain will not be saved for the last section in the above loop - foreach (Skill s in skills) - s.SaveCurrentPeak(); - - double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; - double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; - double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; - - // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future - double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; - double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; - - int maxCombo = beatmap.HitObjects.Count; - // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) - maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); - - return new OsuDifficultyAttributes(mods, starRating) - { - AimStrain = aimRating, - SpeedStrain = speedRating, - ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, - OverallDifficulty = (80 - hitWindowGreat) / 6, - MaxCombo = maxCombo - }; - } - - protected override Mod[] DifficultyAdjustmentMods => new Mod[] - { - new OsuModDoubleTime(), - new OsuModHalfTime(), - new OsuModEasy(), - new OsuModHardRock(), - }; - } -} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs deleted file mode 100644 index 068564d50c..0000000000 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs +++ /dev/null @@ -1,50 +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 System.Collections; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing -{ - /// - /// An enumerable container wrapping input as - /// which contains extra data required for difficulty calculation. - /// - public class OsuDifficultyBeatmap : IEnumerable - { - private readonly IEnumerator difficultyObjects; - - /// - /// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as - /// which contains extra data required for difficulty calculation. - /// - public OsuDifficultyBeatmap(List objects, double timeRate) - { - // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases. - // This should probably happen before the objects reach the difficulty calculator. - difficultyObjects = createDifficultyObjectEnumerator(objects.OrderBy(h => h.StartTime).ToList(), timeRate); - } - - /// - /// Returns an enumerator that enumerates all s in the . - /// - public IEnumerator GetEnumerator() => difficultyObjects; - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - private IEnumerator createDifficultyObjectEnumerator(List objects, double timeRate) - { - // The first jump is formed by the first two hitobjects of the map. - // If the map has less than two OsuHitObjects, the enumerator will not return anything. - for (int i = 1; i < objects.Count; i++) - { - var lastLast = i > 1 ? objects[i - 2] : null; - var last = objects[i - 1]; - var current = objects[i]; - - yield return new OsuDifficultyHitObject(lastLast, last, current, timeRate); - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 1ec12adb3b..31e69de6ab 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -1,24 +1,20 @@ -// 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.Linq; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osuTK; namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing { - /// - /// A wrapper around extending it with additional data required for difficulty calculation. - /// - public class OsuDifficultyHitObject + public class OsuDifficultyHitObject : DifficultyHitObject { private const int normalized_radius = 52; - /// - /// The this refers to. - /// - public OsuHitObject BaseObject { get; } + protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject; /// /// Normalized distance from the end position of the previous to the start position of this . @@ -30,40 +26,30 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing /// public double TravelDistance { get; private set; } - /// - /// Milliseconds elapsed since the StartTime of the previous . - /// - public double DeltaTime { get; private set; } - - /// - /// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms. - /// - public double StrainTime { get; private set; } - /// /// Angle the player has to take to hit this . /// Calculated as the angle between the circles (current-2, current-1, current). /// public double? Angle { get; private set; } + /// + /// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms. + /// + public readonly double StrainTime; + private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; - private readonly double timeRate; - /// - /// Initializes the object calculating extra data required for difficulty calculation. - /// - public OsuDifficultyHitObject(OsuHitObject lastLastObject, OsuHitObject lastObject, OsuHitObject currentObject, double timeRate) + public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double timeRate) + : base(hitObject, lastObject, timeRate) { - this.lastLastObject = lastLastObject; - this.lastObject = lastObject; - this.timeRate = timeRate; - - BaseObject = currentObject; + this.lastLastObject = (OsuHitObject)lastLastObject; + this.lastObject = (OsuHitObject)lastObject; setDistances(); - setTimingValues(); - // Calculate angle here + + // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure + StrainTime = Math.Max(50, DeltaTime); } private void setDistances() @@ -102,14 +88,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing } } - private void setTimingValues() - { - DeltaTime = (BaseObject.StartTime - lastObject.StartTime) / timeRate; - - // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure - StrainTime = Math.Max(50, DeltaTime); - } - private void computeSliderCursorPosition(Slider slider) { if (slider.LazyEndPosition != null) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index b5e57985e9..b2f2a3ac0b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; namespace osu.Game.Rulesets.Osu.Difficulty.Skills @@ -17,33 +19,37 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double SkillMultiplier => 26.25; protected override double StrainDecayBase => 0.15; - protected override double StrainValueOf(OsuDifficultyHitObject current) + protected override double StrainValueOf(DifficultyHitObject current) { + var osuCurrent = (OsuDifficultyHitObject)current; + double result = 0; - const double scale = 90; - - double applyDiminishingExp(double val) => Math.Pow(val, 0.99); - if (Previous.Count > 0) { - if (current.Angle != null && current.Angle.Value > angle_bonus_begin) + var osuPrevious = (OsuDifficultyHitObject)Previous[0]; + + if (osuCurrent.Angle != null && osuCurrent.Angle.Value > angle_bonus_begin) { + const double scale = 90; + var angleBonus = Math.Sqrt( - Math.Max(Previous[0].JumpDistance - scale, 0) - * Math.Pow(Math.Sin(current.Angle.Value - angle_bonus_begin), 2) - * Math.Max(current.JumpDistance - scale, 0)); - result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, Previous[0].StrainTime); + Math.Max(osuPrevious.JumpDistance - scale, 0) + * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) + * Math.Max(osuCurrent.JumpDistance - scale, 0)); + result = 1.5 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime); } } - double jumpDistanceExp = applyDiminishingExp(current.JumpDistance); - double travelDistanceExp = applyDiminishingExp(current.TravelDistance); + double jumpDistanceExp = applyDiminishingExp(osuCurrent.JumpDistance); + double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance); return Math.Max( - result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(current.StrainTime, timing_threshold), - (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / current.StrainTime + result + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold), + (Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime ); } + + private double applyDiminishingExp(double val) => Math.Pow(val, 0.99); } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs deleted file mode 100644 index 2f23552eb9..0000000000 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Skill.cs +++ /dev/null @@ -1,103 +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 System; -using System.Collections.Generic; -using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; -using osu.Game.Rulesets.Osu.Difficulty.Utils; -using osu.Game.Rulesets.Osu.Objects; - -namespace osu.Game.Rulesets.Osu.Difficulty.Skills -{ - /// - /// Used to processes strain values of s, keep track of strain levels caused by the processed objects - /// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects. - /// - public abstract class Skill - { - protected const double SINGLE_SPACING_THRESHOLD = 125; - protected const double STREAM_SPACING_THRESHOLD = 110; - - /// - /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. - /// - protected abstract double SkillMultiplier { get; } - - /// - /// Determines how quickly strain decays for the given skill. - /// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second. - /// - protected abstract double StrainDecayBase { get; } - - /// - /// s that were processed previously. They can affect the strain values of the following objects. - /// - protected readonly History Previous = new History(2); // Contained objects not used yet - - private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap. - private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. - private readonly List strainPeaks = new List(); - - /// - /// Process an and update current strain values accordingly. - /// - public void Process(OsuDifficultyHitObject current) - { - currentStrain *= strainDecay(current.DeltaTime); - if (!(current.BaseObject is Spinner)) - currentStrain += StrainValueOf(current) * SkillMultiplier; - - currentSectionPeak = Math.Max(currentStrain, currentSectionPeak); - - Previous.Push(current); - } - - /// - /// Saves the current peak strain level to the list of strain peaks, which will be used to calculate an overall difficulty. - /// - public void SaveCurrentPeak() - { - if (Previous.Count > 0) - strainPeaks.Add(currentSectionPeak); - } - - /// - /// Sets the initial strain level for a new section. - /// - /// The beginning of the new section in milliseconds - public void StartNewSectionFrom(double offset) - { - // The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries. - // This means we need to capture the strain level at the beginning of the new section, and use that as the initial peak level. - if (Previous.Count > 0) - currentSectionPeak = currentStrain * strainDecay(offset - Previous[0].BaseObject.StartTime); - } - - /// - /// Returns the calculated difficulty value representing all processed s. - /// - public double DifficultyValue() - { - strainPeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - double difficulty = 0; - double weight = 1; - - // Difficulty is the weighted sum of the highest strains from every section. - foreach (double strain in strainPeaks) - { - difficulty += strain * weight; - weight *= 0.9; - } - - return difficulty; - } - - /// - /// Calculates the strain value of an . This value is affected by previously processed objects. - /// - protected abstract double StrainValueOf(OsuDifficultyHitObject current); - - private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000); - } -} diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index e78691ce53..de9a541ac9 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; namespace osu.Game.Rulesets.Osu.Difficulty.Skills @@ -11,6 +13,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills /// public class Speed : Skill { + private const double single_spacing_threshold = 125; + private const double angle_bonus_begin = 5 * Math.PI / 6; private const double pi_over_4 = Math.PI / 4; private const double pi_over_2 = Math.PI / 2; @@ -22,9 +26,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills private const double max_speed_bonus = 45; // ~330BPM private const double speed_balancing_factor = 40; - protected override double StrainValueOf(OsuDifficultyHitObject current) + protected override double StrainValueOf(DifficultyHitObject current) { - double distance = Math.Min(SINGLE_SPACING_THRESHOLD, current.TravelDistance + current.JumpDistance); + var osuCurrent = (OsuDifficultyHitObject)current; + + double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); double deltaTime = Math.Max(max_speed_bonus, current.DeltaTime); double speedBonus = 1.0; @@ -32,20 +38,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); double angleBonus = 1.0; - if (current.Angle != null && current.Angle.Value < angle_bonus_begin) + if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) { - angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - current.Angle.Value)), 2) / 3.57; - if (current.Angle.Value < pi_over_2) + angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57; + if (osuCurrent.Angle.Value < pi_over_2) { angleBonus = 1.28; - if (distance < 90 && current.Angle.Value < pi_over_4) + if (distance < 90 && osuCurrent.Angle.Value < pi_over_4) angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1); else if (distance < 90) - angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - current.Angle.Value) / pi_over_4); + angleBonus += (1 - angleBonus) * Math.Min((90 - distance) / 10, 1) * Math.Sin((pi_over_2 - osuCurrent.Angle.Value) / pi_over_4); } } - return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / SINGLE_SPACING_THRESHOLD, 3.5)) / current.StrainTime; + return (1 + (speedBonus - 1) * 0.75) * angleBonus * (0.95 + speedBonus * Math.Pow(distance / single_spacing_threshold, 3.5)) / osuCurrent.StrainTime; } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Utils/History.cs b/osu.Game.Rulesets.Osu/Difficulty/Utils/History.cs deleted file mode 100644 index e39351087e..0000000000 --- a/osu.Game.Rulesets.Osu/Difficulty/Utils/History.cs +++ /dev/null @@ -1,86 +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 System; -using System.Collections; -using System.Collections.Generic; - -namespace osu.Game.Rulesets.Osu.Difficulty.Utils -{ - /// - /// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full. - /// Indexing starts at the top of the stack. - /// - public class History : IEnumerable - { - public int Count { get; private set; } - - private readonly T[] array; - private readonly int capacity; - private int marker; // Marks the position of the most recently added item. - - /// - /// Initializes a new instance of the History class that is empty and has the specified capacity. - /// - /// The number of items the History can hold. - public History(int capacity) - { - if (capacity < 0) - throw new ArgumentOutOfRangeException(); - - this.capacity = capacity; - array = new T[capacity]; - marker = capacity; // Set marker to the end of the array, outside of the indexed range by one. - } - - /// - /// The most recently added item is returned at index 0. - /// - public T this[int i] - { - get - { - if (i < 0 || i > Count - 1) - throw new IndexOutOfRangeException(); - - i += marker; - if (i > capacity - 1) - i -= capacity; - - return array[i]; - } - } - - /// - /// Adds the item as the most recent one in the history. - /// The oldest item is disposed if the history is full. - /// - public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition. - { - if (marker == 0) - marker = capacity - 1; - else - --marker; - - array[marker] = item; - - if (Count < capacity) - ++Count; - } - - /// - /// Returns an enumerator which enumerates items in the history starting from the most recently added one. - /// - public IEnumerator GetEnumerator() - { - for (int i = marker; i < capacity; ++i) - yield return array[i]; - - if (Count == capacity) - for (int i = 0; i < marker; ++i) - yield return array[i]; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6fa1532580..aff91e0dcb 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; - public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuLegacyDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap); public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new OsuPerformanceCalculator(this, beatmap, score); From c930cc5fb548760654f7dc9c7088fedcd309da1c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 14 Feb 2019 12:11:03 +0900 Subject: [PATCH 130/167] Fix incorrect OsuDifficultyHitObject instantiation --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 97a925360e..13d1621a39 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty var last = beatmap.HitObjects[i - 1]; var current = beatmap.HitObjects[i]; - yield return new OsuDifficultyHitObject(lastLast, last, current, timeRate); + yield return new OsuDifficultyHitObject(current, lastLast, last, timeRate); } } From 659ec267b6d582bbe2f00a38078590f49ccd9045 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 14:58:33 +0900 Subject: [PATCH 131/167] Fix spinners increasing strain --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 4 ++++ osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index b2f2a3ac0b..e74f4933b2 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -5,6 +5,7 @@ using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -21,6 +22,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { + if (current.BaseObject is Spinner) + return 0; + var osuCurrent = (OsuDifficultyHitObject)current; double result = 0; diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index de9a541ac9..46a81a9480 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -5,6 +5,7 @@ using System; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; +using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -28,6 +29,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { + if (current.BaseObject is Spinner) + return 0; + var osuCurrent = (OsuDifficultyHitObject)current; double distance = Math.Min(single_spacing_threshold, osuCurrent.TravelDistance + osuCurrent.JumpDistance); From 68725dc005f85773144031367904f618f9653931 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Feb 2019 15:49:30 +0900 Subject: [PATCH 132/167] Implement new difficulty calculator for Rulesets.Mania --- .../ManiaDifficultyCalculatorTest.cs | 2 +- .../Difficulty/ManiaDifficultyAttributes.cs | 4 +- .../Difficulty/ManiaDifficultyCalculator.cs | 96 ++++++++++ .../ManiaLegacyDifficultyCalculator.cs | 173 ------------------ .../Preprocessing/ManiaDifficultyHitObject.cs | 19 ++ .../Difficulty/Skills/Individual.cs | 47 +++++ .../Difficulty/Skills/Overall.cs | 56 ++++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../Objects/ManiaHitObjectDifficulty.cs | 112 ------------ 9 files changed, 222 insertions(+), 289 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs delete mode 100644 osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs create mode 100644 osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs create mode 100644 osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs create mode 100644 osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs delete mode 100644 osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs index ef660b9ea8..a5c7e051d3 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Tests public void Test(double expected, string name) => base.Test(expected, name); - protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaLegacyDifficultyCalculator(new ManiaRuleset(), beatmap); + protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap); protected override Ruleset CreateRuleset() => new ManiaRuleset(); } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index 2f614ea14b..4aa6cd730d 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -10,8 +10,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty { public double GreatHitWindow; - public ManiaDifficultyAttributes(Mod[] mods, double starRating) - : base(mods, starRating) + public ManiaDifficultyAttributes(Mod[] mods) + : base(mods) { } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs new file mode 100644 index 0000000000..0abb339607 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -0,0 +1,96 @@ +// 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 System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Difficulty; +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mania.Difficulty.Skills; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mods; + +namespace osu.Game.Rulesets.Mania.Difficulty +{ + public class ManiaDifficultyCalculator : DifficultyCalculator + { + private const double star_scaling_factor = 0.018; + + private int columnCount; + + private readonly bool isForCurrentRuleset; + + public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) + : base(ruleset, beatmap) + { + isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); + } + + protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate) + { + var maniaAttributes = (ManiaDifficultyAttributes)attributes; + + var overallStrain = skills.OfType().Single().DifficultyValue(); + var highestIndividual = skills.OfType().Max(s => s.DifficultyValue()); + + maniaAttributes.StarRating = (overallStrain + highestIndividual) * star_scaling_factor; + + // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future + maniaAttributes.GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; + } + + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) + { + columnCount = ((ManiaBeatmap)beatmap).TotalColumns; + + for (int i = 1; i < beatmap.HitObjects.Count; i++) + yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], timeRate); + } + + protected override Skill[] CreateSkills() + { + var skills = new List { new Overall(columnCount) }; + + for (int i = 0; i < columnCount; i++) + skills.Add(new Individual(i, columnCount)); + + return skills.ToArray(); + } + + protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new ManiaDifficultyAttributes(mods); + + protected override Mod[] DifficultyAdjustmentMods + { + get + { + var mods = new Mod[] + { + new ManiaModDoubleTime(), + new ManiaModHalfTime(), + new ManiaModEasy(), + new ManiaModHardRock(), + }; + + if (isForCurrentRuleset) + return mods; + + // if we are a convert, we can be played in any key mod. + return mods.Concat(new Mod[] + { + new ManiaModKey1(), + new ManiaModKey2(), + new ManiaModKey3(), + new ManiaModKey4(), + new ManiaModKey5(), + new ManiaModKey6(), + new ManiaModKey7(), + new ManiaModKey8(), + new ManiaModKey9(), + }).ToArray(); + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs deleted file mode 100644 index 02b03aca5d..0000000000 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs +++ /dev/null @@ -1,173 +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 System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Mods; -using osu.Game.Rulesets.Mania.Objects; -using osu.Game.Rulesets.Mods; - -namespace osu.Game.Rulesets.Mania.Difficulty -{ - internal class ManiaLegacyDifficultyCalculator : LegacyDifficultyCalculator - { - private const double star_scaling_factor = 0.018; - - /// - /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step. - /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. - /// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage. - /// - private const double strain_step = 400; - - /// - /// The weighting of each strain value decays to this number * it's previous value - /// - private const double decay_weight = 0.9; - - private readonly bool isForCurrentRuleset; - - public ManiaLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) - : base(ruleset, beatmap) - { - isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); - } - - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) - { - if (!beatmap.HitObjects.Any()) - return new ManiaDifficultyAttributes(mods, 0); - - var difficultyHitObjects = new List(); - - int columnCount = ((ManiaBeatmap)beatmap).TotalColumns; - - // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. - // Note: Stable sort is done so that the ordering of hitobjects with equal start times doesn't change - difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime)); - - if (!calculateStrainValues(difficultyHitObjects, timeRate)) - return new ManiaDifficultyAttributes(mods, 0); - - double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor; - - return new ManiaDifficultyAttributes(mods, starRating) - { - // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future - GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate - }; - } - - private bool calculateStrainValues(List objects, double timeRate) - { - // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - using (var hitObjectsEnumerator = objects.GetEnumerator()) - { - if (!hitObjectsEnumerator.MoveNext()) - return false; - - ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current; - - // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. - while (hitObjectsEnumerator.MoveNext()) - { - var next = hitObjectsEnumerator.Current; - next?.CalculateStrains(current, timeRate); - current = next; - } - - return true; - } - } - - private double calculateDifficulty(List objects, double timeRate) - { - double actualStrainStep = strain_step * timeRate; - - // Find the highest strain value within each strain step - List highestStrains = new List(); - double intervalEndTime = actualStrainStep; - double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval - - ManiaHitObjectDifficulty previousHitObject = null; - foreach (var hitObject in objects) - { - // While we are beyond the current interval push the currently available maximum to our strain list - while (hitObject.BaseHitObject.StartTime > intervalEndTime) - { - highestStrains.Add(maximumStrain); - - // The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay - // until the beginning of the next interval. - if (previousHitObject == null) - { - maximumStrain = 0; - } - else - { - double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); - double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000); - maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay; - } - - // Go to the next time interval - intervalEndTime += actualStrainStep; - } - - // Obtain maximum strain - double strain = hitObject.IndividualStrain + hitObject.OverallStrain; - maximumStrain = Math.Max(strain, maximumStrain); - - previousHitObject = hitObject; - } - - // Build the weighted sum over the highest strains for each interval - double difficulty = 0; - double weight = 1; - highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. - - foreach (double strain in highestStrains) - { - difficulty += weight * strain; - weight *= decay_weight; - } - - return difficulty; - } - - protected override Mod[] DifficultyAdjustmentMods - { - get - { - var mods = new Mod[] - { - new ManiaModDoubleTime(), - new ManiaModHalfTime(), - new ManiaModEasy(), - new ManiaModHardRock(), - }; - - if (isForCurrentRuleset) - return mods; - - // if we are a convert, we can be played in any key mod. - return mods.Concat(new Mod[] - { - new ManiaModKey1(), - new ManiaModKey2(), - new ManiaModKey3(), - new ManiaModKey4(), - new ManiaModKey5(), - new ManiaModKey6(), - new ManiaModKey7(), - new ManiaModKey8(), - new ManiaModKey9(), - }).ToArray(); - } - } - } -} diff --git a/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs b/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs new file mode 100644 index 0000000000..aa823e7586 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs @@ -0,0 +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 osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Mania.Difficulty.Preprocessing +{ + public class ManiaDifficultyHitObject : DifficultyHitObject + { + public new ManiaHitObject BaseObject => (ManiaHitObject)base.BaseObject; + + public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate) + : base(hitObject, lastObject, timeRate) + { + } + } +} diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs new file mode 100644 index 0000000000..059cd39641 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs @@ -0,0 +1,47 @@ +// 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 osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mania.Objects; + +namespace osu.Game.Rulesets.Mania.Difficulty.Skills +{ + public class Individual : Skill + { + protected override double SkillMultiplier => 1; + protected override double StrainDecayBase => 0.125; + + private readonly double[] holdEndTimes; + + private readonly int column; + + public Individual(int column, int columnCount) + { + this.column = column; + + holdEndTimes = new double[columnCount]; + } + + protected override double StrainValueOf(DifficultyHitObject current) + { + var maniaCurrent = (ManiaDifficultyHitObject)current; + var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime; + + try + { + if (maniaCurrent.BaseObject.Column != column) + return 0; + + // We give a slight bonus if something is held meanwhile + return holdEndTimes.Any(t => t > endTime) ? 2.5 : 2; + } + finally + { + holdEndTimes[maniaCurrent.BaseObject.Column] = endTime; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs new file mode 100644 index 0000000000..ed25173d38 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs @@ -0,0 +1,56 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Difficulty.Preprocessing; +using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; +using osu.Game.Rulesets.Mania.Objects; + +namespace osu.Game.Rulesets.Mania.Difficulty.Skills +{ + public class Overall : Skill + { + protected override double SkillMultiplier => 1; + protected override double StrainDecayBase => 0.3; + + private readonly double[] holdEndTimes; + + private readonly int columnCount; + + public Overall(int columnCount) + { + this.columnCount = columnCount; + + holdEndTimes = new double[columnCount]; + } + + protected override double StrainValueOf(DifficultyHitObject current) + { + var maniaCurrent = (ManiaDifficultyHitObject)current; + var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime; + + double holdFactor = 1.0; // Factor in case something else is held + double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly + + for (int i = 0; i < columnCount; i++) + { + // If there is at least one other overlapping end or note, then we get an addition, buuuuuut... + if (current.BaseObject.StartTime < holdEndTimes[i] && endTime > holdEndTimes[i]) + holdAddition = 1.0; + + // ... this addition only is valid if there is _no_ other note with the same ending. + // Releasing multiple notes at the same time is just as easy as releasing one + if (endTime == holdEndTimes[i]) + holdAddition = 0; + + // We give a slight bonus if something is held meanwhile + if (holdEndTimes[i] > endTime) + holdFactor = 1.25; + } + + holdEndTimes[maniaCurrent.BaseObject.Column] = endTime; + + return (1 + holdAddition) * holdFactor; + } + } +} diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 7a2a539a9d..d86ee19802 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Mania public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; - public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaLegacyDifficultyCalculator(this, beatmap); + public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap); public override int? LegacyID => 3; diff --git a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs b/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs deleted file mode 100644 index b6ea8c8665..0000000000 --- a/osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs +++ /dev/null @@ -1,112 +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 osu.Game.Rulesets.Objects.Types; -using System; - -namespace osu.Game.Rulesets.Mania.Objects -{ - internal class ManiaHitObjectDifficulty - { - /// - /// Factor by how much individual / overall strain decays per second. - /// - /// - /// These values are results of tweaking a lot and taking into account general feedback. - /// - internal const double INDIVIDUAL_DECAY_BASE = 0.125; - internal const double OVERALL_DECAY_BASE = 0.30; - - internal ManiaHitObject BaseHitObject; - - private readonly int beatmapColumnCount; - - private readonly double endTime; - private readonly double[] heldUntil; - - /// - /// Measures jacks or more generally: repeated presses of the same button - /// - private readonly double[] individualStrains; - - internal double IndividualStrain - { - get - { - return individualStrains[BaseHitObject.Column]; - } - - set - { - individualStrains[BaseHitObject.Column] = value; - } - } - - /// - /// Measures note density in a way - /// - internal double OverallStrain = 1; - - public ManiaHitObjectDifficulty(ManiaHitObject baseHitObject, int columnCount) - { - BaseHitObject = baseHitObject; - - endTime = (baseHitObject as IHasEndTime)?.EndTime ?? baseHitObject.StartTime; - - beatmapColumnCount = columnCount; - heldUntil = new double[beatmapColumnCount]; - individualStrains = new double[beatmapColumnCount]; - - for (int i = 0; i < beatmapColumnCount; ++i) - { - individualStrains[i] = 0; - heldUntil[i] = 0; - } - } - - internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate) - { - // TODO: Factor in holds - double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate; - double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000); - double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000); - - double holdFactor = 1.0; // Factor to all additional strains in case something else is held - double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly - - // Fill up the heldUntil array - for (int i = 0; i < beatmapColumnCount; ++i) - { - heldUntil[i] = previousHitObject.heldUntil[i]; - - // If there is at least one other overlapping end or note, then we get an addition, buuuuuut... - if (BaseHitObject.StartTime < heldUntil[i] && endTime > heldUntil[i]) - { - holdAddition = 1.0; - } - - // ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1 - if (endTime == heldUntil[i]) - { - holdAddition = 0; - } - - // We give a slight bonus to everything if something is held meanwhile - if (heldUntil[i] > endTime) - { - holdFactor = 1.25; - } - - // Decay individual strains - individualStrains[i] = previousHitObject.individualStrains[i] * individualDecay; - } - - heldUntil[BaseHitObject.Column] = endTime; - - // Increase individual strain in own column - IndividualStrain += 2.0 * holdFactor; - - OverallStrain = previousHitObject.OverallStrain * overallDecay + (1.0 + holdAddition) * holdFactor; - } - } -} From 9cce9ce97c7bfda1bfe328dee6be550ce2fde57c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 15:00:32 +0900 Subject: [PATCH 133/167] Consider aggregate peaks --- .../Difficulty/ManiaDifficultyCalculator.cs | 37 +++++++++++++++++-- osu.Game/Rulesets/Difficulty/Skills/Skill.cs | 6 +++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 0abb339607..523ac46515 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -33,15 +33,44 @@ namespace osu.Game.Rulesets.Mania.Difficulty { var maniaAttributes = (ManiaDifficultyAttributes)attributes; - var overallStrain = skills.OfType().Single().DifficultyValue(); - var highestIndividual = skills.OfType().Max(s => s.DifficultyValue()); - - maniaAttributes.StarRating = (overallStrain + highestIndividual) * star_scaling_factor; + maniaAttributes.StarRating = difficultyValue(skills) * star_scaling_factor; // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future maniaAttributes.GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; } + private double difficultyValue(Skill[] skills) + { + // Preprocess the strains to find the maximum overall + individual (aggregate) strain from each section + var overall = skills.OfType().Single(); + var aggregatePeaks = new List(Enumerable.Repeat(0.0, overall.StrainPeaks.Count)); + + foreach (var individual in skills.OfType()) + { + for (int i = 0; i < individual.StrainPeaks.Count; i++) + { + double aggregate = individual.StrainPeaks[i] + overall.StrainPeaks[i]; + + if (aggregate > aggregatePeaks[i]) + aggregatePeaks[i] = aggregate; + } + } + + aggregatePeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain. + + double difficulty = 0; + double weight = 1; + + // Difficulty is the weighted sum of the highest strains from every section. + foreach (double strain in aggregatePeaks) + { + difficulty += strain * weight; + weight *= 0.9; + } + + return difficulty; + } + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) { columnCount = ((ManiaBeatmap)beatmap).TotalColumns; diff --git a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs index fa7aa8f637..380531595a 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs @@ -14,6 +14,11 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// public abstract class Skill { + /// + /// The peak strain for each section of the beatmap. + /// + public IList StrainPeaks => strainPeaks; + /// /// Strain values are multiplied by this number for the given skill. Used to balance the value of different skills between each other. /// @@ -37,6 +42,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap. private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. + private readonly List strainPeaks = new List(); /// From b47ced8c583d88cbec7fd5c89adc4f58ca3254b0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 15:01:14 +0900 Subject: [PATCH 134/167] Fix failing test --- osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs index a5c7e051d3..61ee322ce2 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; - [TestCase(2.2676066895468976, "diffcalc-test")] + [TestCase(2.3683365342338796d, "diffcalc-test")] public void Test(double expected, string name) => base.Test(expected, name); From ddc1ad848e3334f68d0d56ae2ffc0234f75d15b2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 15:02:09 +0900 Subject: [PATCH 135/167] Fix failing test --- .../TaikoDifficultyCalculatorTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs index 16130c2c3d..a26b184766 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs @@ -13,8 +13,8 @@ namespace osu.Game.Rulesets.Taiko.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; - [TestCase(2.9811336589467095, "diffcalc-test")] - [TestCase(2.9811336589467095, "diffcalc-test-strong")] + [TestCase(2.9811338051242915d, "diffcalc-test")] + [TestCase(2.9811338051242915d, "diffcalc-test-strong")] public void Test(double expected, string name) => base.Test(expected, name); From 20f91106d9988154252a681d15841e3683594f47 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 18 Feb 2019 15:02:46 +0900 Subject: [PATCH 136/167] Fix failing test --- osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 61fb4ca5d1..c9831aad6d 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - [TestCase(3.8664391043534758, "diffcalc-test")] + [TestCase(3.8701854263563118d, "diffcalc-test")] public void Test(double expected, string name) => base.Test(expected, name); From 133c002d02829038d6b796dc9a4e73b9beb9e789 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Feb 2019 12:12:21 +0900 Subject: [PATCH 137/167] Fix test dlls being loaded as actual rulesets (and failing) --- osu.Game/Rulesets/RulesetStore.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 5283c5c3cf..0ebadd73d2 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -22,7 +22,8 @@ namespace osu.Game.Rulesets { AppDomain.CurrentDomain.AssemblyResolve += currentDomain_AssemblyResolve; - foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll")) + foreach (string file in Directory.GetFiles(Environment.CurrentDirectory, $"{ruleset_library_prefix}.*.dll") + .Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); } @@ -124,7 +125,7 @@ namespace osu.Game.Rulesets } catch (Exception e) { - Logger.Error(e, "Failed to load ruleset"); + Logger.Error(e, $"Failed to load ruleset {filename}"); } } } From af0bb4d5e8cdb14744c7330276c115c53af55a16 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 13:40:39 +0900 Subject: [PATCH 138/167] Remove mods from constructor --- osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs | 5 ++--- osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index b1a88b8abd..d808ee528e 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -7,13 +7,12 @@ namespace osu.Game.Rulesets.Difficulty { public class DifficultyAttributes { - public readonly Mod[] Mods; + public Mod[] Mods; public double StarRating; - public DifficultyAttributes(Mod[] mods) + public DifficultyAttributes() { - Mods = mods; } public DifficultyAttributes(Mod[] mods, double starRating) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index d7ae527bb1..30fc698664 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -25,7 +25,8 @@ namespace osu.Game.Rulesets.Difficulty protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) { - var attributes = CreateDifficultyAttributes(mods); + var attributes = CreateDifficultyAttributes(); + attributes.Mods = mods; if (!beatmap.HitObjects.Any()) return attributes; @@ -132,8 +133,7 @@ namespace osu.Game.Rulesets.Difficulty /// /// Creates an empty . /// - /// The s which difficulty is being processed with. /// The empty . - protected abstract DifficultyAttributes CreateDifficultyAttributes(Mod[] mods); + protected abstract DifficultyAttributes CreateDifficultyAttributes(); } } From 3784b673aec8a3139c622fb9935566834ee03756 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 13:51:19 +0900 Subject: [PATCH 139/167] History -> LimitedCapacityStack + re-xmldoc --- osu.Game/Rulesets/Difficulty/Skills/Skill.cs | 2 +- .../{History.cs => LimitedCapacityStack.cs} | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) rename osu.Game/Rulesets/Difficulty/Utils/{History.cs => LimitedCapacityStack.cs} (69%) diff --git a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs index fa7aa8f637..72bb686260 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// /// s that were processed previously. They can affect the strain values of the following objects. /// - protected readonly History Previous = new History(2); // Contained objects not used yet + protected readonly LimitedCapacityStack Previous = new LimitedCapacityStack(2); // Contained objects not used yet private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap. private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section. diff --git a/osu.Game/Rulesets/Difficulty/Utils/History.cs b/osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs similarity index 69% rename from osu.Game/Rulesets/Difficulty/Utils/History.cs rename to osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs index d6647d5119..95b7d9b19d 100644 --- a/osu.Game/Rulesets/Difficulty/Utils/History.cs +++ b/osu.Game/Rulesets/Difficulty/Utils/LimitedCapacityStack.cs @@ -8,11 +8,13 @@ using System.Collections.Generic; namespace osu.Game.Rulesets.Difficulty.Utils { /// - /// An indexed stack with Push() only, which disposes items at the bottom after the capacity is full. - /// Indexing starts at the top of the stack. + /// An indexed stack with limited depth. Indexing starts at the top of the stack. /// - public class History : IEnumerable + public class LimitedCapacityStack : IEnumerable { + /// + /// The number of elements in the stack. + /// public int Count { get; private set; } private readonly T[] array; @@ -20,10 +22,10 @@ namespace osu.Game.Rulesets.Difficulty.Utils private int marker; // Marks the position of the most recently added item. /// - /// Initializes a new instance of the History class that is empty and has the specified capacity. + /// Constructs a new . /// - /// The number of items the History can hold. - public History(int capacity) + /// The number of items the stack can hold. + public LimitedCapacityStack(int capacity) { if (capacity < 0) throw new ArgumentOutOfRangeException(); @@ -34,8 +36,9 @@ namespace osu.Game.Rulesets.Difficulty.Utils } /// - /// The most recently added item is returned at index 0. + /// Retrieves the item at an index in the stack. /// + /// The index of the item to retrieve. The top of the stack is returned at index 0. public T this[int i] { get @@ -52,11 +55,12 @@ namespace osu.Game.Rulesets.Difficulty.Utils } /// - /// Adds the item as the most recent one in the history. - /// The oldest item is disposed if the history is full. + /// Pushes an item to this . /// - public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition. + /// The item to push. + public void Push(T item) { + // Overwrite the oldest item instead of shifting every item by one with every addition. if (marker == 0) marker = capacity - 1; else From 9d8ba4073c8eb5f29674cc0539961904620f5f7c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 14:17:23 +0900 Subject: [PATCH 140/167] Add tests for LimitedCapacityStack --- .../NonVisual/LimitedCapacityStackTest.cs | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs diff --git a/osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs b/osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs new file mode 100644 index 0000000000..1c78b63499 --- /dev/null +++ b/osu.Game.Tests/NonVisual/LimitedCapacityStackTest.cs @@ -0,0 +1,115 @@ +// 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 NUnit.Framework; +using osu.Game.Rulesets.Difficulty.Utils; + +namespace osu.Game.Tests.NonVisual +{ + [TestFixture] + public class LimitedCapacityStackTest + { + private const int capacity = 3; + + private LimitedCapacityStack stack; + + [SetUp] + public void Setup() + { + stack = new LimitedCapacityStack(capacity); + } + + [Test] + public void TestEmptyStack() + { + Assert.AreEqual(0, stack.Count); + + Assert.Throws(() => + { + int unused = stack[0]; + }); + + int count = 0; + foreach (var unused in stack) + count++; + + Assert.AreEqual(0, count); + } + + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + public void TestInRangeElements(int count) + { + // e.g. 0 -> 1 -> 2 + for (int i = 0; i < count; i++) + stack.Push(i); + + Assert.AreEqual(count, stack.Count); + + // e.g. 2 -> 1 -> 0 (reverse order) + for (int i = 0; i < stack.Count; i++) + Assert.AreEqual(count - 1 - i, stack[i]); + + // e.g. indices 3, 4, 5, 6 (out of range) + for (int i = stack.Count; i < stack.Count + capacity; i++) + { + Assert.Throws(() => + { + int unused = stack[i]; + }); + } + } + + [TestCase(4)] + [TestCase(5)] + [TestCase(6)] + public void TestOverflowElements(int count) + { + // e.g. 0 -> 1 -> 2 -> 3 + for (int i = 0; i < count; i++) + stack.Push(i); + + Assert.AreEqual(capacity, stack.Count); + + // e.g. 3 -> 2 -> 1 (reverse order) + for (int i = 0; i < stack.Count; i++) + Assert.AreEqual(count - 1 - i, stack[i]); + + // e.g. indices 3, 4, 5, 6 (out of range) + for (int i = stack.Count; i < stack.Count + capacity; i++) + { + Assert.Throws(() => + { + int unused = stack[i]; + }); + } + } + + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + [TestCase(4)] + [TestCase(5)] + [TestCase(6)] + public void TestEnumerator(int count) + { + // e.g. 0 -> 1 -> 2 -> 3 + for (int i = 0; i < count; i++) + stack.Push(i); + + int enumeratorCount = 0; + int expectedValue = count - 1; + + foreach (var item in stack) + { + Assert.AreEqual(expectedValue, item); + enumeratorCount++; + expectedValue--; + } + + Assert.AreEqual(stack.Count, enumeratorCount); + } + } +} From 93b7b51d0a018dadbe52bff79c9d1ab69a3ce4dc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 14:29:23 +0900 Subject: [PATCH 141/167] timeRate -> clockRate --- .../CatchLegacyDifficultyCalculator.cs | 8 ++++---- .../ManiaLegacyDifficultyCalculator.cs | 8 ++++---- .../Difficulty/OsuLegacyDifficultyCalculator.cs | 10 +++++----- .../TaikoLegacyDifficultyCalculator.cs | 8 ++++---- .../DifficultyAdjustmentModCombinationsTest.cs | 2 +- .../Rulesets/Difficulty/DifficultyCalculator.cs | 16 ++++++++-------- .../Difficulty/LegacyDifficultyCalculator.cs | 4 ++-- .../Preprocessing/DifficultyHitObject.cs | 4 ++-- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs index 0a0897d97b..e1f17f309b 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyDifficultyCalculator.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty { } - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) + protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { if (!beatmap.HitObjects.Any()) return new CatchDifficultyAttributes(mods, 0); @@ -59,12 +59,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); - if (!calculateStrainValues(difficultyHitObjects, timeRate)) + if (!calculateStrainValues(difficultyHitObjects, clockRate)) return new CatchDifficultyAttributes(mods, 0); // this is the same as osu!, so there's potential to share the implementation... maybe - double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; - double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor; + double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; + double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, clockRate)) * star_scaling_factor; return new CatchDifficultyAttributes(mods, starRating) { diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs index 02b03aca5d..833454bf32 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyDifficultyCalculator.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); } - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) + protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { if (!beatmap.HitObjects.Any()) return new ManiaDifficultyAttributes(mods, 0); @@ -50,15 +50,15 @@ namespace osu.Game.Rulesets.Mania.Difficulty // Note: Stable sort is done so that the ordering of hitobjects with equal start times doesn't change difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime)); - if (!calculateStrainValues(difficultyHitObjects, timeRate)) + if (!calculateStrainValues(difficultyHitObjects, clockRate)) return new ManiaDifficultyAttributes(mods, 0); - double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor; + double starRating = calculateDifficulty(difficultyHitObjects, clockRate) * star_scaling_factor; return new ManiaDifficultyAttributes(mods, starRating) { // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future - GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate + GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate }; } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs index d01f75df6b..137630fb85 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyDifficultyCalculator.cs @@ -23,19 +23,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty { } - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) + protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { if (!beatmap.HitObjects.Any()) return new OsuDifficultyAttributes(mods, 0); - OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast().ToList(), timeRate); + OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast().ToList(), clockRate); Skill[] skills = { new Aim(), new Speed() }; - double sectionLength = section_length * timeRate; + double sectionLength = section_length * clockRate; // The first object doesn't generate a strain, so we begin with an incremented section end double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength; @@ -66,8 +66,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future - double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; - double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; + double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate; + double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs index 650b367e34..22ee39541b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyDifficultyCalculator.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { } - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) + protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { if (!beatmap.HitObjects.Any()) return new TaikoDifficultyAttributes(mods, 0); @@ -46,15 +46,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); - if (!calculateStrainValues(difficultyHitObjects, timeRate)) + if (!calculateStrainValues(difficultyHitObjects, clockRate)) return new TaikoDifficultyAttributes(mods, 0); - double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor; + double starRating = calculateDifficulty(difficultyHitObjects, clockRate) * star_scaling_factor; return new TaikoDifficultyAttributes(mods, starRating) { // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future - GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate, + GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, MaxCombo = beatmap.HitObjects.Count(h => h is Hit) }; } diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index f57f25e1ff..cc73cd54a2 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -146,7 +146,7 @@ namespace osu.Game.Tests.NonVisual protected override Mod[] DifficultyAdjustmentMods { get; } - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => throw new NotImplementedException(); + protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) => throw new NotImplementedException(); } } } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 30fc698664..ecfca9c589 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Difficulty { } - protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) + protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { var attributes = CreateDifficultyAttributes(); attributes.Mods = mods; @@ -31,10 +31,10 @@ namespace osu.Game.Rulesets.Difficulty if (!beatmap.HitObjects.Any()) return attributes; - var difficultyHitObjects = CreateDifficultyHitObjects(beatmap, timeRate).OrderBy(h => h.BaseObject.StartTime).ToList(); + var difficultyHitObjects = CreateDifficultyHitObjects(beatmap, clockRate).OrderBy(h => h.BaseObject.StartTime).ToList(); var skills = CreateSkills(); - double sectionLength = SectionLength * timeRate; + double sectionLength = SectionLength * clockRate; // The first object doesn't generate a strain, so we begin with an incremented section end double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength; @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Difficulty foreach (Skill s in skills) s.SaveCurrentPeak(); - PopulateAttributes(attributes, beatmap, skills, timeRate); + PopulateAttributes(attributes, beatmap, skills, clockRate); return attributes; } @@ -113,16 +113,16 @@ namespace osu.Game.Rulesets.Difficulty /// The to populate with information about the difficulty of . /// The whose difficulty was processed. /// The skills which processed the difficulty. - /// The rate of time in . - protected abstract void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate); + /// The rate at which the gameplay clock is run at. + protected abstract void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double clockRate); /// /// Enumerates s to be processed from s in the . /// /// The providing the s to enumerate. - /// The rate of time in . + /// The rate at which the gameplay clock is run at. /// The enumerated s. - protected abstract IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate); + protected abstract IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate); /// /// Creates the s to calculate the difficulty of s. diff --git a/osu.Game/Rulesets/Difficulty/LegacyDifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/LegacyDifficultyCalculator.cs index 15565ef847..a1324601aa 100644 --- a/osu.Game/Rulesets/Difficulty/LegacyDifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/LegacyDifficultyCalculator.cs @@ -100,8 +100,8 @@ namespace osu.Game.Rulesets.Difficulty /// /// The to compute the difficulty for. /// The s that should be applied. - /// The rate of time in . + /// The rate at which the gameplay clock is run at. /// A structure containing the difficulty attributes. - protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate); + protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate); } } diff --git a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs index 77c9b7e47f..23a8740f3d 100644 --- a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs +++ b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs @@ -22,11 +22,11 @@ namespace osu.Game.Rulesets.Difficulty.Preprocessing /// public readonly HitObject LastObject; - public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate) + public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate) { BaseObject = hitObject; LastObject = lastObject; - DeltaTime = (hitObject.StartTime - lastObject.StartTime) / timeRate; + DeltaTime = (hitObject.StartTime - lastObject.StartTime) / clockRate; } } } From 7ed461aa8c31a55775fde83f39d273c8b4383542 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 14:30:59 +0900 Subject: [PATCH 142/167] XMLDoc DifficultyHitObject --- .../Preprocessing/DifficultyHitObject.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs index 23a8740f3d..ebbffb5143 100644 --- a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs +++ b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs @@ -5,23 +5,32 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Difficulty.Preprocessing { + /// + /// Wraps a and provides additional information to be used for difficulty calculation. + /// public class DifficultyHitObject { /// - /// Milliseconds elapsed since the of the previous . - /// - public double DeltaTime { get; private set; } - - /// - /// The this refers to. + /// The this wraps. /// public readonly HitObject BaseObject; /// - /// The previous to . + /// The last which occurs before . /// public readonly HitObject LastObject; + /// + /// Amount of time elapsed between and . + /// + public readonly double DeltaTime; + + /// + /// Creates a new . + /// + /// The which this wraps. + /// The last which occurs before in the beatmap. + /// The rate at which the gameplay clock is run at. public DifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate) { BaseObject = hitObject; From ade5763160835ef79f0794e709dd2da820f0b16c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 14:34:02 +0900 Subject: [PATCH 143/167] Fix post-merge errors --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 6 ------ osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 9a9e72a056..6e991a1d08 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Difficulty { @@ -13,10 +12,5 @@ namespace osu.Game.Rulesets.Osu.Difficulty public double ApproachRate; public double OverallDifficulty; public int MaxCombo; - - public OsuDifficultyAttributes(Mod[] mods) - : base(mods) - { - } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 13d1621a39..9bbdb75343 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty new Speed() }; - protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new OsuDifficultyAttributes(mods); + protected override DifficultyAttributes CreateDifficultyAttributes() => new OsuDifficultyAttributes(); protected override Mod[] DifficultyAdjustmentMods => new Mod[] { From 0609fcf7d4a2a06af0b04d91a6432aebe3008494 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Feb 2019 15:52:53 +0900 Subject: [PATCH 144/167] Fix TestCaseMultiScreen intermittent failures --- osu.Game.Tests/Visual/TestCaseMultiScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs index fc4037f58b..8faaefb0bd 100644 --- a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs +++ b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs @@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual Multiplayer multi = new Multiplayer(); AddStep(@"show", () => LoadScreen(multi)); - AddWaitStep(5); + AddUntilStep(() => multi.IsCurrentScreen(), "wait until current"); AddStep(@"exit", multi.Exit); } } From bf1782636368d6428d0c9b7219fd22dcc858bd34 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 16:30:29 +0900 Subject: [PATCH 145/167] Fix post-merge errors --- .../Difficulty/ManiaDifficultyAttributes.cs | 6 ------ .../Difficulty/ManiaDifficultyCalculator.cs | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs index 4aa6cd730d..3ff665d2c8 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyAttributes.cs @@ -2,17 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Difficulty { public class ManiaDifficultyAttributes : DifficultyAttributes { public double GreatHitWindow; - - public ManiaDifficultyAttributes(Mod[] mods) - : base(mods) - { - } } } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 523ac46515..1e7e16329a 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty return skills.ToArray(); } - protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new ManiaDifficultyAttributes(mods); + protected override DifficultyAttributes CreateDifficultyAttributes() => new ManiaDifficultyAttributes(); protected override Mod[] DifficultyAdjustmentMods { From 7ba0d090fc2a1491050c05c48219add0ad0306a8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 16:40:52 +0900 Subject: [PATCH 146/167] Fix post-merge errors --- .../Difficulty/TaikoDifficultyAttributes.cs | 6 ------ .../Difficulty/TaikoDifficultyCalculator.cs | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs index 07721e2ac5..75d3807bba 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyAttributes.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Difficulty { @@ -10,10 +9,5 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { public double GreatHitWindow; public int MaxCombo; - - public TaikoDifficultyAttributes(Mod[] mods) - : base(mods) - { - } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 1cdb3495ae..e2c0ea7f46 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override Skill[] CreateSkills() => new Skill[] { new Strain() }; - protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new TaikoDifficultyAttributes(mods); + protected override DifficultyAttributes CreateDifficultyAttributes() => new TaikoDifficultyAttributes(); protected override Mod[] DifficultyAdjustmentMods => new Mod[] { From 3abb281ad570ceebdd58a5d53457e031f24fd239 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 16:41:53 +0900 Subject: [PATCH 147/167] Fix post-merge errors --- .../Difficulty/CatchDifficultyAttributes.cs | 6 ------ .../Difficulty/CatchDifficultyCalculator.cs | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs index 8669543841..75f5b18607 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyAttributes.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Difficulty; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Catch.Difficulty { @@ -10,10 +9,5 @@ namespace osu.Game.Rulesets.Catch.Difficulty { public double ApproachRate; public int MaxCombo; - - public CatchDifficultyAttributes(Mod[] mods) - : base(mods) - { - } } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index d4110ab1e1..f8ca066a4f 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -12,7 +12,6 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; -using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Catch.Difficulty { @@ -78,6 +77,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty new Movement(), }; - protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new CatchDifficultyAttributes(mods); + protected override DifficultyAttributes CreateDifficultyAttributes() => new CatchDifficultyAttributes(); } } From 618455f7ba3496591bc8ea2e8859e7ae74de3328 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Feb 2019 16:47:59 +0900 Subject: [PATCH 148/167] Remove exit step (needs login to show properly) --- osu.Game.Tests/Visual/TestCaseMultiScreen.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs index 8faaefb0bd..ff95d5836d 100644 --- a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs +++ b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs @@ -26,8 +26,6 @@ namespace osu.Game.Tests.Visual Multiplayer multi = new Multiplayer(); AddStep(@"show", () => LoadScreen(multi)); - AddUntilStep(() => multi.IsCurrentScreen(), "wait until current"); - AddStep(@"exit", multi.Exit); } } } From 4504aee089f3a2dd6400ef98aaf3d8705196a353 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Feb 2019 16:50:56 +0900 Subject: [PATCH 149/167] Unnecessary using --- osu.Game.Tests/Visual/TestCaseMultiScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs index ff95d5836d..804e3c5b1f 100644 --- a/osu.Game.Tests/Visual/TestCaseMultiScreen.cs +++ b/osu.Game.Tests/Visual/TestCaseMultiScreen.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.Screens; using osu.Game.Screens.Multi; using osu.Game.Screens.Multi.Lounge; using osu.Game.Screens.Multi.Lounge.Components; From ca8b7f24b46f69c92478408f42a9a549a86d0e2c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:36:33 +0900 Subject: [PATCH 150/167] Remove PopulateAttributes() --- .../Difficulty/DifficultyCalculator.cs | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index ecfca9c589..c6e54c52e7 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -25,14 +25,12 @@ namespace osu.Game.Rulesets.Difficulty protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { - var attributes = CreateDifficultyAttributes(); - attributes.Mods = mods; + var skills = CreateSkills(); if (!beatmap.HitObjects.Any()) - return attributes; + return CreateDifficultyAttributes(beatmap, mods, skills, clockRate); var difficultyHitObjects = CreateDifficultyHitObjects(beatmap, clockRate).OrderBy(h => h.BaseObject.StartTime).ToList(); - var skills = CreateSkills(); double sectionLength = SectionLength * clockRate; @@ -60,9 +58,7 @@ namespace osu.Game.Rulesets.Difficulty foreach (Skill s in skills) s.SaveCurrentPeak(); - PopulateAttributes(attributes, beatmap, skills, clockRate); - - return attributes; + return CreateDifficultyAttributes(beatmap, mods, skills, clockRate); } /// @@ -108,13 +104,13 @@ namespace osu.Game.Rulesets.Difficulty protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty(); /// - /// Populates after difficulty has been processed. + /// Creates to describe beatmap's calculated difficulty. /// - /// The to populate with information about the difficulty of . /// The whose difficulty was processed. + /// The s that were applied during the process. /// The skills which processed the difficulty. /// The rate at which the gameplay clock is run at. - protected abstract void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double clockRate); + protected abstract DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate); /// /// Enumerates s to be processed from s in the . @@ -129,11 +125,5 @@ namespace osu.Game.Rulesets.Difficulty /// /// The s. protected abstract Skill[] CreateSkills(); - - /// - /// Creates an empty . - /// - /// The empty . - protected abstract DifficultyAttributes CreateDifficultyAttributes(); } } From 847f7d8658f1124dbac22b7c2d778538cb200b95 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:38:33 +0900 Subject: [PATCH 151/167] Adjust with PopulateAttributes() removal --- .../Difficulty/OsuDifficultyCalculator.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 9bbdb75343..70ee7e251d 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -25,28 +25,32 @@ namespace osu.Game.Rulesets.Osu.Difficulty { } - protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate) + protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - var osuAttributes = (OsuDifficultyAttributes)attributes; + if (beatmap.HitObjects.Count == 0) + return new OsuDifficultyAttributes(); double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future - double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; - double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; + double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate; + double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); - osuAttributes.StarRating = starRating; - osuAttributes.AimStrain = aimRating; - osuAttributes.SpeedStrain = speedRating; - osuAttributes.ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5; - osuAttributes.OverallDifficulty = (80 - hitWindowGreat) / 6; - osuAttributes.MaxCombo = maxCombo; + return new OsuDifficultyAttributes + { + StarRating = starRating, + AimStrain = aimRating, + SpeedStrain = speedRating, + ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, + OverallDifficulty = (80 - hitWindowGreat) / 6, + MaxCombo = maxCombo + }; } protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) @@ -69,8 +73,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty new Speed() }; - protected override DifficultyAttributes CreateDifficultyAttributes() => new OsuDifficultyAttributes(); - protected override Mod[] DifficultyAdjustmentMods => new Mod[] { new OsuModDoubleTime(), From 37f9ac6eca7b5816c0210f93ee7ef2ce40a1bc78 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:39:30 +0900 Subject: [PATCH 152/167] Populate mods too --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 70ee7e251d..63d172d22b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -45,6 +45,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty return new OsuDifficultyAttributes { StarRating = starRating, + Mods = mods, AimStrain = aimRating, SpeedStrain = speedRating, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, From f19a52b960c19f767ea134a8c5cf9b80901c46c2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:40:35 +0900 Subject: [PATCH 153/167] Rename argument --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 63d172d22b..b146d201cc 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty }; } - protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { // The first jump is formed by the first two hitobjects of the map. // If the map has less than two OsuHitObjects, the enumerator will not return anything. @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty var last = beatmap.HitObjects[i - 1]; var current = beatmap.HitObjects[i]; - yield return new OsuDifficultyHitObject(current, lastLast, last, timeRate); + yield return new OsuDifficultyHitObject(current, lastLast, last, clockRate); } } From 2765ffa19093b796b38e8bf0efe3bcd2d7ebb486 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:42:24 +0900 Subject: [PATCH 154/167] Update with PopulateAttributes() removal --- .../Difficulty/CatchDifficultyCalculator.cs | 25 +++++++++++-------- .../Preprocessing/CatchDifficultyHitObject.cs | 4 +-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index f8ca066a4f..d29dd4591e 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; +using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Catch.Difficulty { @@ -30,19 +31,23 @@ namespace osu.Game.Rulesets.Catch.Difficulty halfCatchWidth = catcher.CatchWidth * 0.5f; } - protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate) + protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - var catchAttributes = (CatchDifficultyAttributes)attributes; + if (beatmap.HitObjects.Count == 0) + return new CatchDifficultyAttributes(); // this is the same as osu!, so there's potential to share the implementation... maybe - double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; + double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; - catchAttributes.StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor; - catchAttributes.ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0; - catchAttributes.MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)); + return new CatchDifficultyAttributes + { + StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor, + ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0, + MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)) + }; } - protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { CatchHitObject lastObject = null; @@ -58,13 +63,13 @@ namespace osu.Game.Rulesets.Catch.Difficulty { // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations. case Fruit fruit: - yield return new CatchDifficultyHitObject(fruit, lastObject, timeRate, halfCatchWidth); + yield return new CatchDifficultyHitObject(fruit, lastObject, clockRate, halfCatchWidth); lastObject = hitObject; break; case JuiceStream _: foreach (var nested in hitObject.NestedHitObjects.OfType().Where(o => !(o is TinyDroplet))) { - yield return new CatchDifficultyHitObject(nested, lastObject, timeRate, halfCatchWidth); + yield return new CatchDifficultyHitObject(nested, lastObject, clockRate, halfCatchWidth); lastObject = nested; } break; @@ -76,7 +81,5 @@ namespace osu.Game.Rulesets.Catch.Difficulty { new Movement(), }; - - protected override DifficultyAttributes CreateDifficultyAttributes() => new CatchDifficultyAttributes(); } } diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index 6ce45bbbde..6d00bb27b5 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -24,8 +24,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing /// public readonly double StrainTime; - public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate, float halfCatcherWidth) - : base(hitObject, lastObject, timeRate) + public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth) + : base(hitObject, lastObject, clockRate) { // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. var scalingFactor = normalized_hitobject_radius / halfCatcherWidth; From 8459cf6ed0f28c08e0da4aebf8ce26d63c284bd6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:43:12 +0900 Subject: [PATCH 155/167] Missed argument --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 31e69de6ab..930c711783 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing private readonly OsuHitObject lastLastObject; private readonly OsuHitObject lastObject; - public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double timeRate) - : base(hitObject, lastObject, timeRate) + public OsuDifficultyHitObject(HitObject hitObject, HitObject lastLastObject, HitObject lastObject, double clockRate) + : base(hitObject, lastObject, clockRate) { this.lastLastObject = (OsuHitObject)lastLastObject; this.lastObject = (OsuHitObject)lastObject; From 0ef15f5bd5156b04cd972e553ebd64c9f6b7ca67 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:45:16 +0900 Subject: [PATCH 156/167] Update with PopulateAttributes() removal --- .../Preprocessing/TaikoDifficultyHitObject.cs | 4 ++-- .../Difficulty/TaikoDifficultyCalculator.cs | 24 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs index 4d63d81663..24345275c1 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/Preprocessing/TaikoDifficultyHitObject.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing { public readonly bool HasTypeChange; - public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate) - : base(hitObject, lastObject, timeRate) + public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate) + : base(hitObject, lastObject, clockRate) { HasTypeChange = lastObject is RimHit != hitObject is RimHit; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index e2c0ea7f46..d973b80e12 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -24,27 +24,29 @@ namespace osu.Game.Rulesets.Taiko.Difficulty { } - protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate) + protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - var taikoAttributes = (TaikoDifficultyAttributes)attributes; + if (beatmap.HitObjects.Count == 0) + return new TaikoDifficultyAttributes(); - taikoAttributes.StarRating = skills.Single().DifficultyValue() * star_scaling_factor; - - // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - taikoAttributes.GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; - taikoAttributes.MaxCombo = beatmap.HitObjects.Count(h => h is Hit); + return new TaikoDifficultyAttributes + { + StarRating = skills.Single().DifficultyValue() * star_scaling_factor, + Mods = mods, + // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future + GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, + MaxCombo = beatmap.HitObjects.Count(h => h is Hit), + }; } - protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { for (int i = 1; i < beatmap.HitObjects.Count; i++) - yield return new TaikoDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], timeRate); + yield return new TaikoDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate); } protected override Skill[] CreateSkills() => new Skill[] { new Strain() }; - protected override DifficultyAttributes CreateDifficultyAttributes() => new TaikoDifficultyAttributes(); - protected override Mod[] DifficultyAdjustmentMods => new Mod[] { new TaikoModDoubleTime(), From 1a645b511592f6a1b275b69d24e874c4c939369b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:45:52 +0900 Subject: [PATCH 157/167] Fix mods not being populated --- .../Difficulty/CatchDifficultyCalculator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index d29dd4591e..9e6511b362 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new CatchDifficultyAttributes(); + return new CatchDifficultyAttributes { Mods = mods }; // this is the same as osu!, so there's potential to share the implementation... maybe double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate; @@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty return new CatchDifficultyAttributes { StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor, + Mods = mods, ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0, MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)) }; From 21f9c813b20f48d0819d36f16c3963eec1a8cccb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:46:18 +0900 Subject: [PATCH 158/167] Fix mods not being populated --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index b146d201cc..3a0467a0c8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new OsuDifficultyAttributes(); + return new OsuDifficultyAttributes { Mods = mods }; double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; From c264a9cc74ad9b64026793a2c7ab9a7f1726ed84 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:46:40 +0900 Subject: [PATCH 159/167] Fix mods not being populated --- osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index d973b80e12..bed0fd9479 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { if (beatmap.HitObjects.Count == 0) - return new TaikoDifficultyAttributes(); + return new TaikoDifficultyAttributes { Mods = mods }; return new TaikoDifficultyAttributes { From 5457097342ee87769bdb3fcf4557e482213b4765 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:48:00 +0900 Subject: [PATCH 160/167] Update with PopulateAttributes() removal --- .../Difficulty/ManiaDifficultyCalculator.cs | 22 ++++++++++--------- .../Preprocessing/ManiaDifficultyHitObject.cs | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 1e7e16329a..548a5d1bca 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -29,14 +29,18 @@ namespace osu.Game.Rulesets.Mania.Difficulty isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); } - protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate) + protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate) { - var maniaAttributes = (ManiaDifficultyAttributes)attributes; + if (beatmap.HitObjects.Count == 0) + return new ManiaDifficultyAttributes { Mods = mods }; - maniaAttributes.StarRating = difficultyValue(skills) * star_scaling_factor; - - // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future - maniaAttributes.GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; + return new ManiaDifficultyAttributes + { + StarRating = difficultyValue(skills) * star_scaling_factor, + Mods = mods, + // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future + GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate, + }; } private double difficultyValue(Skill[] skills) @@ -71,12 +75,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty return difficulty; } - protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate) + protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { columnCount = ((ManiaBeatmap)beatmap).TotalColumns; for (int i = 1; i < beatmap.HitObjects.Count; i++) - yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], timeRate); + yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate); } protected override Skill[] CreateSkills() @@ -89,8 +93,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty return skills.ToArray(); } - protected override DifficultyAttributes CreateDifficultyAttributes() => new ManiaDifficultyAttributes(); - protected override Mod[] DifficultyAdjustmentMods { get diff --git a/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs b/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs index aa823e7586..29ba934e9f 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Preprocessing/ManiaDifficultyHitObject.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Preprocessing { public new ManiaHitObject BaseObject => (ManiaHitObject)base.BaseObject; - public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate) - : base(hitObject, lastObject, timeRate) + public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate) + : base(hitObject, lastObject, clockRate) { } } From 4dcf39846de896ebff28d5ae5536c99bf13fdaa9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:52:59 +0900 Subject: [PATCH 161/167] Pass beatmap to CreateSkills() --- .../Rulesets/Difficulty/DifficultyCalculator.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index c6e54c52e7..29ec9aae25 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Difficulty protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate) { - var skills = CreateSkills(); + var skills = CreateSkills(beatmap); if (!beatmap.HitObjects.Any()) return CreateDifficultyAttributes(beatmap, mods, skills, clockRate); @@ -106,9 +106,9 @@ namespace osu.Game.Rulesets.Difficulty /// /// Creates to describe beatmap's calculated difficulty. /// - /// The whose difficulty was processed. - /// The s that were applied during the process. - /// The skills which processed the difficulty. + /// The whose difficulty was calculated. + /// The s that difficulty was calculated with. + /// The skills which processed the beatmap. /// The rate at which the gameplay clock is run at. protected abstract DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate); @@ -121,9 +121,10 @@ namespace osu.Game.Rulesets.Difficulty protected abstract IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate); /// - /// Creates the s to calculate the difficulty of s. + /// Creates the s to calculate the difficulty of an . /// + /// The whose difficulty will be calculated.The s. - protected abstract Skill[] CreateSkills(); + protected abstract Skill[] CreateSkills(IBeatmap beatmap); } } From ea281e8596e51028a6e24b71cd5feca905dd5a57 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:54:00 +0900 Subject: [PATCH 162/167] Add beatmap argument --- osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 9e6511b362..89f3a8c25e 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty } } - protected override Skill[] CreateSkills() => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] { new Movement(), }; From 4efc03cdf0ab1e401f85d192ded9124e8e365d14 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:56:38 +0900 Subject: [PATCH 163/167] Add beatmap argument + fix crashes --- .../Difficulty/ManiaDifficultyCalculator.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 548a5d1bca..4790bde9ee 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty { private const double star_scaling_factor = 0.018; - private int columnCount; - private readonly bool isForCurrentRuleset; public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap) @@ -77,14 +75,15 @@ namespace osu.Game.Rulesets.Mania.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - columnCount = ((ManiaBeatmap)beatmap).TotalColumns; for (int i = 1; i < beatmap.HitObjects.Count; i++) yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate); } - protected override Skill[] CreateSkills() + protected override Skill[] CreateSkills(IBeatmap beatmap) { + int columnCount = ((ManiaBeatmap)beatmap).TotalColumns; + var skills = new List { new Overall(columnCount) }; for (int i = 0; i < columnCount; i++) From 5ff890434cdd2da760087b60629006bf3bec127b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:57:29 +0900 Subject: [PATCH 164/167] Add beatmap argument --- osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index bed0fd9479..685ad9949b 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty yield return new TaikoDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate); } - protected override Skill[] CreateSkills() => new Skill[] { new Strain() }; + protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] { new Strain() }; protected override Mod[] DifficultyAdjustmentMods => new Mod[] { From 03802930989a83dfdbbf3745757e0bab2fcf4e44 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 19 Feb 2019 17:58:02 +0900 Subject: [PATCH 165/167] Add beatmap argument --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 3a0467a0c8..e2a1542574 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty } } - protected override Skill[] CreateSkills() => new Skill[] + protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[] { new Aim(), new Speed() From d6a2fe6891173c82c9309d5a56962c3f6707b392 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Feb 2019 10:29:08 +0900 Subject: [PATCH 166/167] Remove excess newline --- osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs index 4790bde9ee..59fed1031f 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaDifficultyCalculator.cs @@ -75,7 +75,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty protected override IEnumerable CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) { - for (int i = 1; i < beatmap.HitObjects.Count; i++) yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate); } From 2c76a039ca3ebab69ef0bc199e0692e1bb9cf8f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Feb 2019 10:46:25 +0900 Subject: [PATCH 167/167] Remove unnecessary folder reference --- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 563ed2e7ca..656ebcc7c2 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -10,7 +10,4 @@ - - - - \ No newline at end of file +