2018-11-30 02:41:06 +00:00
using Microsoft.Xna.Framework.Content ;
2019-01-22 05:38:25 +00:00
using RecrownedAthenaeum.ContentSystem ;
2018-11-30 02:41:06 +00:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Threading ;
2018-12-04 13:45:09 +00:00
namespace RecrownedAthenaeum.ContentSystem
2018-11-30 02:41:06 +00:00
{
2019-01-14 06:34:35 +00:00
/// <summary>
/// Wrapper for the content manager that helps with controlling it by adding automated multithreaded content loading.
/// </summary>
2018-11-30 02:41:06 +00:00
public class ContentManagerController
{
Thread thread ;
readonly ContentManager contentManager ;
2019-01-20 07:31:01 +00:00
readonly Queue < ContentData > queue ;
2018-11-30 02:41:06 +00:00
Dictionary < string , IDisposable > assets ;
2019-01-14 06:34:35 +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>
2019-01-22 05:38:25 +00:00
public readonly Dictionary < Type , IContentPathResolver > contentPathModifier ;
2019-01-21 03:31:20 +00:00
/// <summary>
/// Used when no path modifier is defined for that specific type.
/// </summary>
2019-01-22 05:38:25 +00:00
public IContentPathResolver normalPathModifier = new NormalContentResolver ( ) ;
2018-11-30 02:41:06 +00:00
volatile float progress ;
volatile bool running ;
2019-01-14 06:34:35 +00:00
/// <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>
2018-11-30 02:41:06 +00:00
public ContentManagerController ( ContentManager contentManager )
{
this . contentManager = contentManager ;
assets = new Dictionary < string , IDisposable > ( ) ;
2019-01-20 07:31:01 +00:00
queue = new Queue < ContentData > ( ) ;
2019-01-22 05:38:25 +00:00
contentPathModifier = new Dictionary < Type , IContentPathResolver > ( ) ;
2018-11-30 02:41:06 +00:00
}
private void Load ( string assetName , Type type , bool usePathModifier )
{
2019-01-21 03:31:20 +00:00
Debug . WriteLine ( "Loading asset: " + assetName ) ;
2018-11-30 02:41:06 +00:00
string path = assetName ;
if ( usePathModifier )
{
2019-01-22 05:38:25 +00:00
IContentPathResolver handler ;
2019-01-21 03:31:20 +00:00
if ( contentPathModifier . ContainsKey ( type ) )
{
handler = contentPathModifier [ type ] ;
} else
{
handler = normalPathModifier ;
}
2018-11-30 02:41:06 +00:00
path = handler . Modify ( assetName ) ;
}
assets . Add ( assetName , contentManager . Load < IDisposable > ( path ) ) ;
}
2019-01-14 06:34:35 +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>
2018-11-30 02:41:06 +00:00
public T Get < T > ( string assetName )
{
lock ( queue )
{
return ( T ) assets [ assetName ] ;
}
}
2019-01-14 06:34:35 +00:00
/// <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>
2018-11-30 02:41:06 +00:00
public void Queue < T > ( string assetName , bool usePathModifier = true ) where T : IDisposable
{
lock ( queue )
{
if ( ! assets . ContainsKey ( assetName ) )
{
2019-01-20 07:31:01 +00:00
queue . Enqueue ( new ContentData ( assetName , typeof ( T ) , usePathModifier ) ) ;
2018-11-30 02:41:06 +00:00
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 )
{
2019-01-20 07:31:01 +00:00
ContentData content = queue . Dequeue ( ) ;
2018-11-30 02:41:06 +00:00
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 ) )
{
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 )
{
contentManager . Unload ( ) ;
assets . Clear ( ) ;
ClearQueue ( ) ;
Debug . WriteLine ( "Unloaded all assets." ) ;
}
}
}
}