// 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.Framework.Allocation; using osu.Game.Database; using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Scoring; #nullable enable namespace osu.Game.Online { public class ScoreDownloadTracker : DownloadTracker { [Resolved(CanBeNull = true)] protected ScoreModelDownloader? Downloader { get; private set; } private ArchiveDownloadRequest? attachedRequest; private IDisposable? realmSubscription; [Resolved] private RealmContextFactory realmContextFactory { get; set; } = null!; public ScoreDownloadTracker(ScoreInfo trackedItem) : base(trackedItem) { } protected override void LoadComplete() { base.LoadComplete(); if (Downloader == null) return; // Used to interact with manager classes that don't support interface types. Will eventually be replaced. var scoreInfo = new ScoreInfo { ID = TrackedItem.ID, OnlineID = TrackedItem.OnlineID }; Downloader.DownloadBegan += downloadBegan; Downloader.DownloadFailed += downloadFailed; realmSubscription = realmContextFactory.Context.All().Where(s => ((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID) || s.Hash == TrackedItem.Hash) && !s.DeletePending).QueryAsyncWithNotifications((items, changes, ___) => { if (items.Any()) Schedule(() => UpdateState(DownloadState.LocallyAvailable)); else { Schedule(() => { UpdateState(DownloadState.NotDownloaded); attachDownload(Downloader.GetExistingDownload(scoreInfo)); }); } }); } private void downloadBegan(ArchiveDownloadRequest request) => Schedule(() => { if (checkEquality(request.Model, TrackedItem)) attachDownload(request); }); private void downloadFailed(ArchiveDownloadRequest request) => Schedule(() => { if (checkEquality(request.Model, TrackedItem)) attachDownload(null); }); private void attachDownload(ArchiveDownloadRequest? request) { if (attachedRequest != null) { attachedRequest.Failure -= onRequestFailure; attachedRequest.DownloadProgressed -= onRequestProgress; attachedRequest.Success -= onRequestSuccess; } attachedRequest = request; if (attachedRequest != null) { if (attachedRequest.Progress == 1) { UpdateProgress(1); UpdateState(DownloadState.Importing); } else { UpdateProgress(attachedRequest.Progress); UpdateState(DownloadState.Downloading); attachedRequest.Failure += onRequestFailure; attachedRequest.DownloadProgressed += onRequestProgress; attachedRequest.Success += onRequestSuccess; } } else { UpdateState(DownloadState.NotDownloaded); } } private void onRequestSuccess(string _) => Schedule(() => UpdateState(DownloadState.Importing)); private void onRequestProgress(float progress) => Schedule(() => UpdateProgress(progress)); private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); private bool checkEquality(IScoreInfo x, IScoreInfo y) => x.MatchesOnlineID(y); #region Disposal protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); attachDownload(null); realmSubscription?.Dispose(); if (Downloader != null) { Downloader.DownloadBegan -= downloadBegan; Downloader.DownloadFailed -= downloadFailed; } } #endregion } }