using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.Serialization;
namespace UnityEngine.ResourceManagement.Util
{
///
/// Interface for objects that support post construction initialization via an id and byte array.
///
public interface IInitializableObject
{
///
/// Initialize a constructed object.
///
/// The id of the object.
/// Serialized data for the object.
/// The result of the initialization.
bool Initialize(string id, string data);
///
/// Async operation for initializing a constructed object.
///
/// The current instance of Resource Manager.
/// The id of the object.
/// Serialized data for the object.
/// Async operation
AsyncOperationHandle InitializeAsync(ResourceManager rm, string id, string data);
}
///
/// Interface for objects that can create object initialization data.
///
public interface IObjectInitializationDataProvider
{
///
/// The name used in the GUI for this provider
///
string Name { get; }
///
/// Construct initialization data for runtime.
///
/// Init data that will be deserialized at runtime.
ObjectInitializationData CreateObjectInitializationData();
}
///
/// Allocation strategy for managing heap allocations
///
public interface IAllocationStrategy
{
///
/// Create a new object of type t.
///
/// The type to return.
/// The hash code of the type.
/// The new object.
object New(Type type, int typeHash);
///
/// Release an object.
///
/// The hash code of the type.
/// The object to release.
void Release(int typeHash, object obj);
}
///
/// Default allocator that relies in garbace collection
///
public class DefaultAllocationStrategy : IAllocationStrategy
{
///
public object New(Type type, int typeHash)
{
return Activator.CreateInstance(type);
}
///
public void Release(int typeHash, object obj)
{
}
}
///
/// Allocation strategy that uses internal pools of objects to avoid allocations that can trigger GC calls.
///
public class LRUCacheAllocationStrategy : IAllocationStrategy
{
int m_poolMaxSize;
int m_poolInitialCapacity;
int m_poolCacheMaxSize;
List> m_poolCache = new List>();
Dictionary> m_cache = new Dictionary>();
///
/// Create a new LRUAllocationStrategy objct.
///
/// The max size of each pool.
/// The initial capacity to create each pool list with.
/// The max size of the internal pool cache.
/// The initial number of pools to create.
public LRUCacheAllocationStrategy(int poolMaxSize, int poolCapacity, int poolCacheMaxSize, int initialPoolCacheCapacity)
{
m_poolMaxSize = poolMaxSize;
m_poolInitialCapacity = poolCapacity;
m_poolCacheMaxSize = poolCacheMaxSize;
for (int i = 0; i < initialPoolCacheCapacity; i++)
m_poolCache.Add(new List(m_poolInitialCapacity));
}
List GetPool()
{
int count = m_poolCache.Count;
if (count == 0)
return new List(m_poolInitialCapacity);
var pool = m_poolCache[count - 1];
m_poolCache.RemoveAt(count - 1);
return pool;
}
void ReleasePool(List pool)
{
if (m_poolCache.Count < m_poolCacheMaxSize)
m_poolCache.Add(pool);
}
///
public object New(Type type, int typeHash)
{
List pool;
if (m_cache.TryGetValue(typeHash, out pool))
{
var count = pool.Count;
var v = pool[count - 1];
pool.RemoveAt(count - 1);
if (count == 1)
{
m_cache.Remove(typeHash);
ReleasePool(pool);
}
return v;
}
return Activator.CreateInstance(type);
}
///
public void Release(int typeHash, object obj)
{
List pool;
if (!m_cache.TryGetValue(typeHash, out pool))
m_cache.Add(typeHash, pool = GetPool());
if (pool.Count < m_poolMaxSize)
pool.Add(obj);
}
}
///
/// Attribute for restricting which types can be assigned to a SerializedType
///
public class SerializedTypeRestrictionAttribute : Attribute
{
///
/// The type to restrict a serialized type to.
///
public Type type;
}
///
/// Cache for nodes of LinkedLists. This can be used to eliminate GC allocations.
///
/// The type of node.
public class LinkedListNodeCache
{
int m_NodesCreated = 0;
LinkedList m_NodeCache;
///
/// Creates or returns a LinkedListNode of the requested type and set the value.
///
/// The value to set to returned node to.
/// A LinkedListNode with the value set to val.
public LinkedListNode Acquire(T val)
{
if (m_NodeCache != null)
{
var n = m_NodeCache.First;
if (n != null)
{
m_NodeCache.RemoveFirst();
n.Value = val;
return n;
}
}
m_NodesCreated++;
return new LinkedListNode(val);
}
///
/// Release the linked list node for later use.
///
///
public void Release(LinkedListNode node)
{
if (m_NodeCache == null)
m_NodeCache = new LinkedList();
node.Value = default(T);
m_NodeCache.AddLast(node);
}
internal int CreatedNodeCount
{
get { return m_NodesCreated; }
}
internal int CachedNodeCount
{
get { return m_NodeCache == null ? 0 : m_NodeCache.Count; }
set
{
if (m_NodeCache == null)
m_NodeCache = new LinkedList();
while (value < m_NodeCache.Count)
m_NodeCache.RemoveLast();
while (value > m_NodeCache.Count)
m_NodeCache.AddLast(new LinkedListNode(default));
}
}
}
internal static class GlobalLinkedListNodeCache
{
static LinkedListNodeCache m_globalCache;
public static bool CacheExists => m_globalCache != null;
public static void SetCacheSize(int length)
{
if (m_globalCache == null)
m_globalCache = new LinkedListNodeCache();
m_globalCache.CachedNodeCount = length;
}
public static LinkedListNode Acquire(T val)
{
if (m_globalCache == null)
m_globalCache = new LinkedListNodeCache();
return m_globalCache.Acquire(val);
}
public static void Release(LinkedListNode node)
{
if (m_globalCache == null)
m_globalCache = new LinkedListNodeCache();
m_globalCache.Release(node);
}
}
///
/// Wrapper for serializing types for runtime.
///
[Serializable]
public struct SerializedType
{
[FormerlySerializedAs("m_assemblyName")]
[SerializeField]
string m_AssemblyName;
///
/// The assembly name of the type.
///
public string AssemblyName
{
get { return m_AssemblyName; }
}
[FormerlySerializedAs("m_className")]
[SerializeField]
string m_ClassName;
///
/// The name of the type.
///
public string ClassName
{
get { return m_ClassName; }
}
Type m_CachedType;
///
/// Converts information about the serialized type to a formatted string.
///
/// Returns information about the serialized type.
public override string ToString()
{
return Value == null ? "" : Value.Name;
}
///
/// Get and set the serialized type.
///
public Type Value
{
get
{
try
{
if (string.IsNullOrEmpty(m_AssemblyName) || string.IsNullOrEmpty(m_ClassName))
return null;
if (m_CachedType == null)
{
var assembly = Assembly.Load(m_AssemblyName);
if (assembly != null)
m_CachedType = assembly.GetType(m_ClassName);
}
return m_CachedType;
}
catch (Exception ex)
{
//file not found is most likely an editor only type, we can ignore error.
if (ex.GetType() != typeof(FileNotFoundException))
Debug.LogException(ex);
return null;
}
}
set
{
if (value != null)
{
m_AssemblyName = value.Assembly.FullName;
m_ClassName = value.FullName;
}
else
{
m_AssemblyName = m_ClassName = null;
}
}
}
///
/// Used for multi-object editing. Indicates whether or not property value was changed.
///
public bool ValueChanged { get; set; }
}
///
/// Contains data used to construct and initialize objects at runtime.
///
[Serializable]
public struct ObjectInitializationData
{
#pragma warning disable 0649
[FormerlySerializedAs("m_id")]
[SerializeField]
string m_Id;
///
/// The object id.
///
public string Id
{
get { return m_Id; }
}
[FormerlySerializedAs("m_objectType")]
[SerializeField]
SerializedType m_ObjectType;
///
/// The object type that will be created.
///
public SerializedType ObjectType
{
get { return m_ObjectType; }
}
[FormerlySerializedAs("m_data")]
[SerializeField]
string m_Data;
///
/// String representation of the data that will be passed to the IInitializableObject.Initialize method of the created object. This is usually a JSON string of the serialized data object.
///
public string Data
{
get { return m_Data; }
}
#pragma warning restore 0649
#if ENABLE_BINARY_CATALOG
internal class Serializer : BinaryStorageBuffer.ISerializationAdapter
{
struct Data
{
public uint id;
public uint type;
public uint data;
}
public IEnumerable Dependencies => null;
public object Deserialize(BinaryStorageBuffer.Reader reader, Type t, uint offset)
{
var d = reader.ReadValue(offset);
return new ObjectInitializationData { m_Id = reader.ReadString(d.id), m_ObjectType = new SerializedType { Value = reader.ReadObject(d.type) }, m_Data = reader.ReadString(d.data) };
}
public uint Serialize(BinaryStorageBuffer.Writer writer, object val)
{
var oid = (ObjectInitializationData)val;
var d = new Data
{
id = writer.WriteString(oid.m_Id),
type = writer.WriteObject(oid.ObjectType.Value, false),
data = writer.WriteString(oid.m_Data)
};
return writer.Write(d);
}
}
#endif
///
/// Converts information about the initialization data to a formatted string.
///
/// Returns information about the initialization data.
public override string ToString()
{
return string.Format("ObjectInitializationData: id={0}, type={1}", m_Id, m_ObjectType);
}
///
/// Create an instance of the defined object. Initialize will be called on it with the id and data if it implements the IInitializableObject interface.
///
/// The instance type.
/// Optional id to assign to the created object. This only applies to objects that inherit from IInitializableObject.
/// Constructed object. This object will already be initialized with its serialized data and id.
public TObject CreateInstance(string idOverride = null)
{
try
{
var objType = m_ObjectType.Value;
if (objType == null)
return default(TObject);
var obj = Activator.CreateInstance(objType, true);
var serObj = obj as IInitializableObject;
if (serObj != null)
{
if (!serObj.Initialize(idOverride == null ? m_Id : idOverride, m_Data))
return default(TObject);
}
return (TObject)obj;
}
catch (Exception ex)
{
Debug.LogException(ex);
return default(TObject);
}
}
///
/// Create an instance of the defined object. This will get the AsyncOperationHandle for the async Initialization operation if the object implements the IInitializableObject interface.
///
/// The current instance of Resource Manager
/// Optional id to assign to the created object. This only applies to objects that inherit from IInitializableObject.
/// AsyncOperationHandle for the async initialization operation if the defined type implements IInitializableObject, otherwise returns a default AsyncOperationHandle.
public AsyncOperationHandle GetAsyncInitHandle(ResourceManager rm, string idOverride = null)
{
try
{
var objType = m_ObjectType.Value;
if (objType == null)
return default(AsyncOperationHandle);
var obj = Activator.CreateInstance(objType, true);
var serObj = obj as IInitializableObject;
if (serObj != null)
return serObj.InitializeAsync(rm, idOverride == null ? m_Id : idOverride, m_Data);
return default(AsyncOperationHandle);
}
catch (Exception ex)
{
Debug.LogException(ex);
return default(AsyncOperationHandle);
}
}
#if UNITY_EDITOR
Type[] m_RuntimeTypes;
///
/// Construct a serialized data for the object.
///
/// The type of object to create.
/// The object id.
/// The serializable object that will be saved into the Data string via the JSONUtility.ToJson method.
/// Contains data used to construct and initialize an object at runtime.
public static ObjectInitializationData CreateSerializedInitializationData(Type objectType, string id = null, object dataObject = null)
{
return new ObjectInitializationData
{
m_ObjectType = new SerializedType {Value = objectType},
m_Id = string.IsNullOrEmpty(id) ? objectType.FullName : id,
m_Data = dataObject == null ? null : JsonUtility.ToJson(dataObject),
m_RuntimeTypes = dataObject == null ? null : new[] {dataObject.GetType()}
};
}
///
/// Construct a serialized data for the object.
///
/// The type of object to create.
/// The ID for the object
/// The serializable object that will be saved into the Data string via the JSONUtility.ToJson method.
/// Contains data used to construct and initialize an object at runtime.
public static ObjectInitializationData CreateSerializedInitializationData(string id = null, object dataObject = null)
{
return CreateSerializedInitializationData(typeof(T), id, dataObject);
}
///
/// Get the set of runtime types need to deserialized this object. This is used to ensure that types are not stripped from player builds.
///
///
public Type[] GetRuntimeTypes()
{
return m_RuntimeTypes;
}
#endif
}
///
/// Resource Manager Config utility class.
///
public static class ResourceManagerConfig
{
///
/// Extracts main and subobject keys if properly formatted
///
/// The key as an object.
/// The key of the main asset. This will be set to null if a sub key is not found.
/// The key of the sub object. This will be set to null if not found.
/// Returns true if properly formatted keys are extracted.
public static bool ExtractKeyAndSubKey(object keyObj, out string mainKey, out string subKey)
{
var key = keyObj as string;
if (key != null)
{
var i = key.IndexOf('[');
if (i > 0)
{
var j = key.LastIndexOf(']');
if (j > i)
{
mainKey = key.Substring(0, i);
subKey = key.Substring(i + 1, j - (i + 1));
return true;
}
}
}
mainKey = null;
subKey = null;
return false;
}
///
/// Check to see if a path is remote or not.
///
/// The path to check.
/// Returns true if path is remote.
public static bool IsPathRemote(string path)
{
return path != null && path.StartsWith("http", StringComparison.Ordinal);
}
///
/// Strips the query parameters of an url.
///
/// The path to check.
/// Returns the path without query parameters.
public static string StripQueryParameters(string path)
{
if (path != null)
{
var idx = path.IndexOf('?');
if (idx >= 0)
return path.Substring(0, idx);
}
return path;
}
///
/// Check if path should use WebRequest. A path should use WebRequest for remote paths and platforms that require WebRequest to load locally.
///
/// The path to check.
/// Returns true if path should use WebRequest.
public static bool ShouldPathUseWebRequest(string path)
{
if (PlatformCanLoadLocallyFromUrlPath() && File.Exists(path))
return false;
return path != null && path.Contains("://");
}
///
/// Checks if the current platform can use urls for load loads.
///
/// True if the current platform can use urls for local loads, false otherwise.
private static bool PlatformCanLoadLocallyFromUrlPath()
{
//For something so simple, this is pretty over engineered. But, if more platforms come up that do this, it'll be easy to account for them.
//Just add runtime platforms to this list that do the same thing Android does.
List platformsThatUseUrlForLocalLoads = new List()
{
RuntimePlatform.Android
};
return platformsThatUseUrlForLocalLoads.Contains((Application.platform));
}
///
/// Used to create an operation result that has multiple items.
///
/// The type of the result.
/// The result objects.
/// Returns Array object with result items.
public static Array CreateArrayResult(Type type, Object[] allAssets)
{
var elementType = type.GetElementType();
if (elementType == null)
return null;
int length = 0;
foreach (var asset in allAssets)
{
if (elementType.IsAssignableFrom(asset.GetType()))
length++;
}
var array = Array.CreateInstance(elementType, length);
int index = 0;
foreach (var asset in allAssets)
{
if (elementType.IsAssignableFrom(asset.GetType()))
array.SetValue(asset, index++);
}
return array;
}
///
/// Used to create an operation result that has multiple items.
///
/// The type of the result.
/// The result objects.
/// Returns result Array as TObject.
public static TObject CreateArrayResult(Object[] allAssets) where TObject : class
{
return CreateArrayResult(typeof(TObject), allAssets) as TObject;
}
///
/// Used to create an operation result that has multiple items.
///
/// The type of the result objects.
/// The result objects.
/// An IList of the resulting objects.
public static IList CreateListResult(Type type, Object[] allAssets)
{
var genArgs = type.GetGenericArguments();
var listType = typeof(List<>).MakeGenericType(genArgs);
var list = Activator.CreateInstance(listType) as IList;
var elementType = genArgs[0];
if (list == null)
return null;
foreach (var a in allAssets)
{
if (elementType.IsAssignableFrom(a.GetType()))
list.Add(a);
}
return list;
}
///
/// Used to create an operation result that has multiple items.
///
/// The type of the result.
/// The result objects.
/// An IList of the resulting objects converted to TObject.
public static TObject CreateListResult(Object[] allAssets)
{
return (TObject)CreateListResult(typeof(TObject), allAssets);
}
///
/// Check if one type is an instance of another type.
///
/// Expected base type.
/// Expected child type.
/// Returns true if T2 is a base type of T1.
public static bool IsInstance()
{
var tA = typeof(T1);
var tB = typeof(T2);
#if !UNITY_EDITOR && UNITY_WSA_10_0 && ENABLE_DOTNET
return tB.GetTypeInfo().IsAssignableFrom(tA.GetTypeInfo());
#else
return tB.IsAssignableFrom(tA);
#endif
}
}
[System.Flags]
internal enum BundleSource
{
None = 0,
Local = 1,
Cache = 2,
Download = 4
}
}