// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Online.API; using osu.Game.Scoring; #nullable enable namespace osu.Game.Online { public class ScoreDownloadTracker : DownloadTracker { [Resolved(CanBeNull = true)] protected ScoreManager? Manager { get; private set; } private ArchiveDownloadRequest? attachedRequest; public ScoreDownloadTracker(ScoreInfo trackedItem) : base(trackedItem) { } private IBindable>? managerUpdated; private IBindable>? managerRemoved; private IBindable>>? managerDownloadBegan; private IBindable>>? managerDownloadFailed; [BackgroundDependencyLoader(true)] private void load() { // Used to interact with manager classes that don't support interface types. Will eventually be replaced. var beatmapSetInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) UpdateState(DownloadState.LocallyAvailable); else if (Manager != null) attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); if (Manager != null) { managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); managerDownloadBegan.BindValueChanged(downloadBegan); managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); managerDownloadFailed.BindValueChanged(downloadFailed); managerUpdated = Manager.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(itemUpdated); managerRemoved = Manager.ItemRemoved.GetBoundCopy(); managerRemoved.BindValueChanged(itemRemoved); } } /// /// Checks that a database model matches the one expected to be downloaded. /// /// /// For online play, this could be used to check that the databased model matches the online beatmap. /// /// The model in database. protected virtual bool VerifyDatabasedModel(ScoreInfo databasedModel) => true; // TODO: do we still need this? private void downloadBegan(ValueChangedEvent>> weakRequest) { if (weakRequest.NewValue.TryGetTarget(out var request)) { Schedule(() => { if (checkEquality(request.Model, TrackedItem)) attachDownload(request); }); } } private void downloadFailed(ValueChangedEvent>> weakRequest) { if (weakRequest.NewValue.TryGetTarget(out var 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 void itemUpdated(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) { Schedule(() => { if (!checkEquality(item, TrackedItem)) return; if (!VerifyDatabasedModel(item)) { UpdateState(DownloadState.NotDownloaded); return; } UpdateState(DownloadState.LocallyAvailable); }); } } private void itemRemoved(ValueChangedEvent> weakItem) { if (weakItem.NewValue.TryGetTarget(out var item)) { Schedule(() => { if (checkEquality(item, TrackedItem)) UpdateState(DownloadState.NotDownloaded); }); } } private bool checkEquality(ScoreInfo x, ScoreInfo y) => x.OnlineScoreID == y.OnlineScoreID; #region Disposal protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); attachDownload(null); } #endregion } }