using Microsoft.Xna.Framework.Content;
using RecrownedAthenaeum.ContentSystem;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace RecrownedAthenaeum.ContentSystem
{
///
/// Wrapper for the content manager that helps with controlling it by adding automated multithreaded content loading.
///
public class ContentManagerController
{
Thread thread;
readonly ContentManager contentManager;
readonly Queue queue;
Dictionary assets;
///
/// 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.
///
public readonly Dictionary contentPathModifier;
///
/// Used when no path modifier is defined for that specific type.
///
public IContentPathResolver normalPathModifier = new NormalContentResolver();
volatile float progress;
volatile bool running;
///
/// Whether or not the queue is empty and all content is loaded.
///
public bool Done { get { return !running && queue.Count == 0; } }
///
/// The progress of the loading. 1 is complete while 0 is incomplete.
///
public float Progress { get { return progress; } }
///
/// Wraps the .
///
/// The manager to wrap.
public ContentManagerController(ContentManager contentManager)
{
this.contentManager = contentManager;
assets = new Dictionary();
queue = new Queue();
contentPathModifier = new Dictionary();
}
private void Load(string assetName, Type type, bool usePathModifier)
{
Debug.WriteLine("Loading asset: " + assetName);
string path = assetName;
if (usePathModifier)
{
IContentPathResolver handler;
if (contentPathModifier.ContainsKey(type))
{
handler = contentPathModifier[type];
} else
{
handler = normalPathModifier;
}
path = handler.Modify(assetName);
}
assets.Add(assetName, contentManager.Load(path));
}
///
/// Gets the requested asset.
///
/// The type of the asset for an alternative way to cast.
/// The name of the asset.
/// The asset casted to the type given with T.
public T Get(string assetName)
{
lock (queue)
{
return (T)assets[assetName];
}
}
///
/// Queues an asset to be loaded.
///
/// The type of the asset to be queued.
/// Name of asset to look for.
/// Whether or not to use the path modifiers.
public void Queue(string assetName, bool usePathModifier = true) where T : IDisposable
{
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);
}
}
}
///
/// Called whenever a batch of assets should be loaded from the queue. Safe to call once every frame.
///
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;
}
///
/// Removes the asset from the list of assets in the system.
/// Cannot remove from queue.
///
/// the string name used to load the asset
public void Remove(string name)
{
lock (queue)
{
if (assets.ContainsKey(name))
{
assets[name].Dispose();
assets.Remove(name);
}
}
}
///
/// Clears the queue.
///
public void ClearQueue()
{
lock (queue)
{
queue.Clear();
}
}
///
/// Unloads everything from both queue and loaded list while properly disposing of the assets loaded.
///
public void UnloadAll()
{
lock (queue)
{
contentManager.Unload();
assets.Clear();
ClearQueue();
Debug.WriteLine("Unloaded all assets.");
}
}
}
}