2019-01-31 18:17:42 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2019-01-17 20:10:34 +08:00
2019-01-18 13:28:06 +08:00
using System ;
2021-01-17 03:57:55 +08:00
using JetBrains.Annotations ;
2019-01-17 20:10:34 +08:00
using osu.Framework.Allocation ;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables ;
2019-01-17 20:10:34 +08:00
using osu.Framework.Graphics.Containers ;
2019-06-12 01:31:01 +08:00
using osu.Game.Database ;
2019-06-11 23:41:30 +08:00
using osu.Game.Online.API ;
2019-01-17 20:10:34 +08:00
2019-06-12 01:31:01 +08:00
namespace osu.Game.Online
2019-01-17 20:10:34 +08:00
{
2019-02-14 15:21:01 +08:00
/// <summary>
2019-11-17 20:48:23 +08:00
/// A component which tracks a <typeparamref name="TModel"/> through potential download/import/deletion.
2019-02-14 15:21:01 +08:00
/// </summary>
2019-06-12 01:31:01 +08:00
public abstract class DownloadTrackingComposite < TModel , TModelManager > : CompositeDrawable
2019-06-12 01:53:40 +08:00
where TModel : class , IEquatable < TModel >
2019-06-12 03:11:17 +08:00
where TModelManager : class , IModelDownloader < TModel >
2019-01-17 20:10:34 +08:00
{
2019-06-26 23:29:09 +08:00
protected readonly Bindable < TModel > Model = new Bindable < TModel > ( ) ;
2019-01-18 13:28:06 +08:00
2020-02-14 21:59:51 +08:00
[Resolved(CanBeNull = true)]
2021-01-17 03:54:54 +08:00
protected TModelManager Manager { get ; private set ; }
2019-01-17 20:10:34 +08:00
2019-01-18 13:28:06 +08:00
/// <summary>
2019-11-17 20:48:23 +08:00
/// Holds the current download state of the <typeparamref name="TModel"/>, whether is has already been downloaded, is in progress, or is not downloaded.
2019-01-18 13:28:06 +08:00
/// </summary>
protected readonly Bindable < DownloadState > State = new Bindable < DownloadState > ( ) ;
2019-01-17 20:10:34 +08:00
2019-12-10 17:08:11 +08:00
protected readonly BindableNumber < double > Progress = new BindableNumber < double > { MinValue = 0 , MaxValue = 1 } ;
2019-01-17 20:10:34 +08:00
2019-06-12 01:31:01 +08:00
protected DownloadTrackingComposite ( TModel model = null )
2019-01-17 20:10:34 +08:00
{
2019-06-26 23:29:09 +08:00
Model . Value = model ;
2019-01-17 20:10:34 +08:00
}
2020-05-27 15:08:47 +08:00
private IBindable < WeakReference < TModel > > managedUpdated ;
2020-05-19 15:44:22 +08:00
private IBindable < WeakReference < TModel > > managerRemoved ;
private IBindable < WeakReference < ArchiveDownloadRequest < TModel > > > managerDownloadBegan ;
private IBindable < WeakReference < ArchiveDownloadRequest < TModel > > > managerDownloadFailed ;
2019-01-17 20:10:34 +08:00
[BackgroundDependencyLoader(true)]
2020-02-14 21:14:00 +08:00
private void load ( )
2019-01-17 20:10:34 +08:00
{
2019-06-26 23:29:09 +08:00
Model . BindValueChanged ( modelInfo = >
2019-02-14 03:04:49 +08:00
{
2019-06-12 01:31:01 +08:00
if ( modelInfo . NewValue = = null )
2019-02-14 03:04:49 +08:00
attachDownload ( null ) ;
2021-01-17 03:58:29 +08:00
else if ( IsModelAvailableLocally ( ) )
2019-02-14 03:04:49 +08:00
State . Value = DownloadState . LocallyAvailable ;
else
2021-01-17 03:54:54 +08:00
attachDownload ( Manager ? . GetExistingDownload ( modelInfo . NewValue ) ) ;
2019-02-14 03:04:49 +08:00
} , true ) ;
2019-01-18 13:28:06 +08:00
2021-01-17 03:54:54 +08:00
if ( Manager = = null )
2020-11-14 21:48:48 +08:00
return ;
2021-01-17 03:54:54 +08:00
managerDownloadBegan = Manager . DownloadBegan . GetBoundCopy ( ) ;
2020-05-19 15:44:22 +08:00
managerDownloadBegan . BindValueChanged ( downloadBegan ) ;
2021-01-17 03:54:54 +08:00
managerDownloadFailed = Manager . DownloadFailed . GetBoundCopy ( ) ;
2020-05-19 15:44:22 +08:00
managerDownloadFailed . BindValueChanged ( downloadFailed ) ;
2021-01-17 03:54:54 +08:00
managedUpdated = Manager . ItemUpdated . GetBoundCopy ( ) ;
2020-05-27 15:08:47 +08:00
managedUpdated . BindValueChanged ( itemUpdated ) ;
2021-01-17 03:54:54 +08:00
managerRemoved = Manager . ItemRemoved . GetBoundCopy ( ) ;
2020-05-19 15:44:22 +08:00
managerRemoved . BindValueChanged ( itemRemoved ) ;
2019-01-17 20:10:34 +08:00
}
2021-01-17 03:57:55 +08:00
/// <summary>
2021-02-04 07:20:18 +08:00
/// Checks that a database model matches the one expected to be downloaded.
2021-01-17 03:57:55 +08:00
/// </summary>
/// <example>
2021-02-05 05:38:56 +08:00
/// For online play, this could be used to check that the databased model matches the online beatmap.
2021-01-17 03:57:55 +08:00
/// </example>
/// <param name="databasedModel">The model in database.</param>
protected virtual bool VerifyDatabasedModel ( [ NotNull ] TModel databasedModel ) = > true ;
2021-01-17 03:58:29 +08:00
/// <summary>
/// Whether the given model is available in the database.
/// By default, this calls <see cref="IModelDownloader{TModel}.IsAvailableLocally"/>,
/// but can be overriden to add additional checks for verifying the model in database.
/// </summary>
2021-01-18 02:19:55 +08:00
protected virtual bool IsModelAvailableLocally ( ) = > Manager ? . IsAvailableLocally ( Model . Value ) = = true ;
2021-01-17 03:58:29 +08:00
2020-05-19 15:44:22 +08:00
private void downloadBegan ( ValueChangedEvent < WeakReference < ArchiveDownloadRequest < TModel > > > weakRequest )
2019-08-05 16:58:16 +08:00
{
2020-05-19 15:44:22 +08:00
if ( weakRequest . NewValue . TryGetTarget ( out var request ) )
{
Schedule ( ( ) = >
{
if ( request . Model . Equals ( Model . Value ) )
attachDownload ( request ) ;
} ) ;
}
}
2019-08-05 16:58:16 +08:00
2020-05-19 15:44:22 +08:00
private void downloadFailed ( ValueChangedEvent < WeakReference < ArchiveDownloadRequest < TModel > > > weakRequest )
2019-08-05 16:58:16 +08:00
{
2020-05-19 15:44:22 +08:00
if ( weakRequest . NewValue . TryGetTarget ( out var request ) )
{
Schedule ( ( ) = >
{
if ( request . Model . Equals ( Model . Value ) )
attachDownload ( null ) ;
} ) ;
}
}
2019-08-05 16:58:16 +08:00
2019-06-26 20:52:37 +08:00
private ArchiveDownloadRequest < TModel > attachedRequest ;
2019-01-17 20:10:34 +08:00
2019-06-26 20:52:37 +08:00
private void attachDownload ( ArchiveDownloadRequest < TModel > request )
2019-01-18 13:28:06 +08:00
{
if ( attachedRequest ! = null )
{
attachedRequest . Failure - = onRequestFailure ;
attachedRequest . DownloadProgressed - = onRequestProgress ;
attachedRequest . Success - = onRequestSuccess ;
}
attachedRequest = request ;
if ( attachedRequest ! = null )
{
2019-01-31 18:08:45 +08:00
if ( attachedRequest . Progress = = 1 )
{
Progress . Value = 1 ;
2021-03-26 13:04:09 +08:00
State . Value = DownloadState . Importing ;
2019-01-31 18:08:45 +08:00
}
else
{
Progress . Value = attachedRequest . Progress ;
2021-03-26 13:04:09 +08:00
State . Value = DownloadState . Downloading ;
2019-01-31 18:08:45 +08:00
attachedRequest . Failure + = onRequestFailure ;
attachedRequest . DownloadProgressed + = onRequestProgress ;
attachedRequest . Success + = onRequestSuccess ;
}
2019-01-18 13:28:06 +08:00
}
else
{
State . Value = DownloadState . NotDownloaded ;
}
2019-01-17 20:10:34 +08:00
}
2021-01-13 23:04:29 +08:00
private void onRequestSuccess ( string _ ) = > Schedule ( ( ) = > State . Value = DownloadState . Importing ) ;
2019-01-17 20:10:34 +08:00
2021-01-18 02:16:45 +08:00
private void onRequestProgress ( float progress ) = > Schedule ( ( ) = > Progress . Value = progress ) ;
2019-01-17 20:10:34 +08:00
2019-02-14 15:21:01 +08:00
private void onRequestFailure ( Exception e ) = > Schedule ( ( ) = > attachDownload ( null ) ) ;
2019-01-17 20:10:34 +08:00
2020-05-27 15:08:47 +08:00
private void itemUpdated ( ValueChangedEvent < WeakReference < TModel > > weakItem )
2020-05-19 15:44:22 +08:00
{
if ( weakItem . NewValue . TryGetTarget ( out var item ) )
2021-01-17 03:57:55 +08:00
{
Schedule ( ( ) = >
{
if ( ! item . Equals ( Model . Value ) )
return ;
if ( ! VerifyDatabasedModel ( item ) )
{
State . Value = DownloadState . NotDownloaded ;
return ;
}
State . Value = DownloadState . LocallyAvailable ;
} ) ;
}
2020-05-19 15:44:22 +08:00
}
2019-02-14 03:11:46 +08:00
2020-05-19 15:44:22 +08:00
private void itemRemoved ( ValueChangedEvent < WeakReference < TModel > > weakItem )
{
if ( weakItem . NewValue . TryGetTarget ( out var item ) )
2021-01-17 03:57:55 +08:00
{
Schedule ( ( ) = >
{
if ( item . Equals ( Model . Value ) )
State . Value = DownloadState . NotDownloaded ;
} ) ;
}
2020-05-19 15:44:22 +08:00
}
2019-02-14 03:11:46 +08:00
2019-06-26 23:29:38 +08:00
#region Disposal
protected override void Dispose ( bool isDisposing )
{
base . Dispose ( isDisposing ) ;
State . UnbindAll ( ) ;
attachDownload ( null ) ;
}
#endregion
2019-01-17 20:10:34 +08:00
}
2019-01-18 13:28:06 +08:00
}