diff --git a/osu.Game.Tests/Visual/Online/TestSceneReplayMissingBeatmap.cs b/osu.Game.Tests/Visual/Online/TestSceneReplayMissingBeatmap.cs index eb84d80051..60197e0eb7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneReplayMissingBeatmap.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneReplayMissingBeatmap.cs @@ -1,13 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using System.Net; using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Screens.Import; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Visual.Online @@ -24,6 +26,12 @@ namespace osu.Game.Tests.Visual.Online OnlineBeatmapSetID = 173612, BeatmapSet = new APIBeatmapSet { + Title = "FREEDOM Dive", + Artist = "xi", + Covers = new BeatmapSetOnlineCovers + { + Card = "https://assets.ppy.sh/beatmaps/173612/covers/card@2x.jpg" + }, OnlineID = 173612 } }; @@ -40,7 +48,7 @@ namespace osu.Game.Tests.Visual.Online } }); - AddUntilStep("Replay missing screen show", () => Game.ScreenStack.CurrentScreen.GetType() == typeof(ReplayMissingBeatmapScreen)); + AddUntilStep("Replay missing notification show", () => Game.Notifications.ChildrenOfType().Any()); } [Test] @@ -58,7 +66,7 @@ namespace osu.Game.Tests.Visual.Online } }); - AddUntilStep("Replay missing screen not show", () => Game.ScreenStack.CurrentScreen.GetType() != typeof(ReplayMissingBeatmapScreen)); + AddUntilStep("Replay missing notification not show", () => !Game.Notifications.ChildrenOfType().Any()); } private void setupBeatmapResponse(APIBeatmap b) diff --git a/osu.Game/Database/MissingBeatmapNotification.cs b/osu.Game/Database/MissingBeatmapNotification.cs new file mode 100644 index 0000000000..2587160a57 --- /dev/null +++ b/osu.Game/Database/MissingBeatmapNotification.cs @@ -0,0 +1,157 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +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.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; +using osu.Game.Scoring; +using osuTK.Graphics; + +namespace osu.Game.Database +{ + public partial class MissingBeatmapNotification : ProgressNotification + { + [Resolved] + private BeatmapModelDownloader beatmapDownloader { get; set; } = null!; + + [Resolved] + private ScoreManager scoreManager { get; set; } = null!; + + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + [Resolved] + private BeatmapSetOverlay? beatmapSetOverlay { get; set; } + + private Container beatmapPanelContainer = null!; + + private readonly MemoryStream scoreStream; + + private readonly APIBeatmapSet beatmapSetInfo; + + private BeatmapDownloadTracker? downloadTracker; + + private Bindable autodownloadConfig = null!; + + public MissingBeatmapNotification(APIBeatmap beatmap, MemoryStream scoreStream) + { + beatmapSetInfo = beatmap.BeatmapSet!; + + this.scoreStream = scoreStream; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, OsuConfigManager config) + { + autodownloadConfig = config.GetBindable(OsuSetting.AutomaticallyDownloadWhenSpectating); + + Text = "You do not have the required beatmap for this replay"; + + Content.Add(beatmapPanelContainer = new ClickableContainer + { + RelativeSizeAxes = Axes.X, + Height = 70, + Anchor = Anchor.CentreLeft, + Origin = Anchor.TopLeft, + Action = () => beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmapSetInfo.OnlineID) + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + downloadTracker = new BeatmapDownloadTracker(beatmapSetInfo); + downloadTracker.State.BindValueChanged(downloadStatusChanged, true); + + beatmapPanelContainer.Clear(); + beatmapPanelContainer.Child = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 4, + Children = new Drawable[] + { + downloadTracker, + new DelayedLoadWrapper(() => new UpdateableOnlineBeatmapSetCover(BeatmapSetCoverType.Card) + { + OnlineInfo = beatmapSetInfo, + RelativeSizeAxes = Axes.Both + }) + { + RelativeSizeAxes = Axes.Both + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.4f + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Padding = new MarginPadding + { + Left = 10f, + Top = 5f + }, + Children = new Drawable[] + { + new TruncatingSpriteText + { + Text = beatmapSetInfo.Title, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 17, italics: true), + RelativeSizeAxes = Axes.X, + }, + new TruncatingSpriteText + { + Text = beatmapSetInfo.Artist, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12, italics: true), + RelativeSizeAxes = Axes.X, + } + } + }, + new DownloadButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Width = 50, + Height = 30, + Margin = new MarginPadding + { + Bottom = 1f + }, + Action = () => beatmapDownloader.Download(beatmapSetInfo), + State = { BindTarget = downloadTracker.State } + } + } + }; + + if (autodownloadConfig.Value) + beatmapDownloader.Download(beatmapSetInfo); + } + + private void downloadStatusChanged(ValueChangedEvent status) + { + if (status.NewValue != DownloadState.LocallyAvailable) + return; + + var importTask = new ImportTask(scoreStream, "score.osr"); + scoreManager.Import(this, new[] { importTask }); + } + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5d130af6d4..c60bff9e4c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -854,8 +854,6 @@ namespace osu.Game MultiplayerClient.PostNotification = n => Notifications.Post(n); - ScoreManager.Performer = this; - // make config aware of how to lookup skins for on-screen display purposes. // if this becomes a more common thing, tracked settings should be reconsidered to allow local DI. LocalConfig.LookupSkinName = id => SkinManager.Query(s => s.ID == id)?.ToString() ?? "Unknown"; diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index 5c354ac3d1..e3fce4a82a 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -10,7 +10,6 @@ using System.Threading; using Newtonsoft.Json; using osu.Framework.Logging; using osu.Framework.Platform; -using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.IO.Archives; @@ -21,8 +20,6 @@ using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; -using osu.Game.Screens; -using osu.Game.Screens.Import; using Realms; namespace osu.Game.Scoring @@ -31,8 +28,6 @@ namespace osu.Game.Scoring { public override IEnumerable HandledExtensions => new[] { ".osr" }; - public IPerformFromScreenRunner? Performer { get; set; } - protected override string[] HashableFileTypes => new[] { ".osr" }; private readonly RulesetStore rulesets; @@ -69,9 +64,6 @@ namespace osu.Game.Scoring private void onMissingBeatmap(LegacyScoreDecoder.BeatmapNotFoundException e, ArchiveReader archive, string name) { - if (Performer == null) - return; - var stream = new MemoryStream(); // stream will close after exception throw, so fetch the stream again. @@ -87,7 +79,7 @@ namespace osu.Game.Scoring req.Success += res => { - Performer.PerformFromScreen(screen => screen.Push(new ReplayMissingBeatmapScreen(res, stream))); + PostNotification?.Invoke(new MissingBeatmapNotification(res, stream)); }; api.Queue(req); diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index 9331168ab0..31b5bd8365 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -21,7 +21,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; using osu.Game.Online.API; using osu.Game.Scoring.Legacy; -using osu.Game.Screens; namespace osu.Game.Scoring { @@ -31,12 +30,6 @@ namespace osu.Game.Scoring private readonly ScoreImporter scoreImporter; private readonly LegacyScoreExporter scoreExporter; - [CanBeNull] - public IPerformFromScreenRunner Performer - { - set => scoreImporter.Performer = value; - } - public override bool PauseImports { get => base.PauseImports; diff --git a/osu.Game/Screens/Import/ReplayMissingBeatmapScreen.cs b/osu.Game/Screens/Import/ReplayMissingBeatmapScreen.cs deleted file mode 100644 index 614d652f47..0000000000 --- a/osu.Game/Screens/Import/ReplayMissingBeatmapScreen.cs +++ /dev/null @@ -1,199 +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.IO; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Audio; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables.Cards; -using osu.Game.Configuration; -using osu.Game.Database; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Overlays; -using osu.Game.Overlays.Settings; -using osu.Game.Scoring; -using osu.Game.Screens.Ranking; -using osuTK; -using Realms; - -namespace osu.Game.Screens.Import -{ - [Cached(typeof(IPreviewTrackOwner))] - public partial class ReplayMissingBeatmapScreen : OsuScreen, IPreviewTrackOwner - { - [Resolved] - private BeatmapModelDownloader beatmapDownloader { get; set; } = null!; - - [Resolved] - private ScoreManager scoreManager { get; set; } = null!; - - [Resolved] - private RealmAccess realm { get; set; } = null!; - - private IDisposable? realmSubscription; - - [Cached] - private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - - [Resolved] - private INotificationOverlay? notificationOverlay { get; set; } - - private Container beatmapPanelContainer = null!; - private ReplayDownloadButton replayDownloadButton = null!; - private SettingsCheckbox automaticDownload = null!; - - private readonly MemoryStream scoreStream; - - private readonly APIBeatmapSet beatmapSetInfo; - - public ReplayMissingBeatmapScreen(APIBeatmap beatmap, MemoryStream scoreStream) - { - beatmapSetInfo = beatmap.BeatmapSet!; - - this.scoreStream = scoreStream; - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours, OsuConfigManager config) - { - InternalChildren = new Drawable[] - { - new Container - { - Masking = true, - CornerRadius = 20, - AutoSizeAxes = Axes.Both, - AutoSizeDuration = 500, - AutoSizeEasing = Easing.OutQuint, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new Drawable[] - { - new Box - { - Colour = colours.Gray5, - RelativeSizeAxes = Axes.Both, - }, - new FillFlowContainer - { - Margin = new MarginPadding(20), - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Spacing = new Vector2(15), - Children = new Drawable[] - { - new OsuSpriteText - { - Text = "Beatmap info", - Font = OsuFont.Default.With(size: 30), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Spacing = new Vector2(15), - Children = new Drawable[] - { - beatmapPanelContainer = new Container - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, - } - }, - automaticDownload = new SettingsCheckbox - { - LabelText = "Automatically download beatmaps", - Current = config.GetBindable(OsuSetting.AutomaticallyDownloadWhenSpectating), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - replayDownloadButton = new ReplayDownloadButton(new ScoreInfo()) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - } - } - } - }, - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateStatus(); - realmSubscription = realm.RegisterForNotifications( - realm => realm.All().Where(s => !s.DeletePending), beatmapsChanged); - } - - private void updateStatus() - { - beatmapPanelContainer.Clear(); - beatmapPanelContainer.Child = new BeatmapCardNormal(beatmapSetInfo, allowExpansion: false); - checkForAutomaticDownload(beatmapSetInfo); - } - - private void checkForAutomaticDownload(APIBeatmapSet beatmap) - { - if (!automaticDownload.Current.Value) - return; - - beatmapDownloader.Download(beatmap); - } - - private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes) - { - if (changes?.InsertedIndices == null) return; - - if (!scoreStream.CanRead) return; - - if (sender.Any(b => b.OnlineID == beatmapSetInfo.OnlineID)) - { - var progressNotification = new ImportProgressNotification(); - var importTask = new ImportTask(scoreStream, "score.osr"); - scoreManager.Import(progressNotification, new[] { importTask }) - .ContinueWith(s => - { - s.GetResultSafely>>().FirstOrDefault()?.PerformRead(score => - { - Guid scoreid = score.ID; - Scheduler.Add(() => - { - replayDownloadButton.Score.Value = realm.Realm.Find(scoreid) ?? new ScoreInfo(); - }); - }); - }); - - notificationOverlay?.Post(progressNotification); - - realmSubscription?.Dispose(); - } - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - realmSubscription?.Dispose(); - } - } -}