using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; namespace RecrownedGTK.Assets { /// /// Wrapper for the content manager that helps with controlling it by adding automated multithreaded content loading. /// public class AssetManager { Thread thread; readonly AssetManager 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. /// private readonly Dictionary contentPathModifier; /// /// Used when no path modifier is defined for that specific type. /// public IAssetPathResolver normalPathModifier = new NormalAssetPathResolver(); 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 AssetManager() { assets = new Dictionary(); queue = new Queue(); contentPathModifier = new Dictionary(); } /// /// Adds a to this handler. /// /// /// public void AddContentPathResolver(Type assetType, IAssetPathResolver contentResolver) { contentPathModifier.Add(assetType, contentResolver); } /// /// Removes the for the key. /// /// 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); } 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) { 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)) { if (assets[name] is IDisposable) { ((IDisposable)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."); } } 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; } } } }