2020-02-17 02:44:21 +00:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Threading ;
2020-02-20 21:20:32 +00:00
namespace RecrownedGTK.AssetsSystem
2020-02-17 02:44:21 +00:00
{
/// <summary>
/// Wrapper for the content manager that helps with controlling it by adding automated multithreaded content loading.
/// </summary>
public class AssetManager
{
2020-02-20 21:34:00 +00:00
private AssetLoader assetLoader ;
2020-02-17 02:44:21 +00:00
Thread thread ;
readonly Queue < ContentData > queue ;
2020-02-20 21:34:00 +00:00
Dictionary < string , IDisposable > assets ;
2020-02-17 02:44:21 +00:00
/// <summary>
/// Path modifiers to change the path in which the content manager looks to load a file. Used for better organizing things while not needing to type entire path.
/// </summary>
private readonly Dictionary < Type , IAssetPathResolver > contentPathModifier ;
/// <summary>
/// Used when no path modifier is defined for that specific type.
/// </summary>
public IAssetPathResolver normalPathModifier = new NormalAssetPathResolver ( ) ;
volatile float progress ;
volatile bool running ;
/// <summary>
/// Whether or not the queue is empty and all content is loaded.
/// </summary>
public bool Done { get { return ! running & & queue . Count = = 0 ; } }
/// <summary>
/// The progress of the loading. 1 is complete while 0 is incomplete.
/// </summary>
public float Progress { get { return progress ; } }
/// <summary>
/// Wraps the <see cref="ContentManager"/>.
/// </summary>
/// <param name="contentManager">The manager to wrap.</param>
public AssetManager ( )
{
2020-02-20 21:34:00 +00:00
assets = new Dictionary < string , IDisposable > ( ) ;
2020-02-17 02:44:21 +00:00
queue = new Queue < ContentData > ( ) ;
contentPathModifier = new Dictionary < Type , IAssetPathResolver > ( ) ;
}
/// <summary>
/// Adds a <see cref="IContentPathResolver"/> to this handler.
/// </summary>
/// <param name="assetType"></param>
/// <param name="contentResolver"></param>
public void AddContentPathResolver ( Type assetType , IAssetPathResolver contentResolver ) {
contentPathModifier . Add ( assetType , contentResolver ) ;
}
/// <summary>
/// Removes the <see cref="IContentPathResolver"/> for the key.
/// </summary>
/// <param name="assetType"></param>
public void RemoveContentResolver ( Type assetType ) {
contentPathModifier . Remove ( assetType ) ;
}
private void Load ( string assetName , Type type , bool usePathModifier )
{
Debug . WriteLine ( "Loading asset: " + assetName ) ;
string path = assetName ;
if ( usePathModifier )
{
IAssetPathResolver handler ;
if ( contentPathModifier . ContainsKey ( type ) )
{
handler = contentPathModifier [ type ] ;
}
else
{
handler = normalPathModifier ;
}
path = handler . Modify ( assetName ) ;
}
2020-02-20 21:34:00 +00:00
assets . Add ( assetName , assetLoader . Load < IDisposable > ( path ) ) ;
2020-02-17 02:44:21 +00:00
}
/// <summary>
/// Gets the requested asset.
/// </summary>
/// <typeparam name="T">The type of the asset for an alternative way to cast.</typeparam>
/// <param name="assetName">The name of the asset.</param>
/// <returns>The asset casted to the type given with T.</returns>
public T Get < T > ( string assetName )
{
lock ( queue )
{
return ( T ) assets [ assetName ] ;
}
}
/// <summary>
/// Queues an asset to be loaded.
/// </summary>
/// <typeparam name="T">The type of the asset to be queued.</typeparam>
/// <param name="assetName">Name of asset to look for.</param>
/// <param name="usePathModifier">Whether or not to use the path modifiers.</param>
public void Queue < T > ( string assetName , bool usePathModifier = true )
{
lock ( queue )
{
if ( ! assets . ContainsKey ( assetName ) )
{
queue . Enqueue ( new ContentData ( assetName , typeof ( T ) , usePathModifier ) ) ;
Debug . WriteLine ( "Queued asset: " + assetName ) ;
}
else
{
throw new InvalidOperationException ( "Did not queue asset due to asset with same name being loaded: " + assetName ) ;
}
}
}
/// <summary>
/// Called whenever a batch of assets should be loaded from the queue. Safe to call once every frame.
/// </summary>
public void Update ( )
{
if ( queue . Count > 0 & & ( thread = = null | | ! thread . IsAlive ) )
{
thread = new Thread ( LoadBatch ) ;
thread . Start ( ) ;
}
}
private void LoadBatch ( )
{
running = true ;
int totalTasks = queue . Count ;
int tasksCompleted = 0 ;
while ( queue . Count ! = 0 )
{
lock ( queue )
{
ContentData content = queue . Dequeue ( ) ;
Load ( content . assetName , content . type , content . usePathModifier ) ;
tasksCompleted + + ;
progress = ( float ) tasksCompleted / totalTasks ;
}
}
running = false ;
}
/// <summary>
/// Removes the asset from the list of assets in the system.
/// Cannot remove from queue.
/// </summary>
/// <param name="name">the string name used to load the asset</param>
public void Remove ( string name )
{
lock ( queue )
{
if ( assets . ContainsKey ( name ) )
{
if ( assets [ name ] is IDisposable )
{
( ( IDisposable ) assets [ name ] ) . Dispose ( ) ;
}
assets . Remove ( name ) ;
}
}
}
/// <summary>
/// Clears the queue.
/// </summary>
public void ClearQueue ( )
{
lock ( queue )
{
queue . Clear ( ) ;
}
}
/// <summary>
/// Unloads everything from both queue and loaded list while properly disposing of the assets loaded.
/// </summary>
public void UnloadAll ( )
{
lock ( queue )
{
2020-02-20 21:34:00 +00:00
foreach ( KeyValuePair < string , IDisposable > asset in assets )
{
asset . Value . Dispose ( ) ;
}
2020-02-17 02:44:21 +00:00
assets . Clear ( ) ;
ClearQueue ( ) ;
Debug . WriteLine ( "Unloaded all assets." ) ;
}
}
private struct ContentData
{
internal Type type ;
internal string assetName ;
internal bool usePathModifier ;
public ContentData ( string assetName , Type type , bool usePathModifier )
{
this . type = type ;
this . assetName = assetName ;
this . usePathModifier = usePathModifier ;
}
}
}
}