using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using RecrownedGTK.AssetsSystem.Information; namespace RecrownedGTK.AssetsSystem { /// /// Wrapper for the content manager that helps with controlling it by adding automated multithreaded content loading. /// public class AssetManager { public readonly AssetLoader assetLoader; readonly Queue queue; ConcurrentDictionary assets; private readonly Dictionary contentPathModifier; 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; } } /// /// Creates an asset manager with a default asset loader. /// public AssetManager() : this(new AssetLoader()) { } /// /// A manager for the given asset loader. /// The manager will asynchronously load assets and store them. /// /// The asset loader to use for loading. public AssetManager(AssetLoader assetLoader) { this.assetLoader = assetLoader; assets = new ConcurrentDictionary(); 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.TryAdd(assetName, assetLoader.Load(path, type).CreateUseable()); } /// /// 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 AssetOutline(assetName, typeof(T), usePathModifier)); Debug.WriteLine("Queued asset: " + assetName); Update(); } 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 && !running) { ThreadPool.QueueUserWorkItem(LoadBatchCallback); } } private void LoadBatchCallback(object info) { LoadBatch(); } private void LoadBatch() { running = true; int totalTasks = queue.Count; int tasksCompleted = 0; while (queue.Count != 0) { lock (queue) { AssetOutline 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(); } IDisposable disposable = null; assets.TryRemove(name, out disposable); } } } /// /// 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) { foreach (KeyValuePair asset in assets) { asset.Value.Dispose(); } assets.Clear(); ClearQueue(); Debug.WriteLine("Unloaded all assets."); } } private struct AssetOutline { internal Type type; internal string assetName; internal bool usePathModifier; public AssetOutline(string assetName, Type type, bool usePathModifier) { this.type = type; this.assetName = assetName; this.usePathModifier = usePathModifier; } } } }