736 lines
26 KiB
C#
736 lines
26 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Interface for objects that support post construction initialization via an id and byte array.
|
|
/// </summary>
|
|
public interface IInitializableObject
|
|
{
|
|
/// <summary>
|
|
/// Initialize a constructed object.
|
|
/// </summary>
|
|
/// <param name="id">The id of the object.</param>
|
|
/// <param name="data">Serialized data for the object.</param>
|
|
/// <returns>The result of the initialization.</returns>
|
|
bool Initialize(string id, string data);
|
|
|
|
/// <summary>
|
|
/// Async operation for initializing a constructed object.
|
|
/// </summary>
|
|
/// <param name="rm">The current instance of Resource Manager.</param>
|
|
/// <param name="id">The id of the object.</param>
|
|
/// <param name="data">Serialized data for the object.</param>
|
|
/// <returns>Async operation</returns>
|
|
AsyncOperationHandle<bool> InitializeAsync(ResourceManager rm, string id, string data);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Interface for objects that can create object initialization data.
|
|
/// </summary>
|
|
public interface IObjectInitializationDataProvider
|
|
{
|
|
/// <summary>
|
|
/// The name used in the GUI for this provider
|
|
/// </summary>
|
|
string Name { get; }
|
|
|
|
/// <summary>
|
|
/// Construct initialization data for runtime.
|
|
/// </summary>
|
|
/// <returns>Init data that will be deserialized at runtime.</returns>
|
|
ObjectInitializationData CreateObjectInitializationData();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allocation strategy for managing heap allocations
|
|
/// </summary>
|
|
public interface IAllocationStrategy
|
|
{
|
|
/// <summary>
|
|
/// Create a new object of type t.
|
|
/// </summary>
|
|
/// <param name="type">The type to return.</param>
|
|
/// <param name="typeHash">The hash code of the type.</param>
|
|
/// <returns>The new object.</returns>
|
|
object New(Type type, int typeHash);
|
|
|
|
/// <summary>
|
|
/// Release an object.
|
|
/// </summary>
|
|
/// <param name="typeHash">The hash code of the type.</param>
|
|
/// <param name="obj">The object to release.</param>
|
|
void Release(int typeHash, object obj);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default allocator that relies in garbace collection
|
|
/// </summary>
|
|
public class DefaultAllocationStrategy : IAllocationStrategy
|
|
{
|
|
/// <inheritdoc/>
|
|
public object New(Type type, int typeHash)
|
|
{
|
|
return Activator.CreateInstance(type);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Release(int typeHash, object obj)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allocation strategy that uses internal pools of objects to avoid allocations that can trigger GC calls.
|
|
/// </summary>
|
|
public class LRUCacheAllocationStrategy : IAllocationStrategy
|
|
{
|
|
int m_poolMaxSize;
|
|
int m_poolInitialCapacity;
|
|
int m_poolCacheMaxSize;
|
|
List<List<object>> m_poolCache = new List<List<object>>();
|
|
Dictionary<int, List<object>> m_cache = new Dictionary<int, List<object>>();
|
|
|
|
/// <summary>
|
|
/// Create a new LRUAllocationStrategy objct.
|
|
/// </summary>
|
|
/// <param name="poolMaxSize">The max size of each pool.</param>
|
|
/// <param name="poolCapacity">The initial capacity to create each pool list with.</param>
|
|
/// <param name="poolCacheMaxSize">The max size of the internal pool cache.</param>
|
|
/// <param name="initialPoolCacheCapacity">The initial number of pools to create.</param>
|
|
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<object>(m_poolInitialCapacity));
|
|
}
|
|
|
|
List<object> GetPool()
|
|
{
|
|
int count = m_poolCache.Count;
|
|
if (count == 0)
|
|
return new List<object>(m_poolInitialCapacity);
|
|
var pool = m_poolCache[count - 1];
|
|
m_poolCache.RemoveAt(count - 1);
|
|
return pool;
|
|
}
|
|
|
|
void ReleasePool(List<object> pool)
|
|
{
|
|
if (m_poolCache.Count < m_poolCacheMaxSize)
|
|
m_poolCache.Add(pool);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public object New(Type type, int typeHash)
|
|
{
|
|
List<object> 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);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void Release(int typeHash, object obj)
|
|
{
|
|
List<object> pool;
|
|
if (!m_cache.TryGetValue(typeHash, out pool))
|
|
m_cache.Add(typeHash, pool = GetPool());
|
|
if (pool.Count < m_poolMaxSize)
|
|
pool.Add(obj);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attribute for restricting which types can be assigned to a SerializedType
|
|
/// </summary>
|
|
public class SerializedTypeRestrictionAttribute : Attribute
|
|
{
|
|
/// <summary>
|
|
/// The type to restrict a serialized type to.
|
|
/// </summary>
|
|
public Type type;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cache for nodes of LinkedLists. This can be used to eliminate GC allocations.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of node.</typeparam>
|
|
public class LinkedListNodeCache<T>
|
|
{
|
|
int m_NodesCreated = 0;
|
|
LinkedList<T> m_NodeCache;
|
|
|
|
/// <summary>
|
|
/// Creates or returns a LinkedListNode of the requested type and set the value.
|
|
/// </summary>
|
|
/// <param name="val">The value to set to returned node to.</param>
|
|
/// <returns>A LinkedListNode with the value set to val.</returns>
|
|
public LinkedListNode<T> 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<T>(val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release the linked list node for later use.
|
|
/// </summary>
|
|
/// <param name="node"></param>
|
|
public void Release(LinkedListNode<T> node)
|
|
{
|
|
if (m_NodeCache == null)
|
|
m_NodeCache = new LinkedList<T>();
|
|
|
|
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<T>();
|
|
while (value < m_NodeCache.Count)
|
|
m_NodeCache.RemoveLast();
|
|
while (value > m_NodeCache.Count)
|
|
m_NodeCache.AddLast(new LinkedListNode<T>(default));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static class GlobalLinkedListNodeCache<T>
|
|
{
|
|
static LinkedListNodeCache<T> m_globalCache;
|
|
|
|
public static bool CacheExists => m_globalCache != null;
|
|
|
|
public static void SetCacheSize(int length)
|
|
{
|
|
if (m_globalCache == null)
|
|
m_globalCache = new LinkedListNodeCache<T>();
|
|
m_globalCache.CachedNodeCount = length;
|
|
}
|
|
|
|
public static LinkedListNode<T> Acquire(T val)
|
|
{
|
|
if (m_globalCache == null)
|
|
m_globalCache = new LinkedListNodeCache<T>();
|
|
return m_globalCache.Acquire(val);
|
|
}
|
|
|
|
public static void Release(LinkedListNode<T> node)
|
|
{
|
|
if (m_globalCache == null)
|
|
m_globalCache = new LinkedListNodeCache<T>();
|
|
m_globalCache.Release(node);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wrapper for serializing types for runtime.
|
|
/// </summary>
|
|
[Serializable]
|
|
public struct SerializedType
|
|
{
|
|
[FormerlySerializedAs("m_assemblyName")]
|
|
[SerializeField]
|
|
string m_AssemblyName;
|
|
|
|
/// <summary>
|
|
/// The assembly name of the type.
|
|
/// </summary>
|
|
public string AssemblyName
|
|
{
|
|
get { return m_AssemblyName; }
|
|
}
|
|
|
|
[FormerlySerializedAs("m_className")]
|
|
[SerializeField]
|
|
string m_ClassName;
|
|
|
|
/// <summary>
|
|
/// The name of the type.
|
|
/// </summary>
|
|
public string ClassName
|
|
{
|
|
get { return m_ClassName; }
|
|
}
|
|
|
|
Type m_CachedType;
|
|
|
|
/// <summary>
|
|
/// Converts information about the serialized type to a formatted string.
|
|
/// </summary>
|
|
/// <returns>Returns information about the serialized type.</returns>
|
|
public override string ToString()
|
|
{
|
|
return Value == null ? "<none>" : Value.Name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get and set the serialized type.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used for multi-object editing. Indicates whether or not property value was changed.
|
|
/// </summary>
|
|
public bool ValueChanged { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains data used to construct and initialize objects at runtime.
|
|
/// </summary>
|
|
[Serializable]
|
|
public struct ObjectInitializationData
|
|
{
|
|
#pragma warning disable 0649
|
|
[FormerlySerializedAs("m_id")]
|
|
[SerializeField]
|
|
string m_Id;
|
|
|
|
/// <summary>
|
|
/// The object id.
|
|
/// </summary>
|
|
public string Id
|
|
{
|
|
get { return m_Id; }
|
|
}
|
|
|
|
[FormerlySerializedAs("m_objectType")]
|
|
[SerializeField]
|
|
SerializedType m_ObjectType;
|
|
|
|
/// <summary>
|
|
/// The object type that will be created.
|
|
/// </summary>
|
|
public SerializedType ObjectType
|
|
{
|
|
get { return m_ObjectType; }
|
|
}
|
|
|
|
[FormerlySerializedAs("m_data")]
|
|
[SerializeField]
|
|
string m_Data;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public string Data
|
|
{
|
|
get { return m_Data; }
|
|
}
|
|
#pragma warning restore 0649
|
|
|
|
#if ENABLE_BINARY_CATALOG
|
|
internal class Serializer : BinaryStorageBuffer.ISerializationAdapter<ObjectInitializationData>
|
|
{
|
|
struct Data
|
|
{
|
|
public uint id;
|
|
public uint type;
|
|
public uint data;
|
|
}
|
|
|
|
public IEnumerable<BinaryStorageBuffer.ISerializationAdapter> Dependencies => null;
|
|
|
|
|
|
public object Deserialize(BinaryStorageBuffer.Reader reader, Type t, uint offset)
|
|
{
|
|
var d = reader.ReadValue<Data>(offset);
|
|
return new ObjectInitializationData { m_Id = reader.ReadString(d.id), m_ObjectType = new SerializedType { Value = reader.ReadObject<Type>(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
|
|
/// <summary>
|
|
/// Converts information about the initialization data to a formatted string.
|
|
/// </summary>
|
|
/// <returns>Returns information about the initialization data.</returns>
|
|
public override string ToString()
|
|
{
|
|
return string.Format("ObjectInitializationData: id={0}, type={1}", m_Id, m_ObjectType);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an instance of the defined object. Initialize will be called on it with the id and data if it implements the IInitializableObject interface.
|
|
/// </summary>
|
|
/// <typeparam name="TObject">The instance type.</typeparam>
|
|
/// <param name="idOverride">Optional id to assign to the created object. This only applies to objects that inherit from IInitializableObject.</param>
|
|
/// <returns>Constructed object. This object will already be initialized with its serialized data and id.</returns>
|
|
public TObject CreateInstance<TObject>(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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an instance of the defined object. This will get the AsyncOperationHandle for the async Initialization operation if the object implements the IInitializableObject interface.
|
|
/// </summary>
|
|
/// <param name="rm">The current instance of Resource Manager</param>
|
|
/// <param name="idOverride">Optional id to assign to the created object. This only applies to objects that inherit from IInitializableObject.</param>
|
|
/// <returns>AsyncOperationHandle for the async initialization operation if the defined type implements IInitializableObject, otherwise returns a default AsyncOperationHandle.</returns>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Construct a serialized data for the object.
|
|
/// </summary>
|
|
/// <param name="objectType">The type of object to create.</param>
|
|
/// <param name="id">The object id.</param>
|
|
/// <param name="dataObject">The serializable object that will be saved into the Data string via the JSONUtility.ToJson method.</param>
|
|
/// <returns>Contains data used to construct and initialize an object at runtime.</returns>
|
|
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()}
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct a serialized data for the object.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of object to create.</typeparam>
|
|
/// <param name="id">The ID for the object</param>
|
|
/// <param name="dataObject">The serializable object that will be saved into the Data string via the JSONUtility.ToJson method.</param>
|
|
/// <returns>Contains data used to construct and initialize an object at runtime.</returns>
|
|
public static ObjectInitializationData CreateSerializedInitializationData<T>(string id = null, object dataObject = null)
|
|
{
|
|
return CreateSerializedInitializationData(typeof(T), id, dataObject);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the set of runtime types need to deserialized this object. This is used to ensure that types are not stripped from player builds.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Type[] GetRuntimeTypes()
|
|
{
|
|
return m_RuntimeTypes;
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resource Manager Config utility class.
|
|
/// </summary>
|
|
public static class ResourceManagerConfig
|
|
{
|
|
/// <summary>
|
|
/// Extracts main and subobject keys if properly formatted
|
|
/// </summary>
|
|
/// <param name="keyObj">The key as an object.</param>
|
|
/// <param name="mainKey">The key of the main asset. This will be set to null if a sub key is not found.</param>
|
|
/// <param name="subKey">The key of the sub object. This will be set to null if not found.</param>
|
|
/// <returns>Returns true if properly formatted keys are extracted.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if a path is remote or not.
|
|
/// </summary>
|
|
/// <param name="path">The path to check.</param>
|
|
/// <returns>Returns true if path is remote.</returns>
|
|
public static bool IsPathRemote(string path)
|
|
{
|
|
return path != null && path.StartsWith("http", StringComparison.Ordinal);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Strips the query parameters of an url.
|
|
/// </summary>
|
|
/// <param name="path">The path to check.</param>
|
|
/// <returns>Returns the path without query parameters.</returns>
|
|
public static string StripQueryParameters(string path)
|
|
{
|
|
if (path != null)
|
|
{
|
|
var idx = path.IndexOf('?');
|
|
if (idx >= 0)
|
|
return path.Substring(0, idx);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if path should use WebRequest. A path should use WebRequest for remote paths and platforms that require WebRequest to load locally.
|
|
/// </summary>
|
|
/// <param name="path">The path to check.</param>
|
|
/// <returns>Returns true if path should use WebRequest.</returns>
|
|
public static bool ShouldPathUseWebRequest(string path)
|
|
{
|
|
if (PlatformCanLoadLocallyFromUrlPath() && File.Exists(path))
|
|
return false;
|
|
return path != null && path.Contains("://");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the current platform can use urls for load loads.
|
|
/// </summary>
|
|
/// <returns>True if the current platform can use urls for local loads, false otherwise.</returns>
|
|
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<RuntimePlatform> platformsThatUseUrlForLocalLoads = new List<RuntimePlatform>()
|
|
{
|
|
RuntimePlatform.Android
|
|
};
|
|
|
|
return platformsThatUseUrlForLocalLoads.Contains((Application.platform));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to create an operation result that has multiple items.
|
|
/// </summary>
|
|
/// <param name="type">The type of the result.</param>
|
|
/// <param name="allAssets">The result objects.</param>
|
|
/// <returns>Returns Array object with result items.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to create an operation result that has multiple items.
|
|
/// </summary>
|
|
/// <typeparam name="TObject">The type of the result.</typeparam>
|
|
/// <param name="allAssets">The result objects.</param>
|
|
/// <returns>Returns result Array as TObject.</returns>
|
|
public static TObject CreateArrayResult<TObject>(Object[] allAssets) where TObject : class
|
|
{
|
|
return CreateArrayResult(typeof(TObject), allAssets) as TObject;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to create an operation result that has multiple items.
|
|
/// </summary>
|
|
/// <param name="type">The type of the result objects.</param>
|
|
/// <param name="allAssets">The result objects.</param>
|
|
/// <returns>An IList of the resulting objects.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to create an operation result that has multiple items.
|
|
/// </summary>
|
|
/// <typeparam name="TObject">The type of the result.</typeparam>
|
|
/// <param name="allAssets">The result objects.</param>
|
|
/// <returns>An IList of the resulting objects converted to TObject.</returns>
|
|
public static TObject CreateListResult<TObject>(Object[] allAssets)
|
|
{
|
|
return (TObject)CreateListResult(typeof(TObject), allAssets);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if one type is an instance of another type.
|
|
/// </summary>
|
|
/// <typeparam name="T1">Expected base type.</typeparam>
|
|
/// <typeparam name="T2">Expected child type.</typeparam>
|
|
/// <returns>Returns true if T2 is a base type of T1.</returns>
|
|
public static bool IsInstance<T1, T2>()
|
|
{
|
|
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
|
|
}
|
|
}
|