using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace RhythmBullet.Zer01HD.Utilities.ContentSystem { public delegate void ContentSystemUpdate(String fileName, float completed); public class ContentSystem { public event ContentSystemUpdate ContentSystemListeners; Thread thread; readonly ContentManager contentManager; readonly Queue queue; Dictionary assets; public readonly Dictionary contentPathModifier; volatile float progress; public ContentSystem(ContentManager contentManager) { this.contentManager = contentManager; assets = new Dictionary(); queue = new Queue(); contentPathModifier = new Dictionary(); } private void Load(string assetName, Type type, bool usePathModifier) { string path = assetName; if (usePathModifier) { IContentPathModifier handler = contentPathModifier[type]; path = handler.Modify(assetName); } assets.Add(assetName, contentManager.Load(path)); Debug.WriteLine("Loaded asset: " + assetName); } public T Get(string assetName) { lock (queue) { return (T)assets[assetName]; } } public void Queue(string assetName, bool usePathModifier = true) where T : IDisposable { lock (queue) { if (!assets.ContainsKey(assetName)) { queue.Enqueue(new LoadableContent(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)) { ThreadStart threadStart = new ThreadStart(LoadBatch); thread = new Thread(threadStart); thread.Start(); } } private void LoadBatch() { int totalTasks = queue.Count; int tasksCompleted = 0; while (queue.Count != 0) { lock (queue) { LoadableContent content = queue.Dequeue(); Load(content.assetName, content.type, content.usePathModifier); tasksCompleted++; progress = (float)tasksCompleted / totalTasks; OnProgress(content.assetName, progress); } } } /// /// 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."); } } public bool Done { get { return queue.Count == 0; } } protected virtual void OnProgress(string fileName, float progress) { ContentSystemListeners?.Invoke(fileName, progress); } public float Progress { get { return progress; } } } }