#region License
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// (C) 2008 John Sedlak
//
// Authors:
// John Sedlak (kriscsc@msn.com)
//
#endregion
using System;
using System.Threading;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;
using FocusedGames.Thrust;
namespace FocusedGames.Thrust.Content
{
/// <summary>
/// Manages content both synchronously and asynchronously and allows for the piece-wise unloading of assets.
/// </summary>
public class AssetManager : ContentManager
{
#region Private Members
private List
<ILoadable
> content
= new List
<ILoadable
>();
private Queue
<ILoadable
> asyncLoadables
= new Queue
<ILoadable
>();
private int current;
private int total;
private bool isLoading = false;
private bool simulateLargeContent = false;
#endregion
#region Events
private event EventHandler InternalBeginLoad;
/// <summary>
/// Fired when the asset manager has finished loading a group of assets.
/// </summary>
public event EventHandler FinishedLoad;
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
/// <param name="serviceProvider">The Game's service container.</param>
public AssetManager(IServiceProvider serviceProvider)
: this(serviceProvider, String.Empty)
{
}
/// <summary>
/// Secondary constructor.
/// </summary>
/// <param name="serviceProvider">The Game's service container.</param>
/// <param name="rootDirectory">The base directory where all assets are stored.</param>
public AssetManager(IServiceProvider serviceProvider, string rootDirectory)
: base(serviceProvider, rootDirectory)
{
InternalBeginLoad += OnBeginLoad;
}
#endregion
#region Unloading
/// <summary>
/// DO NOT USE THIS. IT DOES NOTHING.
/// </summary>
/// <see cref="Unload(ILoadable)"/>
{
}
/// <summary>
/// Unloads a single asset.
/// </summary>
/// <param name="asset">The asset to unload.</param>
public void Unload(ILoadable asset)
{
if (content.Contains(asset))
content.Remove(asset);
asset.Unload();
asset = null;
}
#endregion
#region Loading
/// <summary>
/// Loads a custom asset.
/// </summary>
/// <typeparam name="K">The type of asset to load.</typeparam>
/// <param name="source">The source file of the asset.</param>
/// <param name="asyncLoad">Whether to load it now or asynchronously and later on.</param>
/// <returns>A custom asset object.</returns>
public K LoadCustom
<K
>(string source,
bool asyncLoad
) where K
: IAsset,
new()
{
newAsset.Source = source;
newAsset.AssetManager = this;
if (!asyncLoad)
{
newAsset.Load();
content.Add(newAsset);
}
else
{
asyncLoadables.Enqueue(newAsset);
}
return newAsset;
}
/// <summary>
/// Loads an asset.
/// </summary>
/// <typeparam name="T">The type of content item to load.</typeparam>
/// <param name="source">The source file of the asset.</param>
/// <param name="asyncLoad">Whether to load it now or asynchronously and later on.</param>
/// <returns>An Asset object.</returns>
/// <seealso cref="Asset{T}"/>
public Asset<T> Load<T>(string source, bool asyncLoad) where T : class
{
Asset
<T
> newAsset
= new Asset
<T
>();
newAsset.Source = source;
newAsset.AssetManager = this;
if (!asyncLoad)
{
newAsset.Load();
content.Add(newAsset);
}
else
{
asyncLoadables.Enqueue(newAsset);
}
return newAsset;
}
#endregion
#region Internal Loading (Async)
/// <summary>
/// Begins the asynchronous process of loading.
/// </summary>
public void BeginLoad()
{
isLoading = true;
#if ZUNE
InternalBeginLoad.Invoke(this, EventArgs.Empty);
OnFinishLoad(null);
#else
InternalBeginLoad.BeginInvoke(this, EventArgs.Empty, OnFinishLoad, null);
#endif
}
private void OnBeginLoad(object sender, EventArgs e)
{
total = asyncLoadables.Count;
current = 0;
while (asyncLoadables.Count > 0)
{
ILoadable asset = asyncLoadables.Dequeue();
if (asset.IsLoaded || content.Contains(asset))
continue;
asset.Load();
current++;
content.Add(asset);
if (SimulateLargeContent)
Thread.Sleep(250);
}
}
private void OnFinishLoad(IAsyncResult result)
{
#if !ZUNE
InternalBeginLoad.EndInvoke(result);
#endif
isLoading = false;
if (FinishedLoad != null)
FinishedLoad.Invoke(this, EventArgs.Empty);
}
#endregion
#region Properties
/// <summary>
/// Gets or Sets whether or not to simulate large content.
/// </summary>
public bool SimulateLargeContent
{
get { return simulateLargeContent; }
set { simulateLargeContent = value; }
}
/// <summary>
/// Gets the current number of assets loaded.
/// </summary>
public int Current { get { return current; } }
/// <summary>
/// Gets the total number of assets that need to be loaded.
/// </summary>
public int Total { get { return total; } }
/// <summary>
/// Gets whether or not the object is loading assets.
/// </summary>
public bool IsLoading { get { return isLoading; } }
#endregion
}
}