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