initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae38715d198830145a8bf7fa7adf9ee1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b6713137edb6b2439d3ff26eb990180
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyCompany("Unity Technologies")]
#if UNITY_EDITOR
[assembly: InternalsVisibleTo("Unity.Addressables.Editor")]
[assembly: InternalsVisibleTo("Unity.Addressables.Editor.Tests")]
#endif
[assembly: InternalsVisibleTo("Unity.Addressables.Tests")]
[assembly: InternalsVisibleTo("Unity.Addressables.Samples.Tests")]

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70c658661ab7843829cea681cc9c9319
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,53 @@
using System;
using UnityEngine.Serialization;
namespace UnityEngine.AddressableAssets
{
/// <summary>
/// Reference to an asset label. This class can be used in scripts as a field and will use a CustomPropertyDrawer to provide a DropDown UI of available labels.
/// </summary>
[Serializable]
public class AssetLabelReference : IKeyEvaluator
{
[FormerlySerializedAs("m_labelString")]
[SerializeField]
string m_LabelString;
/// <summary>
/// The label string.
/// </summary>
public string labelString
{
get { return m_LabelString; }
set { m_LabelString = value; }
}
/// <summary>
/// The runtime key used for indexing values in the Addressables system.
/// </summary>
public object RuntimeKey
{
get
{
if (labelString == null)
labelString = string.Empty;
return labelString;
}
}
/// <inheritdoc/>
public bool RuntimeKeyIsValid()
{
return !string.IsNullOrEmpty(RuntimeKey.ToString());
}
/// <summary>
/// Get the hash code of this object.
/// </summary>
/// <returns>The hash code of the label string.</returns>
public override int GetHashCode()
{
return labelString.GetHashCode();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7dc6ecd1c26fdf7479e71472994d72db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,944 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;
using UnityEngine.U2D;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.AddressableAssets
{
/// <summary>
/// Generic version of AssetReference class. This should not be used directly as CustomPropertyDrawers do not support generic types. Instead use the concrete derived classes such as AssetReferenceGameObject.
/// </summary>
/// <typeparam name="TObject"></typeparam>
[Serializable]
public class AssetReferenceT<TObject> : AssetReference where TObject : Object
{
/// <summary>
/// Construct a new AssetReference object.
/// </summary>
/// <param name="guid">The guid of the asset.</param>
public AssetReferenceT(string guid)
: base(guid)
{
}
#if UNITY_EDITOR
protected override internal Type DerivedClassType => typeof(TObject);
#endif
/// <summary>
/// Load the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use <see cref="Addressables.LoadAssetAsync{TObject}(object)"/> and pass your AssetReference in as the key.
///
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <returns>The load operation.</returns>
//[Obsolete("We have added Async to the name of all asynchronous methods (UnityUpgradable) -> LoadAssetAsync(*)", true)]
[Obsolete]
public AsyncOperationHandle<TObject> LoadAsset()
{
return LoadAssetAsync();
}
/// <summary>
/// Load the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use <see cref="Addressables.LoadAssetAsync{TObject}(object)"/> and pass your AssetReference in as the key.
/// on an AssetReference, use Addressables.LoadAssetAsync&lt;&gt;() and pass your AssetReference in as the key.
///
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <returns>The load operation.</returns>
public virtual AsyncOperationHandle<TObject> LoadAssetAsync()
{
return LoadAssetAsync<TObject>();
}
/// <inheritdoc/>
public override bool ValidateAsset(Object obj)
{
var type = obj.GetType();
return typeof(TObject).IsAssignableFrom(type);
}
/// <summary>
/// Validates that the asset located at a path is allowable for this asset reference. An asset is allowable if
/// it is of the correct type or if one of its sub-asset is.
/// </summary>
/// <param name="mainAssetPath">The path to the asset in question.</param>
/// <returns>Whether the referenced asset is valid.</returns>
public override bool ValidateAsset(string mainAssetPath)
{
#if UNITY_EDITOR
Type objType = typeof(TObject);
if (objType.IsAssignableFrom(AssetDatabase.GetMainAssetTypeAtPath(mainAssetPath)))
return true;
var types = AssetPathToTypes.GetTypesForAssetPath(mainAssetPath);
return types.Contains(objType);
#else
return false;
#endif
}
#if UNITY_EDITOR
/// <summary>
/// Type-specific override of parent editorAsset. Used by the editor to represent the main asset referenced.
/// </summary>
/// <returns>Editor Asset as type TObject, else null</returns>
public new TObject editorAsset
{
get => base.editorAsset as TObject;
}
#endif
}
/// <summary>
/// GameObject only asset reference.
/// </summary>
[Serializable]
public class AssetReferenceGameObject : AssetReferenceT<GameObject>
{
/// <summary>
/// Constructs a new reference to a GameObject.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceGameObject(string guid) : base(guid)
{
}
}
/// <summary>
/// Texture only asset reference.
/// </summary>
[Serializable]
public class AssetReferenceTexture : AssetReferenceT<Texture>
{
/// <summary>
/// Constructs a new reference to a Texture.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceTexture(string guid) : base(guid)
{
}
}
/// <summary>
/// Texture2D only asset reference.
/// </summary>
[Serializable]
public class AssetReferenceTexture2D : AssetReferenceT<Texture2D>
{
/// <summary>
/// Constructs a new reference to a Texture2D.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceTexture2D(string guid) : base(guid)
{
}
}
/// <summary>
/// Texture3D only asset reference
/// </summary>
[Serializable]
public class AssetReferenceTexture3D : AssetReferenceT<Texture3D>
{
/// <summary>
/// Constructs a new reference to a Texture3D.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceTexture3D(string guid) : base(guid)
{
}
}
/// <summary>
/// Sprite only asset reference.
/// </summary>
[Serializable]
public class AssetReferenceSprite : AssetReferenceT<Sprite>
{
/// <summary>
/// Constructs a new reference to a AssetReferenceSprite.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceSprite(string guid) : base(guid)
{
}
/// <inheritdoc/>
public override bool ValidateAsset(string path)
{
#if UNITY_EDITOR
if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(SpriteAtlas))
return true;
var type = AssetDatabase.GetMainAssetTypeAtPath(path);
bool isTexture = typeof(Texture2D).IsAssignableFrom(type);
if (isTexture)
{
var importer = AssetImporter.GetAtPath(path) as TextureImporter;
return (importer != null) && (importer.spriteImportMode != SpriteImportMode.None);
}
#endif
return false;
}
#if UNITY_EDITOR
/// <summary>
/// Typeless override of parent editorAsset. Used by the editor to represent the main asset referenced.
/// </summary>
public new Object editorAsset
{
get => GetEditorAssetInternal();
}
internal override Object GetEditorAssetInternal()
{
if (CachedAsset != null || string.IsNullOrEmpty(m_AssetGUID))
return CachedAsset;
var assetPath = AssetDatabase.GUIDToAssetPath(m_AssetGUID);
Type mainAssetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
Object asset = mainAssetType == typeof(SpriteAtlas) ? AssetDatabase.LoadAssetAtPath(assetPath, typeof(SpriteAtlas)) : AssetDatabase.LoadAssetAtPath(assetPath, DerivedClassType);
if (DerivedClassType == null)
return CachedAsset = asset;
if (asset == null)
Debug.LogWarning($"Assigned editorAsset does not match type {typeof(SpriteAtlas)} or {DerivedClassType}. EditorAsset will be null.");
return CachedAsset = asset;
}
internal override bool SetEditorAssetInternal(Object value)
{
if (value is SpriteAtlas)
return OnSetEditorAsset(value, typeof(SpriteAtlas));
return base.SetEditorAssetInternal(value);
}
#endif
}
/// <summary>
/// Assetreference that only allows atlassed sprites.
/// </summary>
[Serializable]
public class AssetReferenceAtlasedSprite : AssetReferenceT<Sprite>
{
/// <summary>
/// Constructs a new reference to a AssetReferenceAtlasedSprite.
/// </summary>
/// <param name="guid">The object guid.</param>
public AssetReferenceAtlasedSprite(string guid) : base(guid)
{
}
#if UNITY_EDITOR
internal override Object GetEditorAssetInternal()
{
if (CachedAsset != null || string.IsNullOrEmpty(m_AssetGUID))
return CachedAsset;
var assetPath = AssetDatabase.GUIDToAssetPath(m_AssetGUID);
Object asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(SpriteAtlas));
if (asset == null)
Debug.LogWarning($"Assigned editorAsset does not match type {typeof(SpriteAtlas)}. EditorAsset will be null.");
return CachedAsset = asset;
}
internal override bool SetEditorAssetInternal(Object value)
{
return OnSetEditorAsset(value, typeof(SpriteAtlas));
}
#endif
/// <inheritdoc/>
public override bool ValidateAsset(Object obj)
{
return obj is SpriteAtlas;
}
/// <inheritdoc/>
public override bool ValidateAsset(string path)
{
#if UNITY_EDITOR
return AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(SpriteAtlas);
#else
return false;
#endif
}
#if UNITY_EDITOR
/// <summary>
/// SpriteAtlas Type-specific override of parent editorAsset. Used by the editor to represent the main asset referenced.
/// </summary>
public new SpriteAtlas editorAsset
{
get
{
if (CachedAsset != null || string.IsNullOrEmpty(AssetGUID))
return CachedAsset as SpriteAtlas;
var assetPath = AssetDatabase.GUIDToAssetPath(AssetGUID);
var main = AssetDatabase.LoadMainAssetAtPath(assetPath) as SpriteAtlas;
if (main != null)
CachedAsset = main;
return main;
}
}
#endif
}
/// <summary>
/// Reference to an addressable asset. This can be used in script to provide fields that can be easily set in the editor and loaded dynamically at runtime.
/// To determine if the reference is set, use RuntimeKeyIsValid().
/// </summary>
[Serializable]
public class AssetReference : IKeyEvaluator
{
[FormerlySerializedAs("m_assetGUID")]
[SerializeField]
protected internal string m_AssetGUID = "";
[SerializeField]
string m_SubObjectName;
[SerializeField]
string m_SubObjectType = null;
AsyncOperationHandle m_Operation;
#if UNITY_EDITOR
virtual protected internal Type DerivedClassType { get; }
#endif
/// <summary>
/// The AsyncOperationHandle currently being used by the AssetReference.
/// For example, if you call AssetReference.LoadAssetAsync, this property will return a handle to that operation.
/// </summary>
public AsyncOperationHandle OperationHandle
{
get { return m_Operation; }
internal set
{
m_Operation = value;
#if UNITY_EDITOR
if (m_Operation.Status != AsyncOperationStatus.Failed)
m_ActiveAssetReferences.Add(this);
#endif
}
}
/// <summary>
/// The actual key used to request the asset at runtime. RuntimeKeyIsValid() can be used to determine if this reference was set.
/// </summary>
public virtual object RuntimeKey
{
get
{
if (m_AssetGUID == null)
m_AssetGUID = string.Empty;
if (!string.IsNullOrEmpty(m_SubObjectName))
return string.Format("{0}[{1}]", m_AssetGUID, m_SubObjectName);
return m_AssetGUID;
}
}
/// <summary>
/// Stores the guid of the asset.
/// </summary>
public virtual string AssetGUID
{
get { return m_AssetGUID; }
}
/// <summary>
/// Stores the name of the sub object.
/// </summary>
public virtual string SubObjectName
{
get { return m_SubObjectName; }
set { m_SubObjectName = value; }
}
internal virtual Type SubOjbectType
{
get
{
if (!string.IsNullOrEmpty(m_SubObjectName) && m_SubObjectType != null)
return Type.GetType(m_SubObjectType);
return null;
}
}
/// <summary>
/// Returns the state of the internal operation.
/// </summary>
/// <returns>True if the operation is valid.</returns>
public bool IsValid()
{
return m_Operation.IsValid();
}
/// <summary>
/// Get the loading status of the internal operation.
/// </summary>
public bool IsDone
{
get { return m_Operation.IsDone; }
}
#if UNITY_EDITOR
[InitializeOnLoadMethod]
static void RegisterForPlaymodeChange()
{
EditorApplication.playModeStateChanged -= EditorApplicationOnplayModeStateChanged;
EditorApplication.playModeStateChanged += EditorApplicationOnplayModeStateChanged;
}
static HashSet<AssetReference> m_ActiveAssetReferences = new HashSet<AssetReference>();
static void EditorApplicationOnplayModeStateChanged(PlayModeStateChange state)
{
if (EditorSettings.enterPlayModeOptionsEnabled && Addressables.reinitializeAddressables)
{
foreach (AssetReference reference in m_ActiveAssetReferences)
{
reference.ReleaseHandleWhenPlaymodeStateChanged(state);
}
}
}
void ReleaseHandleWhenPlaymodeStateChanged(PlayModeStateChange state)
{
if (m_Operation.IsValid())
m_Operation.Release();
}
#endif
/// <summary>
/// Construct a new AssetReference object.
/// </summary>
public AssetReference()
{
}
#if UNITY_EDITOR
~AssetReference()
{
m_ActiveAssetReferences.Remove(this);
}
#endif
/// <summary>
/// Construct a new AssetReference object.
/// </summary>
/// <param name="guid">The guid of the asset.</param>
public AssetReference(string guid)
{
m_AssetGUID = guid;
}
/// <summary>
/// The loaded asset. This value is only set after the AsyncOperationHandle returned from LoadAssetAsync completes.
/// It will not be set if only InstantiateAsync is called. It will be set to null if release is called.
/// </summary>
public virtual Object Asset
{
get
{
if (!m_Operation.IsValid())
return null;
return m_Operation.Result as Object;
}
}
#if UNITY_EDITOR
Object m_CachedAsset;
string m_CachedGUID = "";
/// <summary>
/// Cached Editor Asset.
/// </summary>
protected internal Object CachedAsset
{
get
{
if (m_CachedGUID != m_AssetGUID)
{
m_CachedAsset = null;
m_CachedGUID = "";
}
return m_CachedAsset;
}
set
{
m_CachedAsset = value;
m_CachedGUID = m_AssetGUID;
}
}
#endif
/// <summary>
/// String representation of asset reference.
/// </summary>
/// <returns>The asset guid as a string.</returns>
public override string ToString()
{
#if UNITY_EDITOR
return "[" + m_AssetGUID + "]" + CachedAsset;
#else
return "[" + m_AssetGUID + "]";
#endif
}
static AsyncOperationHandle<T> CreateFailedOperation<T>()
{
//this needs to be set in order for ResourceManager.ExceptionHandler to get hooked up to AddressablesImpl.LogException.
Addressables.InitializeAsync();
return Addressables.ResourceManager.CreateCompletedOperation(default(T), new Exception("Attempting to load an asset reference that has no asset assigned to it.").Message);
}
/// <summary>
/// Load the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use <see cref="Addressables.LoadAssetAsync{TObject}(object)"/> and pass your AssetReference in as the key.
///
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <typeparam name="TObject">The object type.</typeparam>
/// <returns>The load operation.</returns>
//[Obsolete("We have added Async to the name of all asynchronous methods (UnityUpgradable) -> LoadAssetAsync(*)", true)]
[Obsolete]
public AsyncOperationHandle<TObject> LoadAsset<TObject>()
{
return LoadAssetAsync<TObject>();
}
/// <summary>
/// Loads the reference as a scene.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use Addressables.LoadSceneAsync() and pass your AssetReference in as the key.
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <returns>The operation handle for the scene load.</returns>
//[Obsolete("We have added Async to the name of all asynchronous methods (UnityUpgradable) -> LoadSceneAsync(*)", true)]
[Obsolete]
public AsyncOperationHandle<SceneInstance> LoadScene()
{
return LoadSceneAsync();
}
/// <summary>
/// InstantiateAsync the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use Addressables.InstantiateAsync() and pass your AssetReference in as the key.
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <param name="position">Position of the instantiated object.</param>
/// <param name="rotation">Rotation of the instantiated object.</param>
/// <param name="parent">The parent of the instantiated object.</param>
/// <returns>Returns the instantiation operation.</returns>
//[Obsolete("We have added Async to the name of all asynchronous methods (UnityUpgradable) -> InstantiateAsync(*)", true)]
[Obsolete]
public AsyncOperationHandle<GameObject> Instantiate(Vector3 position, Quaternion rotation, Transform parent = null)
{
return InstantiateAsync(position, rotation, parent);
}
/// <summary>
/// InstantiateAsync the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use Addressables.InstantiateAsync() and pass your AssetReference in as the key.
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <param name="parent">The parent of the instantiated object.</param>
/// <param name="instantiateInWorldSpace">Option to retain world space when instantiated with a parent.</param>
/// <returns>Returns the instantiation operation.</returns>
//[Obsolete("We have added Async to the name of all asynchronous methods (UnityUpgradable) -> InstantiateAsync(*)", true)]
[Obsolete]
public AsyncOperationHandle<GameObject> Instantiate(Transform parent = null, bool instantiateInWorldSpace = false)
{
return InstantiateAsync(parent, instantiateInWorldSpace);
}
/// <summary>
/// Load the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use <see cref="Addressables.LoadAssetAsync{TObject}(object)"/> and pass your AssetReference in as the key.
///
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <typeparam name="TObject">The object type.</typeparam>
/// <returns>The load operation if there is not a valid cached operation, otherwise return default operation.</returns>
public virtual AsyncOperationHandle<TObject> LoadAssetAsync<TObject>()
{
AsyncOperationHandle<TObject> result = default(AsyncOperationHandle<TObject>);
if (m_Operation.IsValid())
Debug.LogError("Attempting to load AssetReference that has already been loaded. Handle is exposed through getter OperationHandle");
else
{
result = Addressables.LoadAssetAsync<TObject>(RuntimeKey);
OperationHandle = result;
}
return result;
}
/// <summary>
/// Loads the reference as a scene.
/// This cannot be used a second time until the first load is unloaded. If you wish to call load multiple times
/// on an AssetReference, use Addressables.LoadSceneAsync() and pass your AssetReference in as the key.
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <param name="loadMode">Scene load mode.</param>
/// <param name="activateOnLoad">If false, the scene will load but not activate (for background loading). The SceneInstance returned has an Activate() method that can be called to do this at a later point.</param>
/// <param name="priority">Async operation priority for scene loading.</param>
/// <returns>The operation handle for the request if there is not a valid cached operation, otherwise return default operation</returns>
public virtual AsyncOperationHandle<SceneInstance> LoadSceneAsync(LoadSceneMode loadMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100)
{
AsyncOperationHandle<SceneInstance> result = default(AsyncOperationHandle<SceneInstance>);
if (m_Operation.IsValid())
Debug.LogError("Attempting to load AssetReference Scene that has already been loaded. Handle is exposed through getter OperationHandle");
else
{
result = Addressables.LoadSceneAsync(RuntimeKey, loadMode, activateOnLoad, priority);
OperationHandle = result;
}
return result;
}
/// <summary>
/// Unloads the reference as a scene.
/// </summary>
/// <returns>The operation handle for the scene load.</returns>
public virtual AsyncOperationHandle<SceneInstance> UnLoadScene()
{
return Addressables.UnloadSceneAsync(m_Operation, true);
}
/// <summary>
/// InstantiateAsync the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use Addressables.InstantiateAsync() and pass your AssetReference in as the key.
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <param name="position">Position of the instantiated object.</param>
/// <param name="rotation">Rotation of the instantiated object.</param>
/// <param name="parent">The parent of the instantiated object.</param>
/// <returns></returns>
public virtual AsyncOperationHandle<GameObject> InstantiateAsync(Vector3 position, Quaternion rotation, Transform parent = null)
{
return Addressables.InstantiateAsync(RuntimeKey, position, rotation, parent, true);
}
/// <summary>
/// InstantiateAsync the referenced asset as type TObject.
/// This cannot be used a second time until the first load is released. If you wish to call load multiple times
/// on an AssetReference, use Addressables.InstantiateAsync() and pass your AssetReference in as the key.
/// See the [Loading Addressable Assets](xref:addressables-api-load-asset-async) documentation for more details.
/// </summary>
/// <param name="parent">The parent of the instantiated object.</param>
/// <param name="instantiateInWorldSpace">Option to retain world space when instantiated with a parent.</param>
/// <returns></returns>
public virtual AsyncOperationHandle<GameObject> InstantiateAsync(Transform parent = null, bool instantiateInWorldSpace = false)
{
return Addressables.InstantiateAsync(RuntimeKey, parent, instantiateInWorldSpace, true);
}
/// <inheritdoc/>
public virtual bool RuntimeKeyIsValid()
{
Guid result;
string guid = RuntimeKey.ToString();
int subObjectIndex = guid.IndexOf('[');
if (subObjectIndex != -1) //This means we're dealing with a sub-object and need to convert the runtime key.
guid = guid.Substring(0, subObjectIndex);
return Guid.TryParse(guid, out result);
}
/// <summary>
/// Release the internal operation handle.
/// </summary>
public virtual void ReleaseAsset()
{
if (!m_Operation.IsValid())
{
Debug.LogWarning("Cannot release a null or unloaded asset.");
return;
}
Addressables.Release(m_Operation);
m_Operation = default(AsyncOperationHandle);
}
/// <summary>
/// Release an instantiated object.
/// </summary>
/// <param name="obj">The object to release.</param>
public virtual void ReleaseInstance(GameObject obj)
{
Addressables.ReleaseInstance(obj);
}
/// <summary>
/// Validates that the referenced asset allowable for this asset reference.
/// </summary>
/// <param name="obj">The Object to validate.</param>
/// <returns>Whether the referenced asset is valid.</returns>
public virtual bool ValidateAsset(Object obj)
{
return true;
}
/// <summary>
/// Validates that the referenced asset allowable for this asset reference.
/// </summary>
/// <param name="path">The path to the asset in question.</param>
/// <returns>Whether the referenced asset is valid.</returns>
public virtual bool ValidateAsset(string path)
{
return true;
}
#if UNITY_EDITOR
[SerializeField]
#pragma warning disable CS0414
bool m_EditorAssetChanged;
#pragma warning restore CS0414
/// <summary>
/// Used by the editor to represent the main asset referenced.
/// </summary>
public virtual Object editorAsset
{
get { return GetEditorAssetInternal(); }
}
/// <summary>
/// Helper function that can be used to override the base class editorAsset accessor.
/// </summary>
/// <returns>Returns the main asset referenced used in the editor.</returns>
internal virtual Object GetEditorAssetInternal()
{
if (CachedAsset != null || string.IsNullOrEmpty(m_AssetGUID))
return CachedAsset;
var asset = FetchEditorAsset();
if (DerivedClassType == null)
return CachedAsset = asset;
if (asset == null)
Debug.LogWarning("Assigned editorAsset does not match type " + DerivedClassType + ". EditorAsset will be null.");
return CachedAsset = asset;
}
internal Object FetchEditorAsset()
{
var assetPath = AssetDatabase.GUIDToAssetPath(m_AssetGUID);
var asset = AssetDatabase.LoadAssetAtPath(assetPath, DerivedClassType ?? AssetDatabase.GetMainAssetTypeAtPath(assetPath));
return asset;
}
/// <summary>
/// Sets the main asset on the AssetReference. Only valid in the editor, this sets both the editorAsset attribute,
/// and the internal asset GUID, which drives the RuntimeKey attribute. If the reference uses a sub object,
/// then it will load the editor asset during edit mode and load the sub object during runtime. For example, if
/// the AssetReference is set to a sprite within a sprite atlas, the editorAsset is the atlas (loaded during edit mode)
/// and the sub object is the sprite (loaded during runtime). If called by AssetReferenceT, will set the editorAsset
/// to the requested object if the object is of type T, and null otherwise.
/// <param name="value">Object to reference</param>
/// </summary>
public virtual bool SetEditorAsset(Object value)
{
return SetEditorAssetInternal(value);
}
internal virtual bool SetEditorAssetInternal(Object value)
{
return OnSetEditorAsset(value, DerivedClassType);
}
internal bool OnSetEditorAsset(Object value, Type derivedType)
{
if (value == null)
{
CachedAsset = null;
m_AssetGUID = string.Empty;
m_SubObjectName = null;
m_EditorAssetChanged = true;
return true;
}
if (CachedAsset != value)
{
m_SubObjectName = null;
var path = AssetDatabase.GetAssetOrScenePath(value);
if (string.IsNullOrEmpty(path))
{
Addressables.LogWarningFormat("Invalid object for AssetReference {0}.", value);
return false;
}
if (!ValidateAsset(path))
{
Addressables.LogWarningFormat("Invalid asset for AssetReference path = '{0}'.", path);
return false;
}
else
{
m_AssetGUID = AssetDatabase.AssetPathToGUID(path);
Object mainAsset;
if (derivedType != null)
mainAsset = LocateEditorAssetForTypedAssetReference(value, path, derivedType);
else
{
mainAsset = AssetDatabase.LoadMainAssetAtPath(path);
if (value != mainAsset)
SetEditorSubObject(value);
}
CachedAsset = mainAsset;
}
}
m_EditorAssetChanged = true;
return true;
}
internal Object LocateEditorAssetForTypedAssetReference(Object value, string path, Type type)
{
Object mainAsset;
if (value.GetType() != type)
{
mainAsset = null;
}
else
{
mainAsset = AssetDatabase.LoadAssetAtPath(path, type);
if (mainAsset != value)
{
mainAsset = null;
var subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(path);
foreach (var asset in subAssets)
{
if (asset.GetType() == type && value == asset)
{
mainAsset = asset;
break;
}
}
}
}
if (mainAsset == null)
Debug.LogWarning("Assigned editorAsset does not match type " + type + ". EditorAsset will be null.");
return mainAsset;
}
/// <summary>
/// Sets the sub object for this asset reference.
/// </summary>
/// <param name="value">The sub object.</param>
/// <returns>True if set correctly.</returns>
public virtual bool SetEditorSubObject(Object value)
{
if (value == null)
{
m_SubObjectName = null;
m_SubObjectType = null;
m_EditorAssetChanged = true;
return true;
}
if (editorAsset == null)
return false;
if (editorAsset.GetType() == typeof(SpriteAtlas))
{
var spriteName = value.name;
if (spriteName.EndsWith("(Clone)", StringComparison.Ordinal))
spriteName = spriteName.Replace("(Clone)", "");
if ((editorAsset as SpriteAtlas).GetSprite(spriteName) == null)
{
Debug.LogWarningFormat("Unable to find sprite {0} in atlas {1}.", spriteName, editorAsset.name);
return false;
}
m_SubObjectName = spriteName;
m_SubObjectType = typeof(Sprite).AssemblyQualifiedName;
m_EditorAssetChanged = true;
return true;
}
var subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(AssetDatabase.GUIDToAssetPath(m_AssetGUID));
foreach (var s in subAssets)
{
if (s.name == value.name && s.GetType() == value.GetType())
{
m_SubObjectName = s.name;
m_SubObjectType = s.GetType().AssemblyQualifiedName;
m_EditorAssetChanged = true;
return true;
}
}
return false;
}
#endif
}
#if UNITY_EDITOR
class AssetPathToTypes : AssetPostprocessor
{
private static Dictionary<string, HashSet<Type>> s_PathToTypes = new Dictionary<string, HashSet<Type>>();
public static HashSet<Type> GetTypesForAssetPath(string path)
{
#if UNITY_2020_1_OR_NEWER
AssetDatabase.SaveAssetIfDirty(AssetDatabase.GUIDFromAssetPath(path));
#endif
if (s_PathToTypes.TryGetValue(path, out HashSet<Type> value))
return value;
var objectsForAsset = AssetDatabase.LoadAllAssetRepresentationsAtPath(path);
value = new HashSet<Type>();
foreach (Object o in objectsForAsset)
value.Add(o.GetType());
s_PathToTypes.Add(path, value);
return value;
}
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (string str in importedAssets)
s_PathToTypes.Remove(str);
foreach (string str in deletedAssets)
s_PathToTypes.Remove(str);
for (int i = 0; i < movedFromAssetPaths.Length; ++i)
{
if (s_PathToTypes.TryGetValue(movedFromAssetPaths[i], out var values))
{
s_PathToTypes.Remove(movedFromAssetPaths[i]);
s_PathToTypes.Add(movedAssets[i], values);
}
}
}
}
#endif
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 5ad0e35b994160844b457bd2836737b2
timeCreated: 1505840855
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,95 @@
using System;
using System.Text;
using Object = UnityEngine.Object;
namespace UnityEngine
{
/// <summary>
/// Used to restrict an AssetReference field or property to only allow items wil specific labels. This is only enforced through the UI.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
public class AssetReferenceUIRestriction : Attribute
{
/// <summary>
/// Validates that the referenced asset allowable for this asset reference.
/// </summary>
/// <param name="obj">The Object to validate.</param>
/// <returns>Whether the referenced asset is valid.</returns>
public virtual bool ValidateAsset(Object obj)
{
return true;
}
/// <summary>
/// Validates that the referenced asset allowable for this asset reference.
/// </summary>
/// <param name="path">The path to the asset in question.</param>
/// <returns>Whether the referenced asset is valid.</returns>
public virtual bool ValidateAsset(string path)
{
return true;
}
}
/// <summary>
/// Used to restrict an AssetReference field or property to only allow items wil specific labels. This is only enforced through the UI.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public sealed class AssetReferenceUILabelRestriction : AssetReferenceUIRestriction
{
/// <summary>
/// Stores the labels allowed for the AssetReference.
/// </summary>
public string[] m_AllowedLabels;
/// <summary>
/// Stores the allowed labels formatted as a string.
/// </summary>
public string m_CachedToString;
/// <summary>
/// Creates a new AssetReferenceUILabelRestriction object.
/// </summary>
/// <param name="allowedLabels">The labels allowed for the AssetReference.</param>
public AssetReferenceUILabelRestriction(params string[] allowedLabels)
{
m_AllowedLabels = allowedLabels;
}
/// <inheritdoc/>
public override bool ValidateAsset(Object obj)
{
return true;
}
/// <inheritdoc/>
public override bool ValidateAsset(string path)
{
return true;
}
/// <summary>
/// Converts the information about the allowed labels to a formatted string.
/// </summary>
/// <returns>Returns information about the allowed labels as a string.</returns>
public override string ToString()
{
if (m_CachedToString == null)
{
StringBuilder sb = new StringBuilder();
bool first = true;
foreach (var t in m_AllowedLabels)
{
if (!first)
sb.Append(',');
first = false;
sb.Append(t);
}
m_CachedToString = sb.ToString();
}
return m_CachedToString;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91c0d72bf78e7e042bef124e1c546c32
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.AddressableAssets
{
/// <summary>
/// Interface for providing a key. This allows for objects passed into the Addressables system to provied a key instead of being used directly.
/// </summary>
public interface IKeyEvaluator
{
/// <summary>
/// The runtime key to use.
/// </summary>
object RuntimeKey { get; }
/// <summary>
/// Checks if the current RuntimeKey is valid.
/// </summary>
/// <returns>Whether the RuntimeKey is valid or not.</returns>
bool RuntimeKeyIsValid();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f3f18a7841f7dca47bee246dcd538b31
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b571e819498e94243aaab6be45486255
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace UnityEngine.AddressableAssets.Initialization
{
/// <summary>
/// Supports the evaluation of embedded runtime variables in addressables locations
/// </summary>
public static class AddressablesRuntimeProperties
{
// cache these to avoid GC allocations
static Stack<string> s_TokenStack = new Stack<string>(32);
static Stack<int> s_TokenStartStack = new Stack<int>(32);
static bool s_StaticStacksAreInUse = false;
#if !UNITY_EDITOR && UNITY_WSA_10_0 && ENABLE_DOTNET
static Assembly[] GetAssemblies()
{
//Not supported on UWP platforms
return new Assembly[0];
}
#else
static Assembly[] GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
#endif
static Dictionary<string, string> s_CachedValues = new Dictionary<string, string>();
internal static int GetCachedValueCount()
{
return s_CachedValues.Count;
}
/// <summary>
/// Predefine a runtime property.
/// </summary>
/// <param name="name">The property name.</param>
/// <param name="val">The property value.</param>
public static void SetPropertyValue(string name, string val)
{
s_CachedValues[name] = val;
}
/// <summary>
/// This will clear all PropertyValues that have been cached. This includes all values set by
/// <see cref="SetPropertyValue"/> as well as any reflection-evaluated properties.
/// </summary>
public static void ClearCachedPropertyValues()
{
s_CachedValues.Clear();
}
/// <summary>
/// Evaluates a named property using cached values and static public fields and properties. Be aware that a field or property may be stripped if not referenced anywhere else.
/// </summary>
/// <param name="name">The property name.</param>
/// <returns>The value of the property. If not found, the name is returned.</returns>
public static string EvaluateProperty(string name)
{
Debug.Assert(s_CachedValues != null, "ResourceManagerConfig.GetGlobalVar - s_cachedValues == null.");
if (string.IsNullOrEmpty(name))
return string.Empty;
string cachedValue;
if (s_CachedValues.TryGetValue(name, out cachedValue))
return cachedValue;
int i = name.LastIndexOf('.');
if (i < 0)
return name;
var className = name.Substring(0, i);
var propName = name.Substring(i + 1);
foreach (var a in GetAssemblies())
{
Type t = a.GetType(className, false, false);
if (t == null)
continue;
try
{
var pi = t.GetProperty(propName, BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public);
if (pi != null)
{
var v = pi.GetValue(null, null);
if (v != null)
{
s_CachedValues.Add(name, v.ToString());
return v.ToString();
}
}
var fi = t.GetField(propName, BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public);
if (fi != null)
{
var v = fi.GetValue(null);
if (v != null)
{
s_CachedValues.Add(name, v.ToString());
return v.ToString();
}
}
}
catch (Exception)
{
// ignored
}
}
return name;
}
/// <summary>
/// Evaluates all tokens deliminated by '{' and '}' in a string and evaluates them with the EvaluateProperty method.
/// </summary>
/// <param name="input">The input string.</param>
/// <returns>The evaluated string after resolving all tokens.</returns>
public static string EvaluateString(string input)
{
return EvaluateString(input, '{', '}', EvaluateProperty);
}
/// <summary>
/// Evaluates all tokens deliminated by the specified delimiters in a string and evaluates them with the supplied method.
/// </summary>
/// <param name="inputString">The string to evaluate.</param>
/// <param name="startDelimiter">The start token delimiter.</param>
/// <param name="endDelimiter">The end token delimiter.</param>
/// <param name="varFunc">Func that has a single string parameter and returns a string.</param>
/// <returns>The evaluated string.</returns>
public static string EvaluateString(string inputString, char startDelimiter, char endDelimiter, Func<string, string> varFunc)
{
if (string.IsNullOrEmpty(inputString))
return string.Empty;
string originalString = inputString;
Stack<string> tokenStack;
Stack<int> tokenStartStack;
if (!s_StaticStacksAreInUse)
{
tokenStack = s_TokenStack;
tokenStartStack = s_TokenStartStack;
s_StaticStacksAreInUse = true;
}
else
{
tokenStack = new Stack<string>(32);
tokenStartStack = new Stack<int>(32);
}
tokenStack.Push(inputString);
int popTokenAt = inputString.Length;
char[] delimiters = {startDelimiter, endDelimiter};
bool delimitersMatch = startDelimiter == endDelimiter;
int i = inputString.IndexOf(startDelimiter);
int prevIndex = -2;
while (i >= 0)
{
char c = inputString[i];
if (c == startDelimiter && (!delimitersMatch || tokenStartStack.Count == 0))
{
tokenStartStack.Push(i);
i++;
}
else if (c == endDelimiter && tokenStartStack.Count > 0)
{
int start = tokenStartStack.Peek();
string token = inputString.Substring(start + 1, i - start - 1);
string tokenVal;
if (popTokenAt <= i)
{
tokenStack.Pop();
}
// check if the token is already included
if (tokenStack.Contains(token))
tokenVal = "#ERROR-CyclicToken#";
else
{
tokenVal = varFunc == null ? string.Empty : varFunc(token);
tokenStack.Push(token);
}
i = tokenStartStack.Pop();
popTokenAt = i + tokenVal.Length + 1;
if (i > 0)
{
int rhsStartIndex = i + token.Length + 2;
if (rhsStartIndex == inputString.Length)
inputString = inputString.Substring(0, i) + tokenVal;
else
inputString = inputString.Substring(0, i) + tokenVal + inputString.Substring(rhsStartIndex);
}
else
inputString = tokenVal + inputString.Substring(i + token.Length + 2);
}
bool infiniteLoopDetected = prevIndex == i;
if (infiniteLoopDetected)
return "#ERROR-" + originalString + " contains unmatched delimiters#";
prevIndex = i;
i = inputString.IndexOfAny(delimiters, i);
}
tokenStack.Clear();
tokenStartStack.Clear();
if (ReferenceEquals(tokenStack, s_TokenStack))
s_StaticStacksAreInUse = false;
return inputString;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2dd2619d56bf0674e930cbfd1f598197
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,200 @@
using System;
using System.Collections;
using System.IO;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.Serialization;
namespace UnityEngine.AddressableAssets.Initialization
{
/// <summary>
/// IInitializableObject that sets up the Caching system.
/// </summary>
[Serializable]
public class CacheInitialization : IInitializableObject
{
/// <summary>
/// Sets properties of the Caching system.
/// </summary>
/// <param name="id">The id of thei object.</param>
/// <param name="dataStr">The JSON serialized CacheInitializationData object.</param>
/// <returns>True if the initialization succeeded.</returns>
public bool Initialize(string id, string dataStr)
{
#if ENABLE_CACHING
var data = JsonUtility.FromJson<CacheInitializationData>(dataStr);
if (data != null)
{
Caching.compressionEnabled = data.CompressionEnabled;
var activeCache = Caching.currentCacheForWriting;
if (!string.IsNullOrEmpty(data.CacheDirectoryOverride))
{
var dir = Addressables.ResolveInternalId(data.CacheDirectoryOverride);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
activeCache = Caching.GetCacheByPath(dir);
if (!activeCache.valid)
activeCache = Caching.AddCache(dir);
Caching.currentCacheForWriting = activeCache;
}
if (data.LimitCacheSize)
activeCache.maximumAvailableStorageSpace = data.MaximumCacheSize;
else
activeCache.maximumAvailableStorageSpace = long.MaxValue;
#pragma warning disable 618
activeCache.expirationDelay = data.ExpirationDelay;
#pragma warning restore 618
}
#endif //ENABLE_CACHING
return true;
}
/// <inheritdoc/>
public virtual AsyncOperationHandle<bool> InitializeAsync(ResourceManager rm, string id, string data)
{
CacheInitOp op = new CacheInitOp();
op.Init(() => { return Initialize(id, data); });
return rm.StartOperation(op, default);
}
#if ENABLE_CACHING
/// <summary>
/// The root path of the cache.
/// </summary>
public static string RootPath
{
get { return Path.GetDirectoryName(Caching.defaultCache.path); }
}
#endif //ENABLE_CACHING
class CacheInitOp : AsyncOperationBase<bool>, IUpdateReceiver
{
private Func<bool> m_Callback;
#if ENABLE_CACHING
private bool m_UpdateRequired = true;
#endif //ENABLE_CACHING
public void Init(Func<bool> callback)
{
m_Callback = callback;
}
/// <inheritdoc />
protected override bool InvokeWaitForCompletion()
{
#if ENABLE_CACHING
m_RM?.Update(Time.unscaledDeltaTime);
if (!IsDone)
InvokeExecute();
return IsDone;
#else
return true;
#endif
}
public void Update(float unscaledDeltaTime)
{
#if ENABLE_CACHING
if (Caching.ready && m_UpdateRequired)
{
m_UpdateRequired = false;
if (m_Callback != null)
Complete(m_Callback(), true, "");
else
Complete(true, true, "");
Addressables.Log("CacheInitialization Complete");
}
#else
Complete(true, true, "");
Addressables.Log("UnityEngine.Caching not supported on this platform but a CacheInitialization object has been found in AddressableAssetSettings. No action has been taken.");
#endif
}
protected override void Execute()
{
((IUpdateReceiver)this).Update(0.0f);
}
}
}
/// <summary>
/// Contains settings for the Caching system.
/// </summary>
[Serializable]
public class CacheInitializationData
{
[FormerlySerializedAs("m_compressionEnabled")]
[SerializeField]
bool m_CompressionEnabled = true;
/// <summary>
/// Enable recompression of asset bundles into LZ4 format as they are saved to the cache. This sets the Caching.compressionEnabled value.
/// </summary>
public bool CompressionEnabled
{
get { return m_CompressionEnabled; }
set { m_CompressionEnabled = value; }
}
[FormerlySerializedAs("m_cacheDirectoryOverride")]
[SerializeField]
string m_CacheDirectoryOverride = "";
/// <summary>
/// If not null or empty a new cache is created using Caching.AddCache and it is set active by assigning it to Caching.currentCacheForWriting.
/// </summary>
public string CacheDirectoryOverride
{
get { return m_CacheDirectoryOverride; }
set { m_CacheDirectoryOverride = value; }
}
[FormerlySerializedAs("m_expirationDelay")]
[SerializeField]
int m_ExpirationDelay = 12960000; //this value taken from the docs and is 150 days
/// <summary>
/// Controls how long bundles are kept in the cache. This value is applied to Caching.currentCacheForWriting.expirationDelay. The value is in seconds and has a limit of 12960000 (150 days).
/// </summary>
[Obsolete("Functionality remains unchanged. However, due to issues with Caching this property is being marked obsolete. See Caching API documentation for more details.")]
public int ExpirationDelay
{
get { return m_ExpirationDelay; }
set { m_ExpirationDelay = value; }
}
[FormerlySerializedAs("m_limitCacheSize")]
[SerializeField]
bool m_LimitCacheSize;
/// <summary>
/// If true, the maximum cache size will be set to MaximumCacheSize.
/// </summary>
public bool LimitCacheSize
{
get { return m_LimitCacheSize; }
set { m_LimitCacheSize = value; }
}
[FormerlySerializedAs("m_maximumCacheSize")]
[SerializeField]
long m_MaximumCacheSize = long.MaxValue;
/// <summary>
/// The maximum size of the cache in bytes. This value is applied to Caching.currentCacheForWriting.maximumAvailableStorageSpace. This will only be set if LimitCacheSize is true.
/// </summary>
public long MaximumCacheSize
{
get { return m_MaximumCacheSize; }
set { m_MaximumCacheSize = value; }
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43224c404bd343b44a2ecd99237e285c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,123 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine.AddressableAssets.ResourceProviders;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace UnityEngine.AddressableAssets
{
class CheckCatalogsOperation : AsyncOperationBase<List<string>>
{
AddressablesImpl m_Addressables;
List<string> m_LocalHashes;
List<ResourceLocatorInfo> m_LocatorInfos;
AsyncOperationHandle<IList<AsyncOperationHandle>> m_DepOp;
public CheckCatalogsOperation(AddressablesImpl aa)
{
m_Addressables = aa;
}
public AsyncOperationHandle<List<string>> Start(List<ResourceLocatorInfo> locatorInfos)
{
m_LocatorInfos = new List<ResourceLocatorInfo>(locatorInfos.Count);
m_LocalHashes = new List<string>(locatorInfos.Count);
var locations = new List<IResourceLocation>(locatorInfos.Count);
foreach (var rl in locatorInfos)
{
if (rl.CanUpdateContent)
{
locations.Add(rl.HashLocation);
m_LocalHashes.Add(rl.LocalHash);
m_LocatorInfos.Add(rl);
}
}
ContentCatalogProvider ccp = m_Addressables.ResourceManager.ResourceProviders
.FirstOrDefault(rp => rp.GetType() == typeof(ContentCatalogProvider)) as ContentCatalogProvider;
if (ccp != null)
ccp.DisableCatalogUpdateOnStart = false;
m_DepOp = m_Addressables.ResourceManager.CreateGroupOperation<string>(locations);
return m_Addressables.ResourceManager.StartOperation(this, m_DepOp);
}
/// <inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (IsDone)
return true;
if (m_DepOp.IsValid() && !m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
m_RM?.Update(Time.unscaledDeltaTime);
return IsDone;
}
protected override void Destroy()
{
m_Addressables.Release(m_DepOp);
}
/// <inheritdoc />
public override void GetDependencies(List<AsyncOperationHandle> dependencies)
{
dependencies.Add(m_DepOp);
}
internal static List<string> ProcessDependentOpResults(IList<AsyncOperationHandle> results,
List<ResourceLocatorInfo> locatorInfos, List<string> localHashes, out string errorString, out bool success)
{
var result = new List<string>();
List<string> errorMsgList = new List<string>();
for (int i = 0; i < results.Count; i++)
{
var remHashOp = results[i];
var remoteHash = remHashOp.Result as string;
if (!string.IsNullOrEmpty(remoteHash) && remoteHash != localHashes[i])
{
result.Add(locatorInfos[i].Locator.LocatorId);
locatorInfos[i].ContentUpdateAvailable = true;
}
else if (remHashOp.OperationException != null)
{
result.Add(null);
locatorInfos[i].ContentUpdateAvailable = false;
errorMsgList.Add(remHashOp.OperationException.Message);
}
}
errorString = null;
if (errorMsgList.Count > 0)
{
if (errorMsgList.Count == result.Count)
{
result = null;
errorString = "CheckCatalogsOperation failed with the following errors: ";
}
else
{
errorString = "Partial success in CheckCatalogsOperation with the following errors: ";
}
foreach (string str in errorMsgList)
errorString = errorString + "\n" + str;
}
success = errorMsgList.Count == 0;
return result;
}
protected override void Execute()
{
var result = ProcessDependentOpResults(m_DepOp.Result, m_LocatorInfos, m_LocalHashes, out string errorString, out bool success);
Complete(result, success, errorString);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8ad3aa5a0429b934a8ae72ceddf3b2bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,207 @@
#if ENABLE_CACHING
using System.Collections.Generic;
using System.IO;
using System.Threading;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.Assertions;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
using static UnityEngine.ResourceManagement.ResourceProviders.AssetBundleResource;
namespace UnityEngine.AddressableAssets
{
class CleanBundleCacheOperation : AsyncOperationBase<bool>, IUpdateReceiver
{
AddressablesImpl m_Addressables;
AsyncOperationHandle<IList<AsyncOperationHandle>> m_DepOp;
List<string> m_CacheDirsForRemoval;
Thread m_EnumerationThread;
string m_BaseCachePath;
bool m_UseMultiThreading;
public CleanBundleCacheOperation(AddressablesImpl aa, bool forceSingleThreading)
{
m_Addressables = aa;
m_UseMultiThreading = forceSingleThreading ? false : PlatformUtilities.PlatformUsesMultiThreading(Application.platform);
}
public AsyncOperationHandle<bool> Start(AsyncOperationHandle<IList<AsyncOperationHandle>> depOp)
{
m_DepOp = depOp.Acquire();
return m_Addressables.ResourceManager.StartOperation(this, m_DepOp);
}
public void CompleteInternal(bool result, bool success, string errorMsg)
{
m_DepOp.Release();
Complete(result, success, errorMsg);
}
/// <inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (!m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
if (!HasExecuted)
InvokeExecute();
if (m_EnumerationThread != null)
{
m_EnumerationThread.Join();
RemoveCacheEntries();
}
return IsDone;
}
protected override void Destroy()
{
if (m_DepOp.IsValid())
m_DepOp.Release();
}
public override void GetDependencies(List<AsyncOperationHandle> dependencies)
{
dependencies.Add(m_DepOp);
}
protected override void Execute()
{
Assert.AreEqual(null, m_EnumerationThread, "CleanBundleCacheOperation has already executed. A worker thread has already been created.");
if (m_DepOp.Status == AsyncOperationStatus.Failed)
CompleteInternal(false, false, "Could not clean cache because a dependent catalog operation failed.");
else
{
HashSet<string> cacheDirsInUse = GetCacheDirsInUse(m_DepOp.Result);
if (!Caching.ready)
CompleteInternal(false, false, "Cache is not ready to be accessed.");
m_BaseCachePath = Caching.currentCacheForWriting.path;
if (m_UseMultiThreading)
{
m_EnumerationThread = new Thread(DetermineCacheDirsNotInUse);
m_EnumerationThread.Start(cacheDirsInUse);
}
else
{
DetermineCacheDirsNotInUse(cacheDirsInUse);
RemoveCacheEntries();
}
}
}
void IUpdateReceiver.Update(float unscaledDeltaTime)
{
if (m_UseMultiThreading && !m_EnumerationThread.IsAlive)
{
m_EnumerationThread = null;
RemoveCacheEntries();
}
}
void RemoveCacheEntries()
{
foreach (string cacheDir in m_CacheDirsForRemoval)
{
string bundlename = Path.GetFileName(cacheDir);
Caching.ClearAllCachedVersions(bundlename);
}
CompleteInternal(true, true, null);
}
void DetermineCacheDirsNotInUse(object data)
{
DetermineCacheDirsNotInUse((HashSet<string>)data);
}
void DetermineCacheDirsNotInUse(HashSet<string> cacheDirsInUse)
{
m_CacheDirsForRemoval = new List<string>();
foreach (var cacheDir in Directory.EnumerateDirectories(m_BaseCachePath, "*", SearchOption.TopDirectoryOnly))
{
if (!cacheDirsInUse.Contains(cacheDir))
m_CacheDirsForRemoval.Add(cacheDir);
}
}
#if ENABLE_BINARY_CATALOG
HashSet<string> GetCacheDirsInUse(IList<AsyncOperationHandle> catalogOps)
{
var cacheDirsInUse = new HashSet<string>();
for (int i = 0; i < catalogOps.Count; i++)
{
var locator = catalogOps[i].Result as IResourceLocator;
if (locator == null)
{
var catData = catalogOps[i].Result as ContentCatalogData;
if (catData == null)
return cacheDirsInUse;
locator = catData.CreateCustomLocator(catData.location.PrimaryKey);
}
foreach (var location in locator.AllLocations)
{
if (location.Data is AssetBundleRequestOptions options)
{
GetLoadInfo(location, m_Addressables.ResourceManager, out LoadType loadType, out string path);
if (loadType == LoadType.Web)
{
string cacheDir = Path.Combine(Caching.currentCacheForWriting.path, options.BundleName); // Cache entries are named in this format "baseCachePath/bundleName/hash"
cacheDirsInUse.Add(cacheDir);
}
}
}
}
return cacheDirsInUse;
}
#else
HashSet<string> GetCacheDirsInUse(IList<AsyncOperationHandle> catalogOps)
{
var cacheDirsInUse = new HashSet<string>();
for (int i = 0; i < catalogOps.Count; i++)
{
var locator = catalogOps[i].Result as ResourceLocationMap;
if (locator == null)
{
var catData = catalogOps[i].Result as ContentCatalogData;
if (catData == null)
return cacheDirsInUse;
locator = catData.CreateCustomLocator(catData.location.PrimaryKey);
}
foreach (IList<IResourceLocation> locationList in locator.Locations.Values)
{
foreach (IResourceLocation location in locationList)
{
if (location.Data is AssetBundleRequestOptions options)
{
GetLoadInfo(location, m_Addressables.ResourceManager, out LoadType loadType, out string path);
if (loadType == LoadType.Web)
{
string cacheDir = Path.Combine(Caching.currentCacheForWriting.path, options.BundleName); // Cache entries are named in this format "baseCachePath/bundleName/hash"
cacheDirsInUse.Add(cacheDir);
}
}
}
}
}
return cacheDirsInUse;
}
#endif
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f94d20840f3378647ba2c0da3686e517
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,126 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.Initialization;
namespace UnityEngine.ResourceManagement.AsyncOperations
{
internal class InitalizationObjectsOperation : AsyncOperationBase<bool>
{
private AsyncOperationHandle<ResourceManagerRuntimeData> m_RtdOp;
private AddressablesImpl m_Addressables;
private AsyncOperationHandle<IList<AsyncOperationHandle>> m_DepOp;
public void Init(AsyncOperationHandle<ResourceManagerRuntimeData> rtdOp, AddressablesImpl addressables)
{
m_RtdOp = rtdOp;
m_Addressables = addressables;
m_Addressables.ResourceManager.RegisterForCallbacks();
}
protected override string DebugName
{
get { return "InitializationObjectsOperation"; }
}
internal bool LogRuntimeWarnings(string pathToBuildLogs)
{
if (!File.Exists(pathToBuildLogs))
return false;
PackedPlayModeBuildLogs runtimeBuildLogs = JsonUtility.FromJson<PackedPlayModeBuildLogs>(File.ReadAllText(pathToBuildLogs));
bool messageLogged = false;
foreach (var log in runtimeBuildLogs.RuntimeBuildLogs)
{
messageLogged = true;
switch (log.Type)
{
case LogType.Warning:
Addressables.LogWarning(log.Message);
break;
case LogType.Error:
Addressables.LogError(log.Message);
break;
case LogType.Log:
Addressables.Log(log.Message);
break;
}
}
return messageLogged;
}
/// <inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (IsDone)
return true;
if (m_RtdOp.IsValid() && !m_RtdOp.IsDone)
m_RtdOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
if (m_DepOp.IsValid() && !m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
return IsDone;
}
protected override void Execute()
{
var rtd = m_RtdOp.Result;
if (rtd == null)
{
Addressables.LogError("RuntimeData is null. Please ensure you have built the correct Player Content.");
Complete(true, true, "");
return;
}
string buildLogsPath = m_Addressables.ResolveInternalId(PlayerPrefs.GetString(Addressables.kAddressablesRuntimeBuildLogPath));
if (LogRuntimeWarnings(buildLogsPath))
File.Delete(buildLogsPath);
List<AsyncOperationHandle> initOperations = new List<AsyncOperationHandle>();
foreach (var i in rtd.InitializationObjects)
{
if (i.ObjectType.Value == null)
{
Addressables.LogFormat("Invalid initialization object type {0}.", i.ObjectType);
continue;
}
try
{
var o = i.GetAsyncInitHandle(m_Addressables.ResourceManager);
initOperations.Add(o);
Addressables.LogFormat("Initialization object {0} created instance {1}.", i, o);
}
catch (Exception ex)
{
Addressables.LogErrorFormat("Exception thrown during initialization of object {0}: {1}", i,
ex.ToString());
}
}
if (initOperations.Count > 0)
{
m_DepOp = m_Addressables.ResourceManager.CreateGenericGroupOperation(initOperations, true);
m_DepOp.Completed += (obj) =>
{
bool success = obj.Status == AsyncOperationStatus.Succeeded;
Complete(true, success, success ? "" : $"{obj.DebugName}, status={obj.Status}, result={obj.Result} failed initialization.");
m_Addressables.Release(m_DepOp);
};
}
else
{
Complete(true, true, "");
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1095566ae23dbab4985b87dc92390bc2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,331 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.AddressableAssets.ResourceProviders;
using UnityEngine.AddressableAssets.Utility;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
using ResourceManager = UnityEngine.ResourceManagement.ResourceManager;
namespace UnityEngine.AddressableAssets.Initialization
{
internal class InitializationOperation : AsyncOperationBase<IResourceLocator>
{
AsyncOperationHandle<ResourceManagerRuntimeData> m_rtdOp;
AsyncOperationHandle<IResourceLocator> m_loadCatalogOp;
string m_ProviderSuffix;
AddressablesImpl m_Addressables;
ResourceManagerDiagnostics m_Diagnostics;
InitalizationObjectsOperation m_InitGroupOps;
public InitializationOperation(AddressablesImpl aa)
{
m_Addressables = aa;
m_Diagnostics = new ResourceManagerDiagnostics(aa.ResourceManager);
}
protected override float Progress
{
get
{
if (m_rtdOp.IsValid())
return m_rtdOp.PercentComplete;
return 0f;
}
}
protected override string DebugName
{
get { return "InitializationOperation"; }
}
internal static AsyncOperationHandle<IResourceLocator> CreateInitializationOperation(AddressablesImpl aa, string playerSettingsLocation, string providerSuffix)
{
var jp = new JsonAssetProvider();
aa.ResourceManager.ResourceProviders.Add(jp);
var tdp = new TextDataProvider();
aa.ResourceManager.ResourceProviders.Add(tdp);
aa.ResourceManager.ResourceProviders.Add(new ContentCatalogProvider(aa.ResourceManager));
var runtimeDataLocation = new ResourceLocationBase("RuntimeData", playerSettingsLocation, typeof(JsonAssetProvider).FullName, typeof(ResourceManagerRuntimeData));
var initOp = new InitializationOperation(aa);
initOp.m_rtdOp = aa.ResourceManager.ProvideResource<ResourceManagerRuntimeData>(runtimeDataLocation);
initOp.m_ProviderSuffix = providerSuffix;
initOp.m_InitGroupOps = new InitalizationObjectsOperation();
initOp.m_InitGroupOps.Init(initOp.m_rtdOp, aa);
var groupOpHandle = aa.ResourceManager.StartOperation(initOp.m_InitGroupOps, initOp.m_rtdOp);
return aa.ResourceManager.StartOperation<IResourceLocator>(initOp, groupOpHandle);
}
/// <inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (IsDone)
return true;
if (m_rtdOp.IsValid() && !m_rtdOp.IsDone)
m_rtdOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
if (m_loadCatalogOp.IsValid() && !m_loadCatalogOp.IsDone)
{
m_loadCatalogOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime); //We need completion callbacks to get triggered.
}
return m_rtdOp.IsDone && m_loadCatalogOp.IsDone;
}
protected override void Execute()
{
Addressables.LogFormat("Addressables - runtime data operation completed with status = {0}, result = {1}.", m_rtdOp.Status, m_rtdOp.Result);
if (m_rtdOp.Result == null)
{
Addressables.LogWarningFormat("Addressables - Unable to load runtime data at location {0}.", m_rtdOp);
Complete(Result, false, string.Format("Addressables - Unable to load runtime data at location {0}.", m_rtdOp));
return;
}
Addressables.LogFormat("Initializing Addressables version {0}.", m_rtdOp.Result.AddressablesVersion);
var rtd = m_rtdOp.Result;
#if ENABLE_CCD
Addressables.LogFormat("Initializing CcdManager");
if (!CcdManager.IsConfigured())
{
CcdManager.EnvironmentName = rtd.CcdManagedData.EnvironmentName;
CcdManager.BucketId = rtd.CcdManagedData.BucketId;
CcdManager.Badge = rtd.CcdManagedData.Badge;
}
#endif
m_Addressables.ResourceManager.postProfilerEvents = rtd.ProfileEvents;
WebRequestQueue.SetMaxConcurrentRequests(rtd.MaxConcurrentWebRequests);
m_Addressables.CatalogRequestsTimeout = rtd.CatalogRequestsTimeout;
foreach (var catalogLocation in rtd.CatalogLocations)
{
if (catalogLocation.Data != null && catalogLocation.Data is ProviderLoadRequestOptions loadData)
{
loadData.WebRequestTimeout = rtd.CatalogRequestsTimeout;
}
}
m_Addressables.Release(m_rtdOp);
if (rtd.CertificateHandlerType != null)
m_Addressables.ResourceManager.CertificateHandlerInstance = Activator.CreateInstance(rtd.CertificateHandlerType) as CertificateHandler;
#if UNITY_EDITOR
if (UnityEditor.EditorUserBuildSettings.activeBuildTarget.ToString() != rtd.BuildTarget)
Addressables.LogErrorFormat(
"Addressables - runtime data was built with a different build target. Expected {0}, but data was built with {1}. Certain assets may not load correctly including shaders. You can rebuild player content via the Addressables window.",
UnityEditor.EditorUserBuildSettings.activeBuildTarget, rtd.BuildTarget);
#endif
if (!rtd.LogResourceManagerExceptions)
ResourceManager.ExceptionHandler = null;
if (!rtd.ProfileEvents)
{
m_Diagnostics.Dispose();
m_Diagnostics = null;
m_Addressables.ResourceManager.ClearDiagnosticCallbacks();
}
Addressables.Log("Addressables - loading initialization objects.");
ContentCatalogProvider ccp = m_Addressables.ResourceManager.ResourceProviders
.FirstOrDefault(rp => rp.GetType() == typeof(ContentCatalogProvider)) as ContentCatalogProvider;
if (ccp != null)
{
ccp.DisableCatalogUpdateOnStart = rtd.DisableCatalogUpdateOnStartup;
ccp.IsLocalCatalogInBundle = rtd.IsLocalCatalogInBundle;
}
var locMap = new ResourceLocationMap("CatalogLocator", rtd.CatalogLocations);
m_Addressables.AddResourceLocator(locMap);
IList<IResourceLocation> catalogs;
if (!locMap.Locate(ResourceManagerRuntimeData.kCatalogAddress, typeof(ContentCatalogData), out catalogs))
{
Addressables.LogWarningFormat(
"Addressables - Unable to find any catalog locations in the runtime data.");
m_Addressables.RemoveResourceLocator(locMap);
Complete(Result, false, "Addressables - Unable to find any catalog locations in the runtime data.");
}
else
{
Addressables.LogFormat("Addressables - loading content catalogs, {0} found.", catalogs.Count);
IResourceLocation remoteHashLocation = null;
if (catalogs[0].Dependencies.Count == 2 && rtd.DisableCatalogUpdateOnStartup)
{
remoteHashLocation = catalogs[0].Dependencies[(int)ContentCatalogProvider.DependencyHashIndex.Remote];
catalogs[0].Dependencies[(int)ContentCatalogProvider.DependencyHashIndex.Remote] = catalogs[0].Dependencies[(int)ContentCatalogProvider.DependencyHashIndex.Cache];
}
m_loadCatalogOp = LoadContentCatalogInternal(catalogs, 0, locMap, remoteHashLocation);
}
}
static void LoadProvider(AddressablesImpl addressables, ObjectInitializationData providerData, string providerSuffix)
{
//don't add providers that have the same id...
var indexOfExistingProvider = -1;
var newProviderId = string.IsNullOrEmpty(providerSuffix) ? providerData.Id : (providerData.Id + providerSuffix);
for (int i = 0; i < addressables.ResourceManager.ResourceProviders.Count; i++)
{
var rp = addressables.ResourceManager.ResourceProviders[i];
if (rp.ProviderId == newProviderId)
{
indexOfExistingProvider = i;
break;
}
}
//if not re-initializing, just use the old provider
if (indexOfExistingProvider >= 0 && string.IsNullOrEmpty(providerSuffix))
return;
var provider = providerData.CreateInstance<IResourceProvider>(newProviderId);
if (provider != null)
{
if (indexOfExistingProvider < 0 || !string.IsNullOrEmpty(providerSuffix))
{
Addressables.LogFormat("Addressables - added provider {0} with id {1}.", provider, provider.ProviderId);
addressables.ResourceManager.ResourceProviders.Add(provider);
}
else
{
Addressables.LogFormat("Addressables - replacing provider {0} at index {1}.", provider, indexOfExistingProvider);
addressables.ResourceManager.ResourceProviders[indexOfExistingProvider] = provider;
}
}
else
{
Addressables.LogWarningFormat("Addressables - Unable to load resource provider from {0}.", providerData);
}
}
static AsyncOperationHandle<IResourceLocator> OnCatalogDataLoaded(AddressablesImpl addressables, AsyncOperationHandle<ContentCatalogData> op, string providerSuffix,
IResourceLocation remoteHashLocation)
{
var data = op.Result;
addressables.Release(op);
if (data == null)
{
var opException = op.OperationException != null ? new Exception("Failed to load content catalog.", op.OperationException) : new Exception("Failed to load content catalog.");
return addressables.ResourceManager.CreateCompletedOperationWithException<IResourceLocator>(null, opException);
}
else
{
if (data.ResourceProviderData != null)
foreach (var providerData in data.ResourceProviderData)
LoadProvider(addressables, providerData, providerSuffix);
if (addressables.InstanceProvider == null)
{
var prov = data.InstanceProviderData.CreateInstance<IInstanceProvider>();
if (prov != null)
addressables.InstanceProvider = prov;
}
if (addressables.SceneProvider == null)
{
var prov = data.SceneProviderData.CreateInstance<ISceneProvider>();
if (prov != null)
addressables.SceneProvider = prov;
}
if (remoteHashLocation != null)
data.location.Dependencies[(int)ContentCatalogProvider.DependencyHashIndex.Remote] = remoteHashLocation;
IResourceLocator locMap = data.CreateCustomLocator(data.location.PrimaryKey, providerSuffix);
addressables.AddResourceLocator(locMap, data.localHash, data.location);
addressables.AddResourceLocator(new DynamicResourceLocator(addressables));
return addressables.ResourceManager.CreateCompletedOperation<IResourceLocator>(locMap, string.Empty);
}
}
public static AsyncOperationHandle<IResourceLocator> LoadContentCatalog(AddressablesImpl addressables, IResourceLocation loc, string providerSuffix,
IResourceLocation remoteHashLocation = null)
{
Type provType = typeof(ProviderOperation<ContentCatalogData>);
var catalogOp = addressables.ResourceManager.CreateOperation<ProviderOperation<ContentCatalogData>>(provType, provType.GetHashCode(), null, null);
IResourceProvider catalogProvider = null;
foreach (IResourceProvider provider in addressables.ResourceManager.ResourceProviders)
{
if (provider is ContentCatalogProvider)
{
catalogProvider = provider;
break;
}
}
var dependencies = addressables.ResourceManager.CreateGroupOperation<string>(loc.Dependencies, true);
catalogOp.Init(addressables.ResourceManager, catalogProvider, loc, dependencies, true);
var catalogHandle = addressables.ResourceManager.StartOperation(catalogOp, dependencies);
dependencies.Release();
var chainOp = addressables.ResourceManager.CreateChainOperation(catalogHandle, res => OnCatalogDataLoaded(addressables, res, providerSuffix, remoteHashLocation));
return chainOp;
}
public AsyncOperationHandle<IResourceLocator> LoadContentCatalog(IResourceLocation loc, string providerSuffix, IResourceLocation remoteHashLocation)
{
return LoadContentCatalog(m_Addressables, loc, providerSuffix, remoteHashLocation);
}
//Attempts to load each catalog in order, stopping at first success.
internal AsyncOperationHandle<IResourceLocator> LoadContentCatalogInternal(IList<IResourceLocation> catalogs, int index, ResourceLocationMap locMap, IResourceLocation remoteHashLocation)
{
Addressables.LogFormat("Addressables - loading content catalog from {0}.", m_Addressables.ResourceManager.TransformInternalId(catalogs[index]));
var loadOp = LoadContentCatalog(catalogs[index], m_ProviderSuffix, remoteHashLocation);
if (loadOp.IsDone)
LoadOpComplete(loadOp, catalogs, locMap, index, remoteHashLocation);
else
loadOp.Completed += op => { LoadOpComplete(op, catalogs, locMap, index, remoteHashLocation); };
return loadOp;
}
void LoadOpComplete(AsyncOperationHandle<IResourceLocator> op, IList<IResourceLocation> catalogs, ResourceLocationMap locMap, int index, IResourceLocation remoteHashLocation)
{
if (op.Result != null)
{
m_Addressables.RemoveResourceLocator(locMap);
Result = op.Result;
Complete(Result, true, string.Empty);
m_Addressables.Release(op);
Addressables.Log("Addressables - initialization complete.");
}
else
{
Addressables.LogFormat("Addressables - failed to load content catalog from {0}.", op);
if (index + 1 >= catalogs.Count)
{
Addressables.LogWarningFormat("Addressables - initialization failed.", op);
m_Addressables.RemoveResourceLocator(locMap);
if (op.OperationException != null)
Complete(Result, false, op.OperationException);
else
Complete(Result, false, "LoadContentCatalogInternal");
m_Addressables.Release(op);
}
else
{
m_loadCatalogOp = LoadContentCatalogInternal(catalogs, index + 1, locMap, remoteHashLocation);
m_Addressables.Release(op);
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7db1cf7b24f9784a8c53aaaa271654d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,51 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Creates build logs that need to be seen at runtime.
/// </summary>
[Serializable]
public class PackedPlayModeBuildLogs
{
/// <summary>
/// A container for build logs that need to be seen at runtime.
/// </summary>
[Serializable]
public struct RuntimeBuildLog
{
/// <summary>
/// The type of log being stored. This will determine how the message is portrayed at runtime.
/// </summary>
public LogType Type;
/// <summary>
/// The contents of the build log.
/// </summary>
public string Message;
/// <summary>
/// Create a container for build logs that need to be seen at runtime.
/// </summary>
/// <param name="type">The type of log.</param>
/// <param name="message">The message to be logged.</param>
public RuntimeBuildLog(LogType type, string message)
{
Type = type;
Message = message;
}
}
[SerializeField]
List<RuntimeBuildLog> m_RuntimeBuildLogs = new List<RuntimeBuildLog>();
/// <summary>
/// List of logs that need to appear in the runtime that was generated by the build.
/// </summary>
public List<RuntimeBuildLog> RuntimeBuildLogs
{
get { return m_RuntimeBuildLogs; }
set { m_RuntimeBuildLogs = value; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf769c7817ebfa842bedf81cae9f2f6c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.Serialization;
namespace UnityEngine.AddressableAssets.Initialization
{
/// <summary>
/// Runtime data that is used to initialize the Addressables system.
/// </summary>
[Serializable]
public class ResourceManagerRuntimeData
{
/// <summary>
/// Address of the contained catalogs.
/// </summary>
public const string kCatalogAddress = "AddressablesMainContentCatalog";
[SerializeField]
string m_buildTarget;
/// <summary>
/// The name of the build target that this data was prepared for.
/// </summary>
public string BuildTarget
{
get { return m_buildTarget; }
set { m_buildTarget = value; }
}
[FormerlySerializedAs("m_settingsHash")]
[SerializeField]
string m_SettingsHash;
/// <summary>
/// The hash of the settings that generated this runtime data.
/// </summary>
public string SettingsHash
{
get { return m_SettingsHash; }
set { m_SettingsHash = value; }
}
[FormerlySerializedAs("m_catalogLocations")]
[SerializeField]
List<ResourceLocationData> m_CatalogLocations = new List<ResourceLocationData>();
/// <summary>
/// List of catalog locations to download in order (try remote first, then local)
/// </summary>
public List<ResourceLocationData> CatalogLocations
{
get { return m_CatalogLocations; }
}
[FormerlySerializedAs("m_profileEvents")]
[SerializeField]
bool m_ProfileEvents;
/// <summary>
/// Flag to control whether the ResourceManager sends profiler events.
/// </summary>
public bool ProfileEvents
{
get { return m_ProfileEvents; }
set { m_ProfileEvents = value; }
}
[FormerlySerializedAs("m_logResourceManagerExceptions")]
[SerializeField]
bool m_LogResourceManagerExceptions = true;
/// <summary>
/// When enabled, the Addressables.ResourceManager.ExceptionHandler is set to (op, ex) => Debug.LogException(ex);
/// </summary>
public bool LogResourceManagerExceptions
{
get { return m_LogResourceManagerExceptions; }
set { m_LogResourceManagerExceptions = value; }
}
[FormerlySerializedAs("m_extraInitializationData")]
[SerializeField]
List<ObjectInitializationData> m_ExtraInitializationData = new List<ObjectInitializationData>();
/// <summary>
/// The list of initialization data. These objects will get deserialized and initialized during the Addressables initialization process. This happens after resource providers have been set up but before any catalogs are loaded.
/// </summary>
public List<ObjectInitializationData> InitializationObjects
{
get { return m_ExtraInitializationData; }
}
[SerializeField]
private bool m_DisableCatalogUpdateOnStart = false;
/// <summary>
/// Determine if we should check for Remote Catalogs on during initialization.
/// </summary>
public bool DisableCatalogUpdateOnStartup
{
get { return m_DisableCatalogUpdateOnStart; }
set { m_DisableCatalogUpdateOnStart = value; }
}
[SerializeField]
private bool m_IsLocalCatalogInBundle = false;
/// <summary>
/// Whether the local catalog has been serialized in an asset bundle or as json
/// </summary>
public bool IsLocalCatalogInBundle
{
get { return m_IsLocalCatalogInBundle; }
set { m_IsLocalCatalogInBundle = value; }
}
[SerializeField]
SerializedType m_CertificateHandlerType;
/// <summary>
/// The type of CertificateHandler to use for this provider.
/// </summary>
public Type CertificateHandlerType
{
get { return m_CertificateHandlerType.Value; }
set { m_CertificateHandlerType.Value = value; }
}
[SerializeField]
string m_AddressablesVersion;
/// <summary>
/// The current active version of the Addressables package.
/// </summary>
public string AddressablesVersion
{
get { return m_AddressablesVersion; }
set { m_AddressablesVersion = value; }
}
[SerializeField]
int m_maxConcurrentWebRequests = 500;
/// <summary>
/// The maximum number of concurrent web requests. This value will be clamped from 1 to 1024.
/// </summary>
public int MaxConcurrentWebRequests
{
get { return m_maxConcurrentWebRequests; }
set { m_maxConcurrentWebRequests = Mathf.Clamp(value, 1, 1024); }
}
[SerializeField]
int m_CatalogRequestsTimeout = 0;
/// <summary>
/// The time until a catalog hash or json UnityWebRequest download will timeout in seconds. 0 for Default timeout.
/// </summary>
public int CatalogRequestsTimeout
{
get { return m_CatalogRequestsTimeout; }
set { m_CatalogRequestsTimeout = value < 0 ? 0 : value; }
}
#if ENABLE_CCD
/// <summary>
/// Stores the CcdManager data to set the CCD properties to pull from.
/// </summary>
[SerializeField]
CcdManagedData m_CcdManagedData;
internal CcdManagedData CcdManagedData { get { return m_CcdManagedData; } set { m_CcdManagedData = value; } }
#endif
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 81e994b90fc45fb46963bcce32bf4969
timeCreated: 1499885054
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,128 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.AddressableAssets.ResourceProviders;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace UnityEngine.AddressableAssets
{
class UpdateCatalogsOperation : AsyncOperationBase<List<IResourceLocator>>
{
AddressablesImpl m_Addressables;
List<ResourceLocatorInfo> m_LocatorInfos;
internal AsyncOperationHandle<IList<AsyncOperationHandle>> m_DepOp;
AsyncOperationHandle<bool> m_CleanCacheOp;
bool m_AutoCleanBundleCache = false;
public UpdateCatalogsOperation(AddressablesImpl aa)
{
m_Addressables = aa;
}
public AsyncOperationHandle<List<IResourceLocator>> Start(IEnumerable<string> catalogIds, bool autoCleanBundleCache)
{
m_LocatorInfos = new List<ResourceLocatorInfo>();
var locations = new List<IResourceLocation>();
foreach (var c in catalogIds)
{
if (c == null)
continue;
var loc = m_Addressables.GetLocatorInfo(c);
locations.Add(loc.CatalogLocation);
m_LocatorInfos.Add(loc);
}
if (locations.Count == 0)
return m_Addressables.ResourceManager.CreateCompletedOperation(default(List<IResourceLocator>), "Content update not available.");
ContentCatalogProvider ccp = m_Addressables.ResourceManager.ResourceProviders
.FirstOrDefault(rp => rp.GetType() == typeof(ContentCatalogProvider)) as ContentCatalogProvider;
if (ccp != null)
ccp.DisableCatalogUpdateOnStart = false;
m_DepOp = m_Addressables.ResourceManager.CreateGroupOperation<object>(locations);
m_AutoCleanBundleCache = autoCleanBundleCache;
return m_Addressables.ResourceManager.StartOperation(this, m_DepOp);
}
/// <inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (IsDone)
return true;
if (m_DepOp.IsValid() && !m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
if (m_CleanCacheOp.IsValid() && !m_CleanCacheOp.IsDone)
m_CleanCacheOp.WaitForCompletion();
m_Addressables.ResourceManager.Update(Time.unscaledDeltaTime);
return IsDone;
}
protected override void Destroy()
{
m_Addressables.Release(m_DepOp);
}
/// <inheritdoc />
public override void GetDependencies(List<AsyncOperationHandle> dependencies)
{
dependencies.Add(m_DepOp);
}
protected override void Execute()
{
var catalogs = new List<IResourceLocator>(m_DepOp.Result.Count);
for (int i = 0; i < m_DepOp.Result.Count; i++)
{
var locator = m_DepOp.Result[i].Result as IResourceLocator;
string localHash = null;
IResourceLocation remoteLocation = null;
if (locator == null)
{
var catData = m_DepOp.Result[i].Result as ContentCatalogData;
locator = catData.CreateCustomLocator(catData.location.PrimaryKey);
localHash = catData.localHash;
remoteLocation = catData.location;
}
m_LocatorInfos[i].UpdateContent(locator, localHash, remoteLocation);
catalogs.Add(m_LocatorInfos[i].Locator);
}
if (!m_AutoCleanBundleCache)
{
if (m_DepOp.Status == AsyncOperationStatus.Succeeded)
Complete(catalogs, true, null);
else if (m_DepOp.Status == AsyncOperationStatus.Failed)
Complete(catalogs, false, $"Cannot update catalogs. Failed to load catalog: {m_DepOp.OperationException.Message}");
else
{
var errorMessage = "Cannot update catalogs. Catalog loading operation is still in progress when it should already be completed. ";
errorMessage += (m_DepOp.OperationException != null) ? m_DepOp.OperationException.Message : "";
Complete(catalogs, false, errorMessage);
}
}
else
{
m_CleanCacheOp = m_Addressables.CleanBundleCache(m_DepOp, false);
OnCleanCacheCompleted(m_CleanCacheOp, catalogs);
}
}
void OnCleanCacheCompleted(AsyncOperationHandle<bool> handle, List<IResourceLocator> catalogs)
{
handle.Completed += (obj) =>
{
bool success = obj.Status == AsyncOperationStatus.Succeeded;
Complete(catalogs, success, success ? null : $"{obj.DebugName}, status={obj.Status}, result={obj.Result} catalogs updated, but failed to clean bundle cache.");
};
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6294567ae0241e540a0423a0515d287d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7fb84c109abae6048a38652a447fb108
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,75 @@
using System;
namespace UnityEngine.AddressableAssets.ResourceLocators
{
#if ENABLE_CCD
/// <summary>
/// This is an internal class used as an intermediary data store from editor time to runtime
/// </summary>
[Serializable]
internal class CcdManagedData
{
/// <summary>
/// Denotes what state the config is in.
/// </summary>
public enum ConfigState
{
/// <summary>
/// Config has not been modified.
/// </summary>
None,
/// <summary>
/// Config should use default values according to CCD opinionated workflow.
/// </summary>
Default,
/// <summary>
/// The config has been overriden externally.
/// </summary>
Override
};
/// <summary>
/// Id of the Environment to store
/// </summary>
public string EnvironmentId;
/// <summary>
/// Name of the Environment to store
/// </summary>
public string EnvironmentName;
/// <summary>
/// Id of the Bucket to store
/// </summary>
public string BucketId;
/// <summary>
/// Name of the Badge to store
/// </summary>
public string Badge;
/// <summary>
/// The current state of the config
/// </summary>
public ConfigState State;
/// <summary>
/// Constructor for CcdManagedData
/// </summary>
public CcdManagedData()
{
State = ConfigState.None;
}
/// <summary>
/// Determines if the CcdManagedData has been configured
/// </summary>
/// <returns>True if all fields have been set. False, otherwise.</returns>
public bool IsConfigured()
{
return !string.IsNullOrEmpty(EnvironmentId) && !string.IsNullOrEmpty(BucketId) && !string.IsNullOrEmpty(Badge);
}
}
#endif
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dd13484018b914e10a97ed9e5e9d4016
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d415d633eb1c9d44c854bac7bdc6d17f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.U2D;
namespace UnityEngine.AddressableAssets
{
internal class DynamicResourceLocator : IResourceLocator
{
AddressablesImpl m_Addressables;
public string LocatorId => nameof(DynamicResourceLocator);
public virtual IEnumerable<object> Keys => new object[0];
private string m_AtlasSpriteProviderId = null;
private string AtlasSpriteProviderId
{
get
{
if (!string.IsNullOrEmpty(m_AtlasSpriteProviderId))
return m_AtlasSpriteProviderId;
var providers = m_Addressables.ResourceManager.ResourceProviders;
foreach (IResourceProvider provider in providers)
{
if (provider is AtlasSpriteProvider)
{
m_AtlasSpriteProviderId = provider.ProviderId;
return m_AtlasSpriteProviderId;
}
}
// if nothing found, fallback to the default name
return typeof(AtlasSpriteProvider).FullName;
}
}
public IEnumerable<IResourceLocation> AllLocations => new IResourceLocation[0];
public DynamicResourceLocator(AddressablesImpl addr)
{
m_Addressables = addr;
}
public bool Locate(object key, Type type, out IList<IResourceLocation> locations)
{
locations = null;
if (ResourceManagerConfig.ExtractKeyAndSubKey(key, out string mainKey, out string subKey))
{
if (!m_Addressables.GetResourceLocations(mainKey, type, out IList<IResourceLocation> locs))
{
if (type == typeof(Sprite))
m_Addressables.GetResourceLocations(mainKey, typeof(SpriteAtlas), out locs);
}
if (locs != null && locs.Count > 0)
{
locations = new List<IResourceLocation>(locs.Count);
foreach (var l in locs)
CreateDynamicLocations(type, locations, key as string, subKey, l);
return true;
}
}
return false;
}
internal void CreateDynamicLocations(Type type, IList<IResourceLocation> locations, string locName, string subKey, IResourceLocation mainLoc)
{
if (type == typeof(Sprite) && mainLoc.ResourceType == typeof(U2D.SpriteAtlas))
{
locations.Add(new ResourceLocationBase(locName, $"{mainLoc.InternalId}[{subKey}]", AtlasSpriteProviderId, type, new IResourceLocation[] {mainLoc}));
}
else
{
if (mainLoc.HasDependencies)
locations.Add(new ResourceLocationBase(locName, $"{mainLoc.InternalId}[{subKey}]", mainLoc.ProviderId, mainLoc.ResourceType, mainLoc.Dependencies.ToArray()));
else
locations.Add(new ResourceLocationBase(locName, $"{mainLoc.InternalId}[{subKey}]", mainLoc.ProviderId, mainLoc.ResourceType));
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 753f9965abdd3f24eb42eafccaf7a286
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace UnityEngine.AddressableAssets.ResourceLocators
{
/// <summary>
/// Interface used by the Addressables system to find the locations of a given key.
/// </summary>
public interface IResourceLocator
{
/// <summary>
/// The id for this locator.
/// </summary>
string LocatorId { get; }
/// <summary>
/// The keys defined by this locator.
/// </summary>
IEnumerable<object> Keys { get; }
#if ENABLE_BINARY_CATALOG
/// <summary>
/// All locations that are available by this locator.
/// </summary>
IEnumerable<IResourceLocation> AllLocations { get; }
#endif
/// <summary>
/// Retrieve the locations from a specified key.
/// </summary>
/// <param name="key">The key to use.</param>
/// <param name="type">The resource type.</param>
/// <param name="locations">The resulting set of locations for the key.</param>
/// <returns>True if any locations were found with the specified key.</returns>
bool Locate(object key, Type type, out IList<IResourceLocation> locations);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e0d9d70fb62e8164d98b6567cf268d5c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
namespace UnityEngine.AddressableAssets.ResourceLocators
{
/// <summary>
/// Simple locator that acts as a passthrough for assets loaded from resources directories.
/// </summary>
public class LegacyResourcesLocator : IResourceLocator
{
/// <summary>
/// The key is converted to a string and used as the internal id of the location added to the locations parameter.
/// </summary>
/// <param name="key">The key of the location. This should be a string with the resources path of the asset.</param>
/// <param name="type">The resource type.</param>
/// <param name="locations">The list of locations. This will have at most one item.</param>
/// <returns>True if the key is a string object and a location was created, false otherwise.</returns>
public bool Locate(object key, Type type, out IList<IResourceLocation> locations)
{
locations = null;
var strKey = key as string;
if (strKey == null)
return false;
locations = new List<IResourceLocation>();
locations.Add(new ResourceLocationBase("LegacyResourceLocation", strKey, typeof(LegacyResourcesProvider).FullName, typeof(UnityEngine.Object)));
return true;
}
#if ENABLE_BINARY_CATALOG
/// <summary>
/// Enumeration of all locations for this locator. This will return an empty array.
/// </summary>
public IEnumerable<IResourceLocation> AllLocations => new IResourceLocation[0];
#endif
/// <summary>
/// The keys available in this locator.
/// </summary>
public IEnumerable<object> Keys
{
get { return null; }
}
/// <summary>
/// Id of locator.
/// </summary>
public string LocatorId => nameof(LegacyResourcesLocator);
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 6f5f2eb71e71b446dae10e69b8a38b2c
timeCreated: 1504834356
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,118 @@
using System;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.Serialization;
namespace UnityEngine.AddressableAssets.ResourceLocators
{
/// <summary>
/// Serializable location data. This is used for the locations of the content catalogs.
/// </summary>
[Serializable]
public class ResourceLocationData
{
[FormerlySerializedAs("m_keys")]
[SerializeField]
string[] m_Keys;
/// <summary>
/// The collection of keys for this location.
/// </summary>
public string[] Keys
{
get { return m_Keys; }
}
[FormerlySerializedAs("m_internalId")]
[SerializeField]
string m_InternalId;
/// <summary>
/// The internal id.
/// </summary>
public string InternalId
{
get { return m_InternalId; }
}
[FormerlySerializedAs("m_provider")]
[SerializeField]
string m_Provider;
/// <summary>
/// The provider id.
/// </summary>
public string Provider
{
get { return m_Provider; }
}
[FormerlySerializedAs("m_dependencies")]
[SerializeField]
string[] m_Dependencies;
/// <summary>
/// The collection of dependencies for this location.
/// </summary>
public string[] Dependencies
{
get { return m_Dependencies; }
}
[SerializeField]
SerializedType m_ResourceType;
/// <summary>
/// The type of the resource for the location.
/// </summary>
public Type ResourceType
{
get { return m_ResourceType.Value; }
}
[SerializeField]
byte[] SerializedData;
object _Data;
/// <summary>
/// The optional arbitrary data stored along with location
/// </summary>
public object Data
{
get
{
if (_Data == null)
{
if (SerializedData == null || SerializedData.Length <= 0)
return null;
_Data = Utility.SerializationUtilities.ReadObjectFromByteArray(SerializedData, 0);
}
return _Data;
}
set
{
var tmp = new System.Collections.Generic.List<byte>();
Utility.SerializationUtilities.WriteObjectToByteList(value, tmp);
SerializedData = tmp.ToArray();
}
}
/// <summary>
/// Construct a new ResourceLocationData object.
/// </summary>
/// <param name="keys">Array of keys for the location. This must contain at least one item.</param>
/// <param name="id">The internal id.</param>
/// <param name="provider">The provider id.</param>
/// <param name="t">The resource object type.</param>
/// <param name="dependencies">Optional array of dependencies.</param>
public ResourceLocationData(string[] keys, string id, Type provider, Type t, string[] dependencies = null)
{
m_Keys = keys;
m_InternalId = id;
m_Provider = provider == null ? "" : provider.FullName;
m_Dependencies = dependencies == null ? new string[0] : dependencies;
m_ResourceType = new SerializedType() {Value = t};
}
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: f409833c61db2b9428d2f2bcf4dbb0a1
timeCreated: 1506572728
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace UnityEngine.AddressableAssets.ResourceLocators
{
/// <summary>
/// Simple implementation of an IResourceLocator
/// </summary>
public class ResourceLocationMap : IResourceLocator
{
/// <summary>
/// Construct a new ResourceLocationMap object.
/// </summary>
/// <param name="id">The locator id.</param>
/// <param name="capacity">The expected number of items.</param>
public ResourceLocationMap(string id, int capacity = 0)
{
LocatorId = id;
locations = new Dictionary<object, IList<IResourceLocation>>(capacity == 0 ? 100 : capacity);
}
/// <summary>
/// Stores the resource locator id.
/// </summary>
public string LocatorId { get; private set; }
/// <summary>
/// Construct a new ResourceLocationMap object with a list of locations.
/// </summary>
/// <param name="id">The locator id.</param>
/// <param name="locations">The list of locations to initialize with.</param>
public ResourceLocationMap(string id, IList<ResourceLocationData> locations)
{
LocatorId = id;
if (locations == null)
return;
this.locations = new Dictionary<object, IList<IResourceLocation>>(locations.Count * 2);
var locMap = new Dictionary<string, ResourceLocationBase>();
var dataMap = new Dictionary<string, ResourceLocationData>();
//create and collect locations
for (int i = 0; i < locations.Count; i++)
{
var rlData = locations[i];
if (rlData.Keys == null || rlData.Keys.Length < 1)
{
Addressables.LogErrorFormat("Address with id '{0}' does not have any valid keys, skipping...", rlData.InternalId);
continue;
}
if (locMap.ContainsKey(rlData.Keys[0]))
{
Addressables.LogErrorFormat("Duplicate address '{0}' with id '{1}' found, skipping...", rlData.Keys[0], rlData.InternalId);
continue;
}
var loc = new ResourceLocationBase(rlData.Keys[0], Addressables.ResolveInternalId(rlData.InternalId), rlData.Provider, rlData.ResourceType);
loc.Data = rlData.Data;
locMap.Add(rlData.Keys[0], loc);
dataMap.Add(rlData.Keys[0], rlData);
}
//fix up dependencies between them
foreach (var kvp in locMap)
{
var data = dataMap[kvp.Key];
if (data.Dependencies != null)
{
foreach (var d in data.Dependencies)
kvp.Value.Dependencies.Add(locMap[d]);
kvp.Value.ComputeDependencyHash();
}
}
foreach (KeyValuePair<string, ResourceLocationBase> kvp in locMap)
{
ResourceLocationData rlData = dataMap[kvp.Key];
foreach (var k in rlData.Keys)
Add(k, kvp.Value);
}
}
/// <summary>
/// The mapping of key to location lists.
/// </summary>
Dictionary<object, IList<IResourceLocation>> locations;
#if ENABLE_BINARY_CATALOG
/// <summary>
/// Enumeration of all locations for this locator.
/// </summary>
public IEnumerable<IResourceLocation> AllLocations => locations.SelectMany(k => k.Value);
#endif
/// <summary>
/// Map of all locations for this locator.
/// </summary>
public Dictionary<object, IList<IResourceLocation>> Locations => locations;
/// <summary>
/// The keys available in this locator.
/// </summary>
public IEnumerable<object> Keys
{
get { return locations.Keys; }
}
/// <summary>
/// Locate all of the locations that match the given key.
/// </summary>
/// <param name="key">The key used to locate the locations.</param>
/// <param name="type">The resource type.</param>
/// <param name="locations">The list of found locations. This list is shared so it should not be modified.</param>
/// <returns>Returns true if a location was found. Returns false otherwise.</returns>
public bool Locate(object key, Type type, out IList<IResourceLocation> locations)
{
IList<IResourceLocation> locs = null;
if (!this.locations.TryGetValue(key, out locs))
{
locations = null;
return false;
}
if (type == null)
{
locations = locs;
return true;
}
var validTypeCount = 0;
foreach (var l in locs)
if (type.IsAssignableFrom(l.ResourceType))
validTypeCount++;
if (validTypeCount == 0)
{
locations = null;
return false;
}
if (validTypeCount == locs.Count)
{
locations = locs;
return true;
}
locations = new List<IResourceLocation>();
foreach (var l in locs)
{
if (type.IsAssignableFrom(l.ResourceType))
locations.Add(l);
}
return true;
}
/// <summary>
/// Add a new location.
/// </summary>
/// <param name="key">The key to reference the location.</param>
/// <param name="location">The location to add.</param>
public void Add(object key, IResourceLocation location)
{
IList<IResourceLocation> locations;
if (!this.locations.TryGetValue(key, out locations))
this.locations.Add(key, locations = new List<IResourceLocation>());
locations.Add(location);
}
/// <summary>
/// Add a list of locations.
/// </summary>
/// <param name="key">The key to reference the locations with.</param>
/// <param name="locations">The list of locations to store at the given key.</param>
public void Add(object key, IList<IResourceLocation> locations)
{
this.locations.Add(key, locations);
}
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 902a23aa7fb810b4c90bd35c50e66fde
timeCreated: 1500663969
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6468a66b467b3f440929865a8ad85665
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyCompany("Unity Technologies")]
[assembly: InternalsVisibleTo("Unity.ResourceManager.Tests")]
[assembly: InternalsVisibleTo("Unity.Addressables.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.Addressables.Tests")]
[assembly: InternalsVisibleTo("Unity.Addressables")]
#if UNITY_EDITOR
[assembly: InternalsVisibleTo("Unity.Addressables.Editor")]
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 680fb597e686741c7a2e225708f654d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 61189accf2e8d427e8a15c5ccd4db00b
folderAsset: yes
timeCreated: 1503086217
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,616 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.Util;
// ReSharper disable DelegateSubtraction
namespace UnityEngine.ResourceManagement.AsyncOperations
{
internal interface ICachable
{
IOperationCacheKey Key { get; set; }
}
internal interface IAsyncOperation
{
object GetResultAsObject();
Type ResultType { get; }
int Version { get; }
string DebugName { get; }
void DecrementReferenceCount();
void IncrementReferenceCount();
int ReferenceCount { get; }
float PercentComplete { get; }
DownloadStatus GetDownloadStatus(HashSet<object> visited);
AsyncOperationStatus Status { get; }
Exception OperationException { get; }
bool IsDone { get; }
Action<IAsyncOperation> OnDestroy { set; }
void GetDependencies(List<AsyncOperationHandle> deps);
bool IsRunning { get; }
event Action<AsyncOperationHandle> CompletedTypeless;
event Action<AsyncOperationHandle> Destroyed;
void InvokeCompletionEvent();
System.Threading.Tasks.Task<object> Task { get; }
void Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks);
AsyncOperationHandle Handle { get; }
void WaitForCompletion();
}
/// <summary>
/// base class for implemented AsyncOperations, implements the needed interfaces and consolidates redundant code
/// </summary>
/// <typeparam name="TObject">The type of the operation.</typeparam>
public abstract class AsyncOperationBase<TObject> : IAsyncOperation
{
/// <summary>
/// This will be called by the resource manager after all dependent operation complete. This method should not be called manually.
/// A custom operation should override this method and begin work when it is called.
/// </summary>
protected abstract void Execute();
/// <summary>
/// This will be called by the resource manager when the reference count of the operation reaches zero. This method should not be called manually.
/// A custom operation should override this method and release any held resources
/// </summary>
protected virtual void Destroy()
{
}
/// <summary>
/// A custom operation should override this method to return the progress of the operation.
/// </summary>
/// <returns>Progress of the operation. Value should be between 0.0f and 1.0f</returns>
protected virtual float Progress
{
get { return 0; }
}
/// <summary>
/// A custom operation should override this method to provide a debug friendly name for the operation.
/// </summary>
protected virtual string DebugName
{
get { return this.ToString(); }
}
/// <summary>
/// A custom operation should override this method to provide a list of AsyncOperationHandles that it depends on.
/// </summary>
/// <param name="dependencies">The list that should be populated with dependent AsyncOperationHandles.</param>
public virtual void GetDependencies(List<AsyncOperationHandle> dependencies)
{
}
/// <summary>
/// Accessor to Result of the operation.
/// </summary>
public TObject Result { get; set; }
int m_referenceCount = 1;
internal AsyncOperationStatus m_Status;
internal Exception m_Error;
internal ResourceManager m_RM;
internal int m_Version;
internal int Version
{
get { return m_Version; }
}
DelegateList<AsyncOperationHandle> m_DestroyedAction;
DelegateList<AsyncOperationHandle<TObject>> m_CompletedActionT;
internal bool CompletedEventHasListeners => m_CompletedActionT != null && m_CompletedActionT.Count > 0;
internal bool DestroyedEventHasListeners => m_DestroyedAction != null && m_DestroyedAction.Count > 0;
Action<IAsyncOperation> m_OnDestroyAction;
internal Action<IAsyncOperation> OnDestroy
{
set { m_OnDestroyAction = value; }
}
Action<AsyncOperationHandle> m_dependencyCompleteAction;
/// <summary>
/// True, If the operation has been executed, else false
/// </summary>
protected internal bool HasExecuted = false;
internal event Action Executed;
/// <summary>
/// The number of references that are using this operation.
/// When the ReferenceCount reaches 0, this operation is Destroyed.
/// </summary>
protected internal int ReferenceCount
{
get { return m_referenceCount; }
}
/// <summary>
/// True if the current op has begun but hasn't yet reached completion. False otherwise.
/// </summary>
public bool IsRunning { get; internal set; }
/// <summary>
/// Basic constructor for AsyncOperationBase.
/// </summary>
protected AsyncOperationBase()
{
m_UpdateCallback = UpdateCallback;
m_dependencyCompleteAction = o => InvokeExecute();
}
internal static string ShortenPath(string p, bool keepExtension)
{
var slashIndex = p.LastIndexOf('/');
if (slashIndex > 0)
p = p.Substring(slashIndex + 1);
if (!keepExtension)
{
slashIndex = p.LastIndexOf('.');
if (slashIndex > 0)
p = p.Substring(0, slashIndex);
}
return p;
}
/// <summary>
/// Synchronously complete the async operation.
/// </summary>
public void WaitForCompletion()
{
if (PlatformUtilities.PlatformUsesMultiThreading(Application.platform))
while (!InvokeWaitForCompletion())
{
}
else
throw new Exception($"{Application.platform} does not support synchronous Addressable loading. Please do not use WaitForCompletion on the {Application.platform} platform.");
}
/// <summary>
/// Used for the implementation of WaitForCompletion in an IAsyncOperation.
/// </summary>
/// <returns>True if the operation has completed, otherwise false.</returns>
protected virtual bool InvokeWaitForCompletion()
{
return true;
}
/// <summary>
/// Increments the reference count for this operation.
/// </summary>
/// <exception cref="Exception">Thrown if the operation has already been destroyed after reaching 0 reference count.</exception>
protected internal void IncrementReferenceCount()
{
if (m_referenceCount == 0)
throw new Exception(string.Format("Cannot increment reference count on operation {0} because it has already been destroyed", this));
m_referenceCount++;
if (m_RM != null && m_RM.postProfilerEvents)
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationReferenceCount,
m_referenceCount));
}
/// <summary>
/// Reduces the reference count for this operation by 1. If the reference count is reduced to 0, the operation is destroyed.
/// </summary>
/// <exception cref="Exception">Thrown if the operation has already been destroyed after reaching 0 reference count.</exception>
protected internal void DecrementReferenceCount()
{
if (m_referenceCount <= 0)
throw new Exception(string.Format("Cannot decrement reference count for operation {0} because it is already 0", this));
m_referenceCount--;
if (m_RM != null && m_RM.postProfilerEvents)
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationReferenceCount,
m_referenceCount));
if (m_referenceCount == 0)
{
if (m_RM != null && m_RM.postProfilerEvents)
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationDestroy));
if (m_DestroyedAction != null)
{
m_DestroyedAction.Invoke(new AsyncOperationHandle<TObject>(this));
m_DestroyedAction.Clear();
}
Destroy();
Result = default(TObject);
m_referenceCount = 1;
m_Status = AsyncOperationStatus.None;
m_taskCompletionSource = null;
m_taskCompletionSourceTypeless = null;
m_Error = null;
m_Version++;
m_RM = null;
if (m_OnDestroyAction != null)
{
m_OnDestroyAction(this);
m_OnDestroyAction = null;
}
}
}
TaskCompletionSource<TObject> m_taskCompletionSource;
internal Task<TObject> Task
{
get
{
if (m_taskCompletionSource == null)
{
m_taskCompletionSource = new TaskCompletionSource<TObject>(TaskCreationOptions.RunContinuationsAsynchronously);
if (IsDone && !CompletedEventHasListeners)
m_taskCompletionSource.SetResult(Result);
}
return m_taskCompletionSource.Task;
}
}
TaskCompletionSource<object> m_taskCompletionSourceTypeless;
Task<object> IAsyncOperation.Task
{
get
{
if (m_taskCompletionSourceTypeless == null)
{
m_taskCompletionSourceTypeless = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
if (IsDone && !CompletedEventHasListeners)
m_taskCompletionSourceTypeless.SetResult(Result);
}
return m_taskCompletionSourceTypeless.Task;
}
}
/// <summary>
/// Converts the information about the operation to a formatted string.
/// </summary>
/// <returns>Returns the information about the operation.</returns>
public override string ToString()
{
var instId = "";
var or = Result as Object;
if (or != null)
instId = "(" + or.GetInstanceID() + ")";
return string.Format("{0}, result='{1}', status='{2}'", base.ToString(), (or + instId), m_Status);
}
bool m_InDeferredCallbackQueue;
void RegisterForDeferredCallbackEvent(bool incrementReferenceCount = true)
{
if (IsDone && !m_InDeferredCallbackQueue)
{
m_InDeferredCallbackQueue = true;
m_RM.RegisterForDeferredCallback(this, incrementReferenceCount);
}
}
internal event Action<AsyncOperationHandle<TObject>> Completed
{
add
{
if (m_CompletedActionT == null)
m_CompletedActionT = DelegateList<AsyncOperationHandle<TObject>>.CreateWithGlobalCache();
m_CompletedActionT.Add(value);
RegisterForDeferredCallbackEvent();
}
remove { m_CompletedActionT?.Remove(value); }
}
internal event Action<AsyncOperationHandle> Destroyed
{
add
{
if (m_DestroyedAction == null)
m_DestroyedAction = DelegateList<AsyncOperationHandle>.CreateWithGlobalCache();
m_DestroyedAction.Add(value);
}
remove { m_DestroyedAction?.Remove(value); }
}
internal event Action<AsyncOperationHandle> CompletedTypeless
{
add { Completed += s => value(s); }
remove { Completed -= s => value(s); }
}
/// <inheritdoc />
internal AsyncOperationStatus Status
{
get { return m_Status; }
}
/// <inheritdoc />
internal Exception OperationException
{
get { return m_Error; }
private set
{
m_Error = value;
if (m_Error != null && ResourceManager.ExceptionHandler != null)
ResourceManager.ExceptionHandler(new AsyncOperationHandle(this), value);
}
}
internal bool MoveNext()
{
return !IsDone;
}
internal void Reset()
{
}
internal object Current
{
get { return null; }
} // should throw exception?
internal bool IsDone
{
get { return Status == AsyncOperationStatus.Failed || Status == AsyncOperationStatus.Succeeded; }
}
internal float PercentComplete
{
get
{
if (m_Status == AsyncOperationStatus.None)
{
try
{
return Progress;
}
catch
{
return 0.0f;
}
}
return 1.0f;
}
}
internal void InvokeCompletionEvent()
{
if (m_CompletedActionT != null)
{
m_CompletedActionT.Invoke(new AsyncOperationHandle<TObject>(this));
m_CompletedActionT.Clear();
}
if (m_taskCompletionSource != null)
m_taskCompletionSource.TrySetResult(Result);
if (m_taskCompletionSourceTypeless != null)
m_taskCompletionSourceTypeless.TrySetResult(Result);
m_InDeferredCallbackQueue = false;
}
internal AsyncOperationHandle<TObject> Handle
{
get { return new AsyncOperationHandle<TObject>(this); }
}
DelegateList<float> m_UpdateCallbacks;
Action<float> m_UpdateCallback;
private void UpdateCallback(float unscaledDeltaTime)
{
IUpdateReceiver updateOp = this as IUpdateReceiver;
updateOp.Update(unscaledDeltaTime);
}
/// <summary>
/// Complete the operation and invoke events.
/// </summary>
/// <remarks>
/// An operation is considered to have failed silently if success is true and if errorMsg isn't null or empty.
/// The exception handler will be called in cases of silent failures.
/// Any failed operations will call Release on any dependencies that succeeded.
/// </remarks>
/// <param name="result">The result object for the operation.</param>
/// <param name="success">True if successful or if the operation failed silently.</param>
/// <param name="errorMsg">The error message if the operation has failed.</param>
public void Complete(TObject result, bool success, string errorMsg)
{
Complete(result, success, errorMsg, true);
}
/// <summary>
/// Complete the operation and invoke events.
/// </summary>
/// <remarks>
/// An operation is considered to have failed silently if success is true and if errorMsg isn't null or empty.
/// The exception handler will be called in cases of silent failures.
/// </remarks>
/// <param name="result">The result object for the operation.</param>
/// <param name="success">True if successful or if the operation failed silently.</param>
/// <param name="errorMsg">The error message if the operation has failed.</param>
/// <param name="releaseDependenciesOnFailure">When true, failed operations will release any dependencies that succeeded.</param>
public void Complete(TObject result, bool success, string errorMsg, bool releaseDependenciesOnFailure)
{
Complete(result, success, !string.IsNullOrEmpty(errorMsg) ? new OperationException(errorMsg) : null, releaseDependenciesOnFailure);
}
/// <summary>
/// Complete the operation and invoke events.
/// </summary>
/// <remarks>
/// An operation is considered to have failed silently if success is true and if exception isn't null.
/// The exception handler will be called in cases of silent failures.
/// </remarks>
/// <param name="result">The result object for the operation.</param>
/// <param name="success">True if successful or if the operation failed silently.</param>
/// <param name="exception">The exception if the operation has failed.</param>
/// <param name="releaseDependenciesOnFailure">When true, failed operations will release any dependencies that succeeded.</param>
public void Complete(TObject result, bool success, Exception exception, bool releaseDependenciesOnFailure = true)
{
if (IsDone)
return;
IUpdateReceiver upOp = this as IUpdateReceiver;
if (m_UpdateCallbacks != null && upOp != null)
m_UpdateCallbacks.Remove(m_UpdateCallback);
Result = result;
m_Status = success ? AsyncOperationStatus.Succeeded : AsyncOperationStatus.Failed;
if (m_RM != null && m_RM.postProfilerEvents)
{
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationPercentComplete, 1));
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationComplete));
}
if (m_Status == AsyncOperationStatus.Failed || exception != null)
{
if (exception == null || string.IsNullOrEmpty(exception.Message))
OperationException = new OperationException($"Unknown error in AsyncOperation : {DebugName}");
else
OperationException = exception;
}
if (m_Status == AsyncOperationStatus.Failed)
{
if (releaseDependenciesOnFailure)
ReleaseDependencies();
if (m_RM != null && m_RM.postProfilerEvents)
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationFail, 0,
exception?.ToString()));
ICachable cachedOperation = this as ICachable;
if (cachedOperation?.Key != null)
m_RM?.RemoveOperationFromCache(cachedOperation.Key);
RegisterForDeferredCallbackEvent(false);
}
else
{
InvokeCompletionEvent();
DecrementReferenceCount();
}
IsRunning = false;
}
internal void Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks)
{
m_RM = rm;
IsRunning = true;
HasExecuted = false;
if (m_RM != null && m_RM.postProfilerEvents)
{
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationCreate));
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationPercentComplete, 0));
}
IncrementReferenceCount(); // keep a reference until the operation completes
m_UpdateCallbacks = updateCallbacks;
if (dependency.IsValid() && !dependency.IsDone)
dependency.Completed += m_dependencyCompleteAction;
else
InvokeExecute();
}
internal void InvokeExecute()
{
Execute();
HasExecuted = true;
IUpdateReceiver upOp = this as IUpdateReceiver;
if (upOp != null && !IsDone)
m_UpdateCallbacks.Add(m_UpdateCallback);
Executed?.Invoke();
}
event Action<AsyncOperationHandle> IAsyncOperation.CompletedTypeless
{
add { CompletedTypeless += value; }
remove { CompletedTypeless -= value; }
}
event Action<AsyncOperationHandle> IAsyncOperation.Destroyed
{
add { Destroyed += value; }
remove { Destroyed -= value; }
}
int IAsyncOperation.Version => Version;
int IAsyncOperation.ReferenceCount => ReferenceCount;
float IAsyncOperation.PercentComplete => PercentComplete;
AsyncOperationStatus IAsyncOperation.Status => Status;
Exception IAsyncOperation.OperationException => OperationException;
bool IAsyncOperation.IsDone => IsDone;
AsyncOperationHandle IAsyncOperation.Handle => Handle;
Action<IAsyncOperation> IAsyncOperation.OnDestroy
{
set { OnDestroy = value; }
}
string IAsyncOperation.DebugName => DebugName;
/// <inheritdoc/>
object IAsyncOperation.GetResultAsObject() => Result;
Type IAsyncOperation.ResultType
{
get { return typeof(TObject); }
}
/// <inheritdoc/>
void IAsyncOperation.GetDependencies(List<AsyncOperationHandle> deps) => GetDependencies(deps);
/// <inheritdoc/>
void IAsyncOperation.DecrementReferenceCount() => DecrementReferenceCount();
/// <inheritdoc/>
void IAsyncOperation.IncrementReferenceCount() => IncrementReferenceCount();
/// <inheritdoc/>
void IAsyncOperation.InvokeCompletionEvent() => InvokeCompletionEvent();
/// <inheritdoc/>
void IAsyncOperation.Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks) => Start(rm, dependency, updateCallbacks);
internal virtual void ReleaseDependencies()
{
}
/// <inheritdoc/>
DownloadStatus IAsyncOperation.GetDownloadStatus(HashSet<object> visited) => GetDownloadStatus(visited);
internal virtual DownloadStatus GetDownloadStatus(HashSet<object> visited)
{
visited.Add(this);
return new DownloadStatus() {IsDone = IsDone};
}
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: f9fa130dc1a674f38a215c4f52e2b420
timeCreated: 1503083765
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,609 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.ResourceManagement.AsyncOperations
{
/// <summary>
/// Handle for internal operations. This allows for reference counting and checking for valid references.
/// </summary>
/// <typeparam name="TObject">The object type of the underlying operation.</typeparam>
public struct AsyncOperationHandle<TObject> : IEnumerator, IEquatable<AsyncOperationHandle<TObject>>
{
internal AsyncOperationBase<TObject> m_InternalOp;
int m_Version;
string m_LocationName;
internal string LocationName
{
get { return m_LocationName; }
set { m_LocationName = value; }
}
bool m_UnloadSceneOpExcludeReleaseCallback;
internal bool UnloadSceneOpExcludeReleaseCallback
{
get { return m_UnloadSceneOpExcludeReleaseCallback; }
set { m_UnloadSceneOpExcludeReleaseCallback = value; }
}
/// <summary>
/// Conversion from typed to non typed handles. This does not increment the reference count.
/// To convert from non-typed back, use AsyncOperationHandle.Convert&lt;T&gt;()
/// </summary>
/// <param name="obj">The typed handle to convert.</param>
/// <returns>Returns the converted operation handle.</returns>
static public implicit operator AsyncOperationHandle(AsyncOperationHandle<TObject> obj)
{
return new AsyncOperationHandle(obj.m_InternalOp, obj.m_Version, obj.m_LocationName);
}
internal AsyncOperationHandle(AsyncOperationBase<TObject> op)
{
m_InternalOp = op;
m_Version = op?.Version ?? 0;
m_LocationName = null;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
/// <summary>
/// Return the current download status for this operation and its dependencies.
/// </summary>
/// <returns>The download status.</returns>
public DownloadStatus GetDownloadStatus()
{
return InternalGetDownloadStatus(new HashSet<object>());
}
internal DownloadStatus InternalGetDownloadStatus(HashSet<object> visited)
{
if (visited == null)
visited = new HashSet<object>();
return visited.Add(InternalOp) ? InternalOp.GetDownloadStatus(visited) : new DownloadStatus() {IsDone = IsDone};
}
internal AsyncOperationHandle(IAsyncOperation op)
{
m_InternalOp = (AsyncOperationBase<TObject>)op;
m_Version = op?.Version ?? 0;
m_LocationName = null;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
internal AsyncOperationHandle(IAsyncOperation op, int version)
{
m_InternalOp = (AsyncOperationBase<TObject>)op;
m_Version = version;
m_LocationName = null;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
internal AsyncOperationHandle(IAsyncOperation op, string locationName)
{
m_InternalOp = (AsyncOperationBase<TObject>)op;
m_Version = op?.Version ?? 0;
m_LocationName = locationName;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
internal AsyncOperationHandle(IAsyncOperation op, int version, string locationName)
{
m_InternalOp = (AsyncOperationBase<TObject>)op;
m_Version = version;
m_LocationName = locationName;
m_UnloadSceneOpExcludeReleaseCallback = false;
}
/// <summary>
/// Acquire a new handle to the internal operation. This will increment the reference count, therefore the returned handle must also be released.
/// </summary>
/// <returns>A new handle to the operation. This handle must also be released.</returns>
internal AsyncOperationHandle<TObject> Acquire()
{
InternalOp.IncrementReferenceCount();
return this;
}
/// <summary>
/// Completion event for the internal operation. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
/// </summary>
public event Action<AsyncOperationHandle<TObject>> Completed
{
add { InternalOp.Completed += value; }
remove { InternalOp.Completed -= value; }
}
/// <summary>
/// Completion event for non-typed callback handlers. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
/// </summary>
public event Action<AsyncOperationHandle> CompletedTypeless
{
add { InternalOp.CompletedTypeless += value; }
remove { InternalOp.CompletedTypeless -= value; }
}
/// <summary>
/// Debug name of the operation.
/// </summary>
public string DebugName
{
get
{
if (!IsValid())
return "InvalidHandle";
return ((IAsyncOperation)InternalOp).DebugName;
}
}
/// <summary>
/// Get dependency operations.
/// </summary>
/// <param name="deps">The list of AsyncOperationHandles that are dependencies of a given AsyncOperationHandle</param>
public void GetDependencies(List<AsyncOperationHandle> deps)
{
InternalOp.GetDependencies(deps);
}
/// <summary>
/// Event for handling the destruction of the operation.
/// </summary>
public event Action<AsyncOperationHandle> Destroyed
{
add { InternalOp.Destroyed += value; }
remove { InternalOp.Destroyed -= value; }
}
/// <summary>
/// Provide equality for this struct.
/// </summary>
/// <param name="other">The operation to compare to.</param>
/// <returns>True if the the operation handles reference the same AsyncOperation and the version is the same.</returns>
public bool Equals(AsyncOperationHandle<TObject> other)
{
return m_Version == other.m_Version && m_InternalOp == other.m_InternalOp;
}
/// <summary>
/// Get hash code of this struct.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return m_InternalOp == null ? 0 : m_InternalOp.GetHashCode() * 17 + m_Version;
}
/// <summary>
/// Synchronously complete the async operation.
/// </summary>
/// <returns>The result of the operation or null.</returns>
public TObject WaitForCompletion()
{
#if !UNITY_2021_1_OR_NEWER
AsyncOperationHandle.IsWaitingForCompletion = true;
try
{
if (IsValid() && !InternalOp.IsDone)
InternalOp.WaitForCompletion();
if (IsValid())
return Result;
}
finally
{
AsyncOperationHandle.IsWaitingForCompletion = false;
m_InternalOp?.m_RM?.Update(Time.unscaledDeltaTime);
}
#else
if (IsValid() && !InternalOp.IsDone)
InternalOp.WaitForCompletion();
m_InternalOp?.m_RM?.Update(Time.unscaledDeltaTime);
if (IsValid())
return Result;
#endif
return default(TObject);
}
internal AsyncOperationBase<TObject> InternalOp
{
get
{
if (m_InternalOp == null || m_InternalOp.Version != m_Version)
throw new Exception("Attempting to use an invalid operation handle");
return m_InternalOp;
}
}
/// <summary>
/// True if the operation is complete.
/// </summary>
public bool IsDone
{
get { return !IsValid() || InternalOp.IsDone; }
}
/// <summary>
/// Check if the handle references an internal operation.
/// </summary>
/// <returns>True if valid.</returns>
public bool IsValid()
{
return m_InternalOp != null && m_InternalOp.Version == m_Version;
}
/// <summary>
/// The exception for a failed operation. This will be null unless Status is failed.
/// </summary>
public Exception OperationException
{
get { return InternalOp.OperationException; }
}
/// <summary>
/// The progress of the internal operation.
/// This is evenly weighted between all sub-operations. For example, a LoadAssetAsync call could potentially
/// be chained with InitializeAsync and have multiple dependent operations that download and load content.
/// In that scenario, PercentComplete would reflect how far the overal operation was, and would not accurately
/// represent just percent downloaded or percent loaded into memory.
/// For accurate download percentages, use GetDownloadStatus().
/// </summary>
public float PercentComplete
{
get { return InternalOp.PercentComplete; }
}
/// <summary>
/// The current reference count of the internal operation.
/// </summary>
internal int ReferenceCount
{
get { return InternalOp.ReferenceCount; }
}
/// <summary>
/// Release the handle. If the internal operation reference count reaches 0, the resource will be released.
/// </summary>
internal void Release()
{
InternalOp.DecrementReferenceCount();
m_InternalOp = null;
}
/// <summary>
/// The result object of the operations.
/// </summary>
public TObject Result
{
get { return InternalOp.Result; }
}
/// <summary>
/// The status of the internal operation.
/// </summary>
public AsyncOperationStatus Status
{
get { return InternalOp.Status; }
}
/// <summary>
/// Return a Task object to wait on when using async await.
/// </summary>
public System.Threading.Tasks.Task<TObject> Task
{
get { return InternalOp.Task; }
}
object IEnumerator.Current
{
get { return Result; }
}
/// <summary>
/// Overload for <see cref="IEnumerator.MoveNext"/>.
/// </summary>
/// <returns>Returns true if the enumerator can advance to the next element in the collectin. Returns false otherwise.</returns>
bool IEnumerator.MoveNext()
{
return !IsDone;
}
/// <summary>
/// Overload for <see cref="IEnumerator.Reset"/>.
/// </summary>
void IEnumerator.Reset()
{
}
}
/// <summary>
/// Non typed operation handle. This allows for reference counting and checking for valid references.
/// </summary>
public struct AsyncOperationHandle : IEnumerator
{
#if !UNITY_2021_1_OR_NEWER
private static bool m_IsWaitingForCompletion = false;
/// <summary>
/// Indicates that the async operation is in the process of being completed synchronously.
/// </summary>
public static bool IsWaitingForCompletion
{
get { return m_IsWaitingForCompletion; }
internal set { m_IsWaitingForCompletion = value; }
}
#endif
internal IAsyncOperation m_InternalOp;
int m_Version;
string m_LocationName;
internal string LocationName
{
get { return m_LocationName; }
set { m_LocationName = value; }
}
internal AsyncOperationHandle(IAsyncOperation op)
{
m_InternalOp = op;
m_Version = op?.Version ?? 0;
m_LocationName = null;
}
internal AsyncOperationHandle(IAsyncOperation op, int version)
{
m_InternalOp = op;
m_Version = version;
m_LocationName = null;
}
internal AsyncOperationHandle(IAsyncOperation op, string locationName)
{
m_InternalOp = op;
m_Version = op?.Version ?? 0;
m_LocationName = locationName;
}
internal AsyncOperationHandle(IAsyncOperation op, int version, string locationName)
{
m_InternalOp = op;
m_Version = version;
m_LocationName = locationName;
}
/// <summary>
/// Acquire a new handle to the internal operation. This will increment the reference count, therefore the returned handle must also be released.
/// </summary>
/// <returns>A new handle to the operation. This handle must also be released.</returns>
internal AsyncOperationHandle Acquire()
{
InternalOp.IncrementReferenceCount();
return this;
}
/// <summary>
/// Completion event for the internal operation. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
/// </summary>
public event Action<AsyncOperationHandle> Completed
{
add { InternalOp.CompletedTypeless += value; }
remove { InternalOp.CompletedTypeless -= value; }
}
/// <summary>
/// Converts handle to be typed. This does not increment the reference count.
/// To convert back to non-typed, implicit conversion is available.
/// </summary>
/// <typeparam name="T">The type of the handle.</typeparam>
/// <returns>A new handle that is typed.</returns>
public AsyncOperationHandle<T> Convert<T>()
{
return new AsyncOperationHandle<T>(InternalOp, m_Version, m_LocationName);
}
/// <summary>
/// Provide equality for this struct.
/// </summary>
/// <param name="other">The operation to compare to.</param>
/// <returns>True if the the operation handles reference the same AsyncOperation and the version is the same.</returns>
public bool Equals(AsyncOperationHandle other)
{
return m_Version == other.m_Version && m_InternalOp == other.m_InternalOp;
}
/// <summary>
/// Debug name of the operation.
/// </summary>
public string DebugName
{
get
{
if (!IsValid())
return "InvalidHandle";
return InternalOp.DebugName;
}
}
/// <summary>
/// Event for handling the destruction of the operation.
/// </summary>
public event Action<AsyncOperationHandle> Destroyed
{
add { InternalOp.Destroyed += value; }
remove { InternalOp.Destroyed -= value; }
}
/// <summary>
/// Get dependency operations.
/// </summary>
/// <param name="deps"></param>
public void GetDependencies(List<AsyncOperationHandle> deps)
{
InternalOp.GetDependencies(deps);
}
/// <summary>
/// Get hash code of this struct.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return m_InternalOp == null ? 0 : m_InternalOp.GetHashCode() * 17 + m_Version;
}
IAsyncOperation InternalOp
{
get
{
if (m_InternalOp == null || m_InternalOp.Version != m_Version)
throw new Exception("Attempting to use an invalid operation handle");
return m_InternalOp;
}
}
/// <summary>
/// True if the operation is complete.
/// </summary>
public bool IsDone
{
get { return !IsValid() || InternalOp.IsDone; }
}
/// <summary>
/// Check if the internal operation is not null and has the same version of this handle.
/// </summary>
/// <returns>True if valid.</returns>
public bool IsValid()
{
return m_InternalOp != null && m_InternalOp.Version == m_Version;
}
/// <summary>
/// The exception for a failed operation. This will be null unless Status is failed.
/// </summary>
public Exception OperationException
{
get { return InternalOp.OperationException; }
}
/// <summary>
/// The progress of the internal operation.
/// This is evenly weighted between all sub-operations. For example, a LoadAssetAsync call could potentially
/// be chained with InitializeAsync and have multiple dependent operations that download and load content.
/// In that scenario, PercentComplete would reflect how far the overal operation was, and would not accurately
/// represent just percent downloaded or percent loaded into memory.
/// For accurate download percentages, use GetDownloadStatus().
/// </summary>
public float PercentComplete
{
get { return InternalOp.PercentComplete; }
}
/// <summary>
/// Return the current download status for this operation and its dependencies. In some instances, the information will not be available. This can happen if the operation
/// is dependent on the initialization operation for addressables. Once the initialization operation completes, the information returned will be accurate.
/// </summary>
/// <returns>The download status.</returns>
public DownloadStatus GetDownloadStatus()
{
return InternalGetDownloadStatus(new HashSet<object>());
}
internal DownloadStatus InternalGetDownloadStatus(HashSet<object> visited)
{
if (visited == null)
visited = new HashSet<object>();
return visited.Add(InternalOp) ? InternalOp.GetDownloadStatus(visited) : new DownloadStatus() {IsDone = IsDone};
}
/// <summary>
/// The current reference count of the internal operation.
/// </summary>
internal int ReferenceCount
{
get { return InternalOp.ReferenceCount; }
}
/// <summary>
/// Release the handle. If the internal operation reference count reaches 0, the resource will be released.
/// </summary>
internal void Release()
{
InternalOp.DecrementReferenceCount();
m_InternalOp = null;
}
/// <summary>
/// The result object of the operations.
/// </summary>
public object Result
{
get { return InternalOp.GetResultAsObject(); }
}
/// <summary>
/// The status of the internal operation.
/// </summary>
public AsyncOperationStatus Status
{
get { return InternalOp.Status; }
}
/// <summary>
/// Return a Task object to wait on when using async await.
/// </summary>
public System.Threading.Tasks.Task<object> Task
{
get { return InternalOp.Task; }
}
object IEnumerator.Current
{
get { return Result; }
}
/// <summary>
/// Overload for <see cref="IEnumerator.MoveNext"/>.
/// </summary>
/// <returns>Returns true if the enumerator can advance to the next element in the collectin. Returns false otherwise.</returns>
bool IEnumerator.MoveNext()
{
return !IsDone;
}
/// <summary>
/// Overload for <see cref="IEnumerator.Reset"/>.
/// </summary>
void IEnumerator.Reset()
{
}
/// <summary>
/// Synchronously complete the async operation.
/// </summary>
/// <returns>The result of the operation or null.</returns>
public object WaitForCompletion()
{
#if !UNITY_2021_1_OR_NEWER
IsWaitingForCompletion = true;
try
{
if (IsValid() && !InternalOp.IsDone)
InternalOp.WaitForCompletion();
if (IsValid())
return Result;
}
finally
{
IsWaitingForCompletion = false;
}
#else
if (IsValid() && !InternalOp.IsDone)
InternalOp.WaitForCompletion();
if (IsValid())
return Result;
#endif
return null;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3b0971a8e5dab44a959bfe453348846
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,28 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace UnityEngine.ResourceManagement.AsyncOperations
{
/// <summary>
/// Options for IAsyncOperations status values
/// </summary>
public enum AsyncOperationStatus
{
/// <summary>
/// Use to indicate that the operation is still in progress.
/// </summary>
None,
/// <summary>
/// Use to indicate that the operation succeeded.
/// </summary>
Succeeded,
/// <summary>
/// Use to indicate that the operation failed.
/// </summary>
Failed
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f2b227c56ee439f46b08efa638b27a84
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,258 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement
{
class ChainOperation<TObject, TObjectDependency> : AsyncOperationBase<TObject>
{
AsyncOperationHandle<TObjectDependency> m_DepOp;
AsyncOperationHandle<TObject> m_WrappedOp;
DownloadStatus m_depStatus = default;
DownloadStatus m_wrapStatus = default;
Func<AsyncOperationHandle<TObjectDependency>, AsyncOperationHandle<TObject>> m_Callback;
Action<AsyncOperationHandle<TObject>> m_CachedOnWrappedCompleted;
bool m_ReleaseDependenciesOnFailure = true;
public ChainOperation()
{
m_CachedOnWrappedCompleted = OnWrappedCompleted;
}
protected override string DebugName
{
get { return $"ChainOperation<{typeof(TObject).Name},{typeof(TObjectDependency).Name}> - {m_DepOp.DebugName}"; }
}
/// <inheritdoc />
public override void GetDependencies(List<AsyncOperationHandle> deps)
{
if (m_DepOp.IsValid())
deps.Add(m_DepOp);
}
public void Init(AsyncOperationHandle<TObjectDependency> dependentOp, Func<AsyncOperationHandle<TObjectDependency>, AsyncOperationHandle<TObject>> callback, bool releaseDependenciesOnFailure)
{
m_DepOp = dependentOp;
m_DepOp.Acquire();
m_Callback = callback;
m_ReleaseDependenciesOnFailure = releaseDependenciesOnFailure;
RefreshDownloadStatus();
}
///<inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (IsDone)
return true;
if (!m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
if (!m_WrappedOp.IsValid())
return m_WrappedOp.IsDone;
m_WrappedOp.WaitForCompletion();
return m_WrappedOp.IsDone;
}
protected override void Execute()
{
m_WrappedOp = m_Callback(m_DepOp);
m_WrappedOp.Completed += m_CachedOnWrappedCompleted;
m_Callback = null;
}
private void OnWrappedCompleted(AsyncOperationHandle<TObject> x)
{
OperationException ex = null;
if (x.Status == AsyncOperationStatus.Failed)
ex = new OperationException($"ChainOperation failed because dependent operation failed", x.OperationException);
Complete(m_WrappedOp.Result, x.Status == AsyncOperationStatus.Succeeded, ex, m_ReleaseDependenciesOnFailure);
}
protected override void Destroy()
{
if (m_WrappedOp.IsValid())
m_WrappedOp.Release();
if (m_DepOp.IsValid())
m_DepOp.Release();
}
internal override void ReleaseDependencies()
{
if (m_DepOp.IsValid())
m_DepOp.Release();
}
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
{
RefreshDownloadStatus(visited);
return new DownloadStatus() {DownloadedBytes = m_depStatus.DownloadedBytes + m_wrapStatus.DownloadedBytes, TotalBytes = m_depStatus.TotalBytes + m_wrapStatus.TotalBytes, IsDone = IsDone};
}
void RefreshDownloadStatus(HashSet<object> visited = default)
{
m_depStatus = m_DepOp.IsValid() ? m_DepOp.InternalGetDownloadStatus(visited) : m_depStatus;
m_wrapStatus = m_WrappedOp.IsValid() ? m_WrappedOp.InternalGetDownloadStatus(visited) : m_wrapStatus;
}
protected override float Progress
{
get
{
DownloadStatus downloadStatus = GetDownloadStatus(new HashSet<object>());
if (!downloadStatus.IsDone && downloadStatus.DownloadedBytes == 0)
return 0.0f;
float total = 0f;
int numberOfOps = 2;
if (m_DepOp.IsValid())
total += m_DepOp.PercentComplete;
else
total++;
if (m_WrappedOp.IsValid())
total += m_WrappedOp.PercentComplete;
else
total++;
return total / numberOfOps;
}
}
}
class ChainOperationTypelessDepedency<TObject> : AsyncOperationBase<TObject>
{
AsyncOperationHandle m_DepOp;
AsyncOperationHandle<TObject> m_WrappedOp;
DownloadStatus m_depStatus = default;
DownloadStatus m_wrapStatus = default;
Func<AsyncOperationHandle, AsyncOperationHandle<TObject>> m_Callback;
Action<AsyncOperationHandle<TObject>> m_CachedOnWrappedCompleted;
bool m_ReleaseDependenciesOnFailure = true;
internal AsyncOperationHandle<TObject> WrappedOp => m_WrappedOp;
public ChainOperationTypelessDepedency()
{
m_CachedOnWrappedCompleted = OnWrappedCompleted;
}
protected override string DebugName
{
get { return $"ChainOperation<{typeof(TObject).Name}> - {m_DepOp.DebugName}"; }
}
/// <inheritdoc />
public override void GetDependencies(List<AsyncOperationHandle> deps)
{
if (m_DepOp.IsValid())
deps.Add(m_DepOp);
}
public void Init(AsyncOperationHandle dependentOp, Func<AsyncOperationHandle, AsyncOperationHandle<TObject>> callback, bool releaseDependenciesOnFailure)
{
m_DepOp = dependentOp;
m_DepOp.Acquire();
m_Callback = callback;
m_ReleaseDependenciesOnFailure = releaseDependenciesOnFailure;
RefreshDownloadStatus();
}
///<inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (IsDone)
return true;
if (!m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
if (!m_WrappedOp.IsValid())
return m_WrappedOp.IsDone;
Result = m_WrappedOp.WaitForCompletion();
return true;
}
protected override void Execute()
{
m_WrappedOp = m_Callback(m_DepOp);
m_WrappedOp.Completed += m_CachedOnWrappedCompleted;
m_Callback = null;
}
private void OnWrappedCompleted(AsyncOperationHandle<TObject> x)
{
OperationException ex = null;
if (x.Status == AsyncOperationStatus.Failed)
ex = new OperationException($"ChainOperation failed because dependent operation failed", x.OperationException);
Complete(m_WrappedOp.Result, x.Status == AsyncOperationStatus.Succeeded, ex, m_ReleaseDependenciesOnFailure);
}
protected override void Destroy()
{
if (m_WrappedOp.IsValid())
m_WrappedOp.Release();
if (m_DepOp.IsValid())
m_DepOp.Release();
}
internal override void ReleaseDependencies()
{
if (m_DepOp.IsValid())
m_DepOp.Release();
}
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
{
RefreshDownloadStatus(visited);
return new DownloadStatus() {DownloadedBytes = m_depStatus.DownloadedBytes + m_wrapStatus.DownloadedBytes, TotalBytes = m_depStatus.TotalBytes + m_wrapStatus.TotalBytes, IsDone = IsDone};
}
void RefreshDownloadStatus(HashSet<object> visited = default)
{
m_depStatus = m_DepOp.IsValid() ? m_DepOp.InternalGetDownloadStatus(visited) : m_depStatus;
m_wrapStatus = m_WrappedOp.IsValid() ? m_WrappedOp.InternalGetDownloadStatus(visited) : m_wrapStatus;
}
protected override float Progress
{
get
{
DownloadStatus downloadStatus = GetDownloadStatus(new HashSet<object>());
if (!downloadStatus.IsDone && downloadStatus.DownloadedBytes == 0)
return 0.0f;
float total = 0f;
int numberOfOps = 2;
if (m_DepOp.IsValid())
total += m_DepOp.PercentComplete;
else
total++;
if (m_WrappedOp.IsValid())
total += m_WrappedOp.PercentComplete;
else
total++;
return total / numberOfOps;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d148220e8d54054e851e5c220feea5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace UnityEngine.ResourceManagement.AsyncOperations
{
/// <summary>
/// Contains download information for async operations.
/// </summary>
public struct DownloadStatus
{
/// <summary>
/// The number of bytes downloaded by the operation and all of its dependencies.
/// </summary>
public long TotalBytes;
/// <summary>
/// The total number of bytes needed to download by the operation and dependencies.
/// </summary>
public long DownloadedBytes;
/// <summary>
/// Is the operation completed. This is used to determine if the computed Percent should be 0 or 1 when TotalBytes is 0.
/// </summary>
public bool IsDone;
/// <summary>
/// Returns the computed percent complete as a float value between 0 &amp; 1. If TotalBytes == 0, 1 is returned.
/// </summary>
public float Percent => (TotalBytes > 0) ? ((float)DownloadedBytes / (float)TotalBytes) : (IsDone ? 1.0f : 0f);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: faf32c22a012a554c8e6e71e64844c23
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,231 @@
using System;
using System.Collections.Generic;
using System.Security;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.AsyncOperations
{
class GroupOperation : AsyncOperationBase<IList<AsyncOperationHandle>>, ICachable
{
[Flags]
public enum GroupOperationSettings
{
None = 0,
ReleaseDependenciesOnFailure = 1,
AllowFailedDependencies = 2
}
Action<AsyncOperationHandle> m_InternalOnComplete;
int m_LoadedCount;
GroupOperationSettings m_Settings;
string debugName = null;
private const int k_MaxDisplayedLocationLength = 45;
public GroupOperation()
{
m_InternalOnComplete = OnOperationCompleted;
Result = new List<AsyncOperationHandle>();
}
///<inheritdoc />
protected override bool InvokeWaitForCompletion()
{
//If Result is null then we've auto released and need to return
if (IsDone || Result == null)
return true;
foreach (var r in Result)
{
r.WaitForCompletion();
if (Result == null)
return true;
}
m_RM?.Update(Time.unscaledDeltaTime);
if (!IsDone && Result != null)
Execute();
m_RM?.Update(Time.unscaledDeltaTime);
return IsDone;
}
IOperationCacheKey ICachable.Key { get; set; }
internal IList<AsyncOperationHandle> GetDependentOps()
{
return Result;
}
/// <inheritdoc />
public override void GetDependencies(List<AsyncOperationHandle> deps)
{
deps.AddRange(Result);
}
internal override void ReleaseDependencies()
{
for (int i = 0; i < Result.Count; i++)
if (Result[i].IsValid())
Result[i].Release();
Result.Clear();
}
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
{
var status = new DownloadStatus() {IsDone = IsDone};
for (int i = 0; i < Result.Count; i++)
{
if (Result[i].IsValid())
{
var depStatus = Result[i].InternalGetDownloadStatus(visited);
status.DownloadedBytes += depStatus.DownloadedBytes;
status.TotalBytes += depStatus.TotalBytes;
}
}
return status;
}
HashSet<string> m_CachedDependencyLocations = new HashSet<string>();
private bool DependenciesAreUnchanged(List<AsyncOperationHandle> deps)
{
if (m_CachedDependencyLocations.Count != deps.Count) return false;
foreach (var d in deps)
if (!m_CachedDependencyLocations.Contains(d.LocationName))
return false;
return true;
}
protected override string DebugName
{
get
{
List<AsyncOperationHandle> deps = new List<AsyncOperationHandle>();
GetDependencies(deps);
if (deps.Count == 0)
return "Dependencies";
//Only recalculate DebugName if a name hasn't been generated for currently held dependencies
if (debugName != null && DependenciesAreUnchanged(deps))
return debugName;
m_CachedDependencyLocations.Clear();
string toBeDisplayed = "Dependencies [";
for (var i = 0; i < deps.Count; i++)
{
var d = deps[i];
var locationString = d.LocationName;
m_CachedDependencyLocations.Add(locationString);
if (locationString == null)
continue;
//Prevent location display from being excessively long
if (locationString.Length > k_MaxDisplayedLocationLength)
{
locationString = AsyncOperationBase<object>.ShortenPath(locationString, true);
locationString = locationString.Substring(0, Math.Min(k_MaxDisplayedLocationLength, locationString.Length)) + "...";
}
if (i == deps.Count - 1)
toBeDisplayed += locationString;
else
toBeDisplayed += locationString + ", ";
}
toBeDisplayed += "]";
debugName = toBeDisplayed;
return debugName;
}
}
protected override void Execute()
{
m_LoadedCount = 0;
for (int i = 0; i < Result.Count; i++)
{
if (Result[i].IsDone)
m_LoadedCount++;
else
Result[i].Completed += m_InternalOnComplete;
}
CompleteIfDependenciesComplete();
}
private void CompleteIfDependenciesComplete()
{
if (m_LoadedCount == Result.Count)
{
bool success = true;
OperationException ex = null;
if (!m_Settings.HasFlag(GroupOperationSettings.AllowFailedDependencies))
{
for (int i = 0; i < Result.Count; i++)
{
if (Result[i].Status != AsyncOperationStatus.Succeeded)
{
success = false;
ex = new OperationException("GroupOperation failed because one of its dependencies failed", Result[i].OperationException);
break;
}
}
}
Complete(Result, success, ex, m_Settings.HasFlag(GroupOperationSettings.ReleaseDependenciesOnFailure));
}
}
protected override void Destroy()
{
ReleaseDependencies();
}
protected override float Progress
{
get
{
float total = 0f;
for (int i = 0; i < Result.Count; i++)
{
var handle = Result[i];
if (!handle.IsDone)
total += handle.PercentComplete;
else
total++;
}
return total / Result.Count;
}
}
public void Init(List<AsyncOperationHandle> operations, bool releaseDependenciesOnFailure = true, bool allowFailedDependencies = false)
{
Result = new List<AsyncOperationHandle>(operations);
m_Settings = releaseDependenciesOnFailure ? GroupOperationSettings.ReleaseDependenciesOnFailure : GroupOperationSettings.None;
if (allowFailedDependencies)
m_Settings |= GroupOperationSettings.AllowFailedDependencies;
}
public void Init(List<AsyncOperationHandle> operations, GroupOperationSettings settings)
{
Result = new List<AsyncOperationHandle>(operations);
m_Settings = settings;
}
void OnOperationCompleted(AsyncOperationHandle op)
{
m_LoadedCount++;
CompleteIfDependenciesComplete();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5fa1c5d453431041ab1e49b72e569e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,303 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.AsyncOperations
{
internal interface IGenericProviderOperation
{
void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp);
void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp, bool releaseDependenciesOnFailure);
int ProvideHandleVersion { get; }
IResourceLocation Location { get; }
int DependencyCount { get; }
void GetDependencies(IList<object> dstList);
TDepObject GetDependency<TDepObject>(int index);
void SetProgressCallback(Func<float> callback);
void ProviderCompleted<T>(T result, bool status, Exception e);
Type RequestedType { get; }
void SetDownloadProgressCallback(Func<DownloadStatus> callback);
void SetWaitForCompletionCallback(Func<bool> callback);
}
[UnityEngine.Scripting.Preserve]
internal class ProviderOperation<TObject> : AsyncOperationBase<TObject>, IGenericProviderOperation, ICachable
{
private bool m_ReleaseDependenciesOnFailure = true;
private Action<int, object, bool, Exception> m_CompletionCallback;
private Action<int, IList<object>> m_GetDepCallback;
private Func<float> m_GetProgressCallback;
private Func<DownloadStatus> m_GetDownloadProgressCallback;
private Func<bool> m_WaitForCompletionCallback;
private bool m_ProviderCompletedCalled = false; //This is used to fix a race condition with WaitForCompletion, like in AssetBundleProvider
private DownloadStatus m_DownloadStatus;
private IResourceProvider m_Provider;
internal AsyncOperationHandle<IList<AsyncOperationHandle>> m_DepOp;
private IResourceLocation m_Location;
private int m_ProvideHandleVersion;
private bool m_NeedsRelease;
IOperationCacheKey ICachable.Key { get; set; }
private ResourceManager m_ResourceManager;
private const float k_OperationWaitingToCompletePercentComplete = 0.99f;
public int ProvideHandleVersion
{
get { return m_ProvideHandleVersion; }
}
public IResourceLocation Location
{
get { return m_Location; }
}
public void SetDownloadProgressCallback(Func<DownloadStatus> callback)
{
m_GetDownloadProgressCallback = callback;
if (m_GetDownloadProgressCallback != null)
m_DownloadStatus = m_GetDownloadProgressCallback();
}
public void SetWaitForCompletionCallback(Func<bool> callback)
{
m_WaitForCompletionCallback = callback;
}
///<inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (IsDone || m_ProviderCompletedCalled)
return true;
if (m_DepOp.IsValid() && !m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
if (m_WaitForCompletionCallback == null)
return false;
return m_WaitForCompletionCallback.Invoke();
}
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
{
var depDLS = m_DepOp.IsValid() ? m_DepOp.InternalGetDownloadStatus(visited) : default;
if (m_GetDownloadProgressCallback != null)
m_DownloadStatus = m_GetDownloadProgressCallback();
if (Status == AsyncOperationStatus.Succeeded)
m_DownloadStatus.DownloadedBytes = m_DownloadStatus.TotalBytes;
return new DownloadStatus() {DownloadedBytes = m_DownloadStatus.DownloadedBytes + depDLS.DownloadedBytes, TotalBytes = m_DownloadStatus.TotalBytes + depDLS.TotalBytes, IsDone = IsDone};
}
public ProviderOperation()
{
}
/// <inheritdoc />
public override void GetDependencies(List<AsyncOperationHandle> deps)
{
if (m_DepOp.IsValid())
deps.Add(m_DepOp);
}
internal override void ReleaseDependencies()
{
if (m_DepOp.IsValid())
m_DepOp.Release();
}
protected override string DebugName
{
get { return string.Format("Resource<{0}>({1})", typeof(TObject).Name, m_Location == null ? "Invalid" : ShortenPath(m_Location.InternalId, true)); }
}
internal const string kInvalidHandleMsg = "The ProvideHandle is invalid. After the handle has been completed, it can no longer be used";
public void GetDependencies(IList<object> dstList)
{
dstList.Clear();
if (!m_DepOp.IsValid())
return;
if (m_DepOp.Result == null)
return;
for (int i = 0; i < m_DepOp.Result.Count; i++)
dstList.Add(m_DepOp.Result[i].Result);
}
public Type RequestedType
{
get { return typeof(TObject); }
}
public int DependencyCount
{
get { return (!m_DepOp.IsValid() || m_DepOp.Result == null) ? 0 : m_DepOp.Result.Count; }
}
public TDepObject GetDependency<TDepObject>(int index)
{
if (!m_DepOp.IsValid() || m_DepOp.Result == null)
throw new Exception("Cannot get dependency because no dependencies were available");
return (TDepObject)(m_DepOp.Result[index].Result);
}
public void SetProgressCallback(Func<float> callback)
{
m_GetProgressCallback = callback;
}
public void ProviderCompleted<T>(T result, bool status, Exception e)
{
m_ProvideHandleVersion++;
m_GetProgressCallback = null;
m_GetDownloadProgressCallback = null;
m_WaitForCompletionCallback = null;
m_NeedsRelease = status;
m_ProviderCompletedCalled = true;
ProviderOperation<T> top = this as ProviderOperation<T>;
if (top != null)
{
top.Result = result;
}
else if (result == null && !typeof(TObject).IsValueType)
{
Result = (TObject)(object)null;
}
else if (result != null && typeof(TObject).IsAssignableFrom(result.GetType()))
{
Result = (TObject)(object)result;
}
else
{
string errorMsg = string.Format(
"Provider of type {0} with id {1} has provided a result of type {2} which cannot be converted to requested type {3}. The operation will be marked as failed.",
m_Provider.GetType().ToString(), m_Provider.ProviderId, typeof(T), typeof(TObject));
Complete(Result, false, errorMsg);
throw new Exception(errorMsg);
}
Complete(Result, status, e, m_ReleaseDependenciesOnFailure);
}
protected override float Progress
{
get
{
try
{
float numberOfOps = 1f;
float total = 0f;
if (m_GetProgressCallback != null)
total += m_GetProgressCallback();
if (!m_DepOp.IsValid() || m_DepOp.Result == null || m_DepOp.Result.Count == 0)
{
total++;
numberOfOps++;
}
else
{
foreach (var handle in m_DepOp.Result)
{
total += handle.PercentComplete;
numberOfOps++;
}
}
float result = total / numberOfOps;
//This is done because all AssetBundle operations (m_DepOp.Result) can complete as well as the
//BundledAssetRequest operation (m_GetProgressCallBack) but this overall operation hasn't completed yet.
//Once the operation has a chance to complete we short circut calling into Progress here and just return 1.0f
return Mathf.Min(result, k_OperationWaitingToCompletePercentComplete);
}
catch
{
return 0.0f;
}
}
}
protected override void Execute()
{
Debug.Assert(m_DepOp.IsDone);
if (m_DepOp.IsValid() && m_DepOp.Status == AsyncOperationStatus.Failed && (m_Provider.BehaviourFlags & ProviderBehaviourFlags.CanProvideWithFailedDependencies) == 0)
{
ProviderCompleted(default(TObject), false, new Exception("Dependency Exception", m_DepOp.OperationException));
}
else
{
try
{
m_Provider.Provide(new ProvideHandle(m_ResourceManager, this));
}
catch (Exception e)
{
ProviderCompleted(default(TObject), false, e);
}
}
}
public void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp)
{
m_DownloadStatus = default;
m_ResourceManager = rm;
m_DepOp = depOp;
if (m_DepOp.IsValid())
m_DepOp.Acquire();
m_Provider = provider;
m_Location = location;
m_ReleaseDependenciesOnFailure = true;
m_ProviderCompletedCalled = false;
SetWaitForCompletionCallback(WaitForCompletionHandler);
}
public void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp, bool releaseDependenciesOnFailure)
{
m_DownloadStatus = default;
m_ResourceManager = rm;
m_DepOp = depOp;
if (m_DepOp.IsValid())
m_DepOp.Acquire();
m_Provider = provider;
m_Location = location;
m_ReleaseDependenciesOnFailure = releaseDependenciesOnFailure;
m_ProviderCompletedCalled = false;
SetWaitForCompletionCallback(WaitForCompletionHandler);
}
bool WaitForCompletionHandler()
{
if (IsDone)
return true;
if (!m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
if (!HasExecuted)
InvokeExecute();
return IsDone;
}
protected override void Destroy()
{
if (m_NeedsRelease)
m_Provider.Release(m_Location, Result);
if (m_DepOp.IsValid())
m_DepOp.Release();
Result = default(TObject);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b28e4f74756c9e3418600f517d56e9f2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
namespace UnityEngine.ResourceManagement.AsyncOperations
{
internal class UnityWebRequestOperation : AsyncOperationBase<UnityWebRequest>
{
UnityWebRequest m_UWR;
public UnityWebRequestOperation(UnityWebRequest webRequest)
{
m_UWR = webRequest;
}
protected override void Execute()
{
m_UWR.SendWebRequest().completed += (request) => { Complete(m_UWR, string.IsNullOrEmpty(m_UWR.error), m_UWR.error); };
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d9a6f10d6aba7e40b982e7ed18ecfff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
using System;
#if ENABLE_CCD
/// <summary>
/// CcdManager is a static class used to determine where to point Addressables when loading resources stored in CCD.
/// </summary>
public static class CcdManager
{
/// <summary>
/// Name of the environment that the project should use.
/// </summary>
public static string EnvironmentName { get; set; }
/// <summary>
/// Id of the bucket that the project should use.
/// </summary>
public static string BucketId { get; set; }
/// <summary>
/// Name of the badge the project should use.
/// </summary>
public static string Badge { get; set; }
/// <summary>
/// Determines if the CcdManager has been configured
/// </summary>
/// <returns>True if all fields have been set. False, otherwise.</returns>
public static bool IsConfigured()
{
return !string.IsNullOrEmpty(EnvironmentName) && !string.IsNullOrEmpty(BucketId) && !string.IsNullOrEmpty(Badge);
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c72184d3a723464ca46e7c2406ca704
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ba3ec7deec18d144e848686e9d4e18bf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine.Serialization;
namespace UnityEngine.ResourceManagement.Diagnostics
{
/// <summary>
/// Diagnostic event data.
/// </summary>
[Serializable]
public struct DiagnosticEvent
{
[SerializeField]
string m_Graph; //id of graph definition to use
[SerializeField]
int[] m_Dependencies; //used to nest datagraphs
[SerializeField]
int m_ObjectId; //id of a set of data streams
[SerializeField]
string m_DisplayName;
[SerializeField]
int m_Stream; //data stream
[SerializeField]
int m_Frame; //frame of the event
[SerializeField]
int m_Value; //data value of event
/// <summary>
/// Gets the graph id that this event is intended for
/// </summary>
/// <value>The graph Id</value>
public string Graph
{
get { return m_Graph; }
}
/// <summary>
/// Unique object identifier for this event
/// </summary>
public int ObjectId
{
get { return m_ObjectId; }
}
/// <summary>
/// Display name for event
/// </summary>
public string DisplayName
{
get { return m_DisplayName; }
}
/// <summary>
/// Array of object identifiers that are dependencies for this event
/// </summary>
public int[] Dependencies
{
get { return m_Dependencies; }
}
/// <summary>
/// The stream id for the event. Each graph may display multiple streams of data for the same event Id
/// </summary>
/// <value>Stream Id</value>
public int Stream
{
get { return m_Stream; }
}
/// <summary>
/// The frame that the event occurred
/// </summary>
/// <value>Frame number</value>
public int Frame
{
get { return m_Frame; }
}
/// <summary>
/// The value of the event. This value depends on the event type
/// </summary>
/// <value>Event value</value>
public int Value
{
get { return m_Value; }
}
/// <summary>
/// DiagnosticEvent constructor
/// </summary>
/// <param name="graph">Graph id.</param>
/// <param name="name">Event name.</param>
/// <param name="id">Event id.</param>
/// <param name="stream">Stream index.</param>
/// <param name="frame">Frame number.</param>
/// <param name="value">Event value.</param>
/// <param name="deps">Array of dependency event ids.</param>
public DiagnosticEvent(string graph, string name, int id, int stream, int frame, int value, int[] deps)
{
m_Graph = graph;
m_DisplayName = name;
m_ObjectId = id;
m_Stream = stream;
m_Frame = frame;
m_Value = value;
m_Dependencies = deps;
}
/// <summary>
/// Serializes the event into JSON and then encodes with System.Text.Encoding.ASCII.GetBytes
/// </summary>
/// <returns>Byte array containing serialized version of the event</returns>
internal byte[] Serialize()
{
return Encoding.ASCII.GetBytes(JsonUtility.ToJson(this));
}
/// <summary>
/// Deserializes event from a byte array created by the <see cref="Serialize"/> method
/// </summary>
/// <returns>Deserialized DiagnosticEvent struct</returns>
/// <param name="data">Serialized data</param>
public static DiagnosticEvent Deserialize(byte[] data)
{
return JsonUtility.FromJson<DiagnosticEvent>(Encoding.ASCII.GetString(data));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8262b6bbc428e3147b18b7f7f4527359
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Linq;
// ReSharper disable DelegateSubtraction
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Networking.PlayerConnection;
#endif
using UnityEngine.Networking.PlayerConnection;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.Diagnostics
{
/// <summary>
/// Collects ResourceManager events and passed them on the registered event handlers. In editor play mode, events are passed directly to the ResourceManager profiler window.
/// In player builds, events are sent to the editor via the EditorConnection API.
/// </summary>
public class DiagnosticEventCollectorSingleton : ComponentSingleton<DiagnosticEventCollectorSingleton>
{
static Guid s_editorConnectionGuid;
internal Dictionary<int, DiagnosticEvent> m_CreatedEvents = new Dictionary<int, DiagnosticEvent>();
internal List<DiagnosticEvent> m_UnhandledEvents = new List<DiagnosticEvent>();
internal DelegateList<DiagnosticEvent> s_EventHandlers = DelegateList<DiagnosticEvent>.CreateWithGlobalCache();
/// <summary>
/// The guid used for the PlayerConnect messaging system.
/// </summary>
public static Guid PlayerConnectionGuid
{
get
{
if (s_editorConnectionGuid == Guid.Empty)
s_editorConnectionGuid = new Guid(1, 2, 3, new byte[] {20, 1, 32, 32, 4, 9, 6, 44});
return s_editorConnectionGuid;
}
}
/// <inheritdoc/>
protected override string GetGameObjectName() => "EventCollector";
/// <summary>
/// Register for diagnostic events. If there is no collector, this will fail and return false.
/// </summary>
/// <param name="handler">The handler method action.</param>
/// <param name="register">Register or unregister.</param>
/// <param name="create">If true, the event collector will be created if needed.</param>
/// <returns>True if registered, false if not.</returns>
public static bool RegisterEventHandler(Action<DiagnosticEvent> handler, bool register, bool create)
{
if (register && (create || Exists))
{
Instance.RegisterEventHandler(handler);
return true;
}
if (!register && Exists)
{
Instance.UnregisterEventHandler(handler);
}
return false;
}
internal void RegisterEventHandler(Action<DiagnosticEvent> handler)
{
Debug.Assert(m_UnhandledEvents != null, "DiagnosticEventCollectorSingleton.RegisterEventHandler - s_unhandledEvents == null.");
if (handler == null)
throw new ArgumentNullException("handler");
s_EventHandlers.Add(handler);
//Ensure that events are handled in frame order
var combinedAndSortedList = m_UnhandledEvents.Concat(m_CreatedEvents.Values).OrderBy(evt => evt.Frame);
foreach (var evt in combinedAndSortedList)
handler(evt);
m_UnhandledEvents.Clear();
}
/// <summary>
/// Unregister event hander
/// </summary>
/// <param name="handler">Method or delegate that will handle the events</param>
public void UnregisterEventHandler(Action<DiagnosticEvent> handler)
{
if (handler == null)
throw new ArgumentNullException("handler");
s_EventHandlers.Remove(handler);
}
/// <summary>
/// Send a <see cref="DiagnosticEvent"/> event to all registered handlers
/// </summary>
/// <param name="diagnosticEvent">The event to send</param>
public void PostEvent(DiagnosticEvent diagnosticEvent)
{
if (diagnosticEvent.Stream == (int)ResourceManager.DiagnosticEventType.AsyncOperationCreate && !m_CreatedEvents.ContainsKey(diagnosticEvent.ObjectId))
m_CreatedEvents.Add(diagnosticEvent.ObjectId, diagnosticEvent);
else if (diagnosticEvent.Stream == (int)ResourceManager.DiagnosticEventType.AsyncOperationDestroy)
m_CreatedEvents.Remove(diagnosticEvent.ObjectId);
Debug.Assert(m_UnhandledEvents != null, "DiagnosticEventCollectorSingleton.PostEvent - s_unhandledEvents == null.");
if (s_EventHandlers.Count > 0)
s_EventHandlers.Invoke(diagnosticEvent);
else
m_UnhandledEvents.Add(diagnosticEvent);
}
void Awake()
{
#if !UNITY_EDITOR
RegisterEventHandler((DiagnosticEvent diagnosticEvent) => {PlayerConnection.instance.Send(DiagnosticEventCollectorSingleton.PlayerConnectionGuid, diagnosticEvent.Serialize()); });
#endif
}
float m_lastTickSent = 0;
int m_lastFrame = 0;
float fpsAvg = 30;
void Update()
{
if (s_EventHandlers.Count > 0)
{
var elapsed = Time.realtimeSinceStartup - m_lastTickSent;
if (elapsed > .25f)
{
var fps = (Time.frameCount - m_lastFrame) / elapsed;
m_lastFrame = Time.frameCount;
fpsAvg = (fpsAvg + fps) * .5f;
m_lastTickSent = Time.realtimeSinceStartup;
int heapKB = (int)(UnityEngine.Profiling.Profiler.GetMonoUsedSizeLong() / 1024);
PostEvent(new DiagnosticEvent("FrameCount", "FPS", 2, 1, Time.frameCount, (int)fpsAvg, null));
PostEvent(new DiagnosticEvent("MemoryCount", "MonoHeap", 3, 2, Time.frameCount, heapKB, null));
}
}
}
}
/// <summary>
/// Collects ResourceManager events and passed them on the registered event handlers. In editor play mode, events are passed directly to the ResourceManager profiler window.
/// In player builds, events are sent to the editor via the EditorConnection API.
/// </summary>
public class DiagnosticEventCollector : MonoBehaviour
{
static DiagnosticEventCollector s_Collector;
/// <summary>
/// The guid used for the PlayerConnect messaging system.
/// </summary>
public static Guid PlayerConnectionGuid => DiagnosticEventCollectorSingleton.PlayerConnectionGuid;
/// <summary>
/// Retrieves the global event collector. A new one is created if needed.
/// </summary>
/// <returns>The event collector global instance.</returns>
public static DiagnosticEventCollector FindOrCreateGlobalInstance()
{
if (s_Collector == null)
{
var go = new GameObject("EventCollector", typeof(DiagnosticEventCollector));
s_Collector = go.GetComponent<DiagnosticEventCollector>();
go.hideFlags = HideFlags.DontSave; // HideFlags.HideAndDontSave;
}
return s_Collector;
}
/// <summary>
/// Register for diagnostic events. If there is no collector, this will fail and return false.
/// </summary>
/// <param name="handler">The handler method action.</param>
/// <param name="register">Register or unregister.</param>
/// <param name="create">If true, the event collector will be created if needed.</param>
/// <returns>True if registered, false if not.</returns>
public static bool RegisterEventHandler(Action<DiagnosticEvent> handler, bool register, bool create) => DiagnosticEventCollectorSingleton.RegisterEventHandler(handler, register, create);
/// <summary>
/// Unregister event hander
/// </summary>
/// <param name="handler">Method or delegate that will handle the events</param>
public void UnregisterEventHandler(Action<DiagnosticEvent> handler) => DiagnosticEventCollectorSingleton.Instance.UnregisterEventHandler(handler);
/// <summary>
/// Send a <see cref="DiagnosticEvent"/> event to all registered handlers
/// </summary>
/// <param name="diagnosticEvent">The event to send</param>
public void PostEvent(DiagnosticEvent diagnosticEvent) => DiagnosticEventCollectorSingleton.Instance.PostEvent(diagnosticEvent);
#if UNITY_EDITOR
public static class PlayStateNotifier
{
}
#endif
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 09b260528be79b74ab75d43b23001eab
timeCreated: 1509377634
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e64db96403bd54244820848cd058a3be
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,87 @@
#if ENABLE_ADDRESSABLE_PROFILER
using System;
using System.Runtime.InteropServices;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.Profiling
{
[System.Flags]
internal enum ContentStatus
{
None = 0,
// 1
Queue = 2,
Downloading = 4,
// 8
Released = 16,
// 32
Loading = 64,
// 128
Active = 256,
}
[System.Flags]
internal enum BundleOptions : short
{
None = 0,
CachingEnabled = 1,
CheckSumEnabled = 2
};
[StructLayout(LayoutKind.Sequential)]
internal struct CatalogFrameData
{
public Hash128 BuildResultHash;
}
[StructLayout(LayoutKind.Sequential)]
internal struct BundleFrameData
{
public int BundleCode;
public int ReferenceCount;
public float PercentComplete;
public ContentStatus Status;
public BundleSource Source;
public BundleOptions LoadingOptions;
}
[StructLayout(LayoutKind.Sequential)]
internal struct AssetFrameData
{
public int AssetCode;
public int BundleCode;
public int ReferenceCount;
public float PercentComplete;
public ContentStatus Status;
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (obj is AssetFrameData other)
{
return AssetCode == other.AssetCode &&
BundleCode == other.BundleCode;
}
return false;
}
public override int GetHashCode()
{
#if UNITY_2022_2_OR_NEWER
return HashCode.Combine(AssetCode.GetHashCode(), BundleCode.GetHashCode(), ReferenceCount.GetHashCode(), PercentComplete.GetHashCode(), Status.GetHashCode());
#else
int hash = 17;
hash = hash * 31 + AssetCode.GetHashCode();
hash = hash * 31 + BundleCode.GetHashCode();
hash = hash * 31 + ReferenceCount.GetHashCode();
hash = hash * 31 + PercentComplete.GetHashCode();
hash = hash * 31 + Status.GetHashCode();
return hash;
#endif
}
}
}
#endif

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6b781cca3dbc4b12bd34a2f36633abca
timeCreated: 1665140928

View file

@ -0,0 +1,90 @@
#if ENABLE_ADDRESSABLE_PROFILER
using System;
using System.Collections.Generic;
namespace UnityEngine.ResourceManagement.Profiling
{
internal class ProfilerFrameData<T1,T2>
{
private Dictionary<T1, T2> m_Data = new Dictionary<T1, T2>();
private T2[] m_Array;
private uint m_Version;
private uint m_ArrayVersion;
public bool Add(T1 key, T2 value)
{
bool alreadyExist = m_Data.ContainsKey(key);
m_Data[key] = value;
m_Version++;
return !alreadyExist;
}
internal bool Remove(T1 key)
{
bool removed = m_Data.Remove(key);
if (removed)
m_Version++;
return removed;
}
public T2[] Values
{
get
{
if (m_ArrayVersion == m_Version)
return m_Array ?? Array.Empty<T2>();
m_Array = new T2[m_Data.Count];
m_Data.Values.CopyTo(m_Array, 0);
m_ArrayVersion = m_Version;
return m_Array;
}
}
public T2 this[T1 key]
{
get
{
if (!m_Data.TryGetValue(key, out T2 value))
throw new System.ArgumentOutOfRangeException($"Key {key.ToString()} not found for FrameData");
return value;
}
set
{
if (m_Array != null && m_Data.TryGetValue(key, out T2 oldValue))
{
for (int i = 0; i < m_Array.Length; ++i)
{
if (m_Array[i].Equals(oldValue))
{
m_Array[i] = value;
break;
}
}
}
m_Data[key] = value;
}
}
public bool TryGetValue(T1 key, out T2 value)
{
return m_Data.TryGetValue(key, out value);
}
public bool ContainsKey(T1 key)
{
return m_Data.ContainsKey(key);
}
public IEnumerable<KeyValuePair<T1,T2>> Enumerate()
{
foreach (var pair in m_Data)
{
yield return pair;
}
}
}
}
#endif

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6a881c35c9c8410fa42113c516121903
timeCreated: 1664977838

View file

@ -0,0 +1,290 @@
#if ENABLE_ADDRESSABLE_PROFILER
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Unity.Profiling;
using UnityEngine.Profiling;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.Profiling
{
internal static class ProfilerRuntime
{
public static readonly Guid kResourceManagerProfilerGuid = new Guid("4f8a8c93-7634-4ef7-bbbc-6c9928567fa4");
public const int kCatalogTag = 0;
public const int kBundleDataTag = 1;
public const int kAssetDataTag = 2;
public const int kSceneDataTag = 3;
private static ProfilerCounterValue<int> CatalogLoadCounter = new ProfilerCounterValue<int>(ProfilerCategory.Loading, "Catalogs", ProfilerMarkerDataUnit.Count);
private static ProfilerCounterValue<int> AssetBundleLoadCounter = new ProfilerCounterValue<int>(ProfilerCategory.Loading, "Asset Bundles", ProfilerMarkerDataUnit.Count);
private static ProfilerCounterValue<int> AssetLoadCounter = new ProfilerCounterValue<int>(ProfilerCategory.Loading, "Assets", ProfilerMarkerDataUnit.Count);
private static ProfilerCounterValue<int> SceneLoadCounter = new ProfilerCounterValue<int>(ProfilerCategory.Loading, "Scenes", ProfilerMarkerDataUnit.Count);
private static ProfilerFrameData<Hash128, CatalogFrameData> m_CatalogData = new ProfilerFrameData<Hash128, CatalogFrameData>();
private static ProfilerFrameData<IAsyncOperation, BundleFrameData> m_BundleData = new ProfilerFrameData<IAsyncOperation, BundleFrameData>();
private static ProfilerFrameData<IAsyncOperation, AssetFrameData> m_AssetData = new ProfilerFrameData<IAsyncOperation, AssetFrameData>();
private static ProfilerFrameData<IAsyncOperation, AssetFrameData> m_SceneData = new ProfilerFrameData<IAsyncOperation, AssetFrameData>();
private static Dictionary<string, IAsyncOperation> m_BundleNameToOperation = new Dictionary<string, IAsyncOperation>();
private static Dictionary<string, List<IAsyncOperation>> m_BundleNameToAssetOperations = new Dictionary<string, List<IAsyncOperation>>();
public static void Initialise()
{
CatalogLoadCounter.Value = 0;
AssetBundleLoadCounter.Value = 0;
AssetLoadCounter.Value = 0;
SceneLoadCounter.Value = 0;
MonoBehaviourCallbackHooks.Instance.OnLateUpdateDelegate += InstanceOnOnLateUpdateDelegate;
}
private static void InstanceOnOnLateUpdateDelegate(float deltaTime)
{
PushToProfilerStream();
}
public static void AddCatalog(Hash128 buildHash)
{
if (!buildHash.isValid)
return;
m_CatalogData.Add(buildHash, new CatalogFrameData(){BuildResultHash = buildHash});
CatalogLoadCounter.Value++;
}
public static void AddBundleOperation(ProvideHandle handle, [NotNull] AssetBundleRequestOptions requestOptions, ContentStatus status, BundleSource source)
{
IAsyncOperation op = handle.InternalOp as IAsyncOperation;
if (op == null)
{
string msg = "Could not get Bundle operation for handle loaded for Key " + handle.Location.PrimaryKey;
throw new System.NullReferenceException(msg);
}
string bundleName = requestOptions.BundleName;
BundleOptions loadingOptions = BundleOptions.None;
bool doCRC = requestOptions.Crc != 0;
if (doCRC && source == BundleSource.Cache)
doCRC = requestOptions.UseCrcForCachedBundle;
if (doCRC)
loadingOptions |= BundleOptions.CheckSumEnabled;
if (!string.IsNullOrEmpty(requestOptions.Hash))
loadingOptions |= BundleOptions.CachingEnabled;
BundleFrameData data = new BundleFrameData()
{
ReferenceCount = op.ReferenceCount,
BundleCode = bundleName.GetHashCode(),
Status = status,
LoadingOptions = loadingOptions,
Source = source
};
m_BundleData.Add(op, data);
if (!m_BundleNameToOperation.ContainsKey(bundleName))
AssetBundleLoadCounter.Value += 1;
m_BundleNameToOperation[bundleName] = op;
}
public static void BundleReleased(string bundleName)
{
if (string.IsNullOrEmpty(bundleName) || !m_BundleNameToOperation.TryGetValue(bundleName, out var op))
return;
m_BundleData.Remove(op);
m_BundleNameToOperation.Remove(bundleName);
AssetBundleLoadCounter.Value -= 1;
// remove all the assets from the bundle
if (m_BundleNameToAssetOperations.TryGetValue(bundleName, out var assetOps))
{
m_BundleNameToAssetOperations.Remove(bundleName);
foreach (IAsyncOperation assetOp in assetOps)
{
AssetLoadCounter.Value -= 1;
m_AssetData.Remove(assetOp);
}
}
}
public static void AddAssetOperation(ProvideHandle handle, ContentStatus status)
{
if (!handle.IsValid)
throw new ArgumentException("Attempting to add a Asset handle to profiler that is not valid");
IAsyncOperation assetLoadOperation = handle.InternalOp as IAsyncOperation;
if (assetLoadOperation == null)
throw new NullReferenceException("Could not get operation for InternalOp of handle loaded with primary key: " + handle.Location.PrimaryKey);
string containingBundleName = GetContainingBundleNameForLocation(handle.Location);
string assetId;
if (handle.Location.InternalId.EndsWith(']'))
{
int start = handle.Location.InternalId.IndexOf('[');
assetId = handle.Location.InternalId.Remove(start);
}
else
assetId = handle.Location.InternalId;
AssetFrameData profileObject = new AssetFrameData();
profileObject.AssetCode = assetId.GetHashCode();
profileObject.ReferenceCount = assetLoadOperation.ReferenceCount;
profileObject.BundleCode = containingBundleName.GetHashCode();
profileObject.Status = status;
if (m_BundleNameToAssetOperations.TryGetValue(containingBundleName, out List<IAsyncOperation> assetOperations))
{
if (!assetOperations.Contains(assetLoadOperation))
assetOperations.Add(assetLoadOperation);
}
else
m_BundleNameToAssetOperations.Add(containingBundleName, new List<IAsyncOperation>(){assetLoadOperation});
if (m_AssetData.Add(assetLoadOperation, profileObject))
AssetLoadCounter.Value += 1;
}
private static string GetContainingBundleNameForLocation(IResourceLocation location)
{
if (location.Dependencies.Count == 0)
{
// AssetDatabase mode has no dependencies
return "";
}
AssetBundleRequestOptions options = location.Dependencies[0].Data as AssetBundleRequestOptions;
if (options == null)
{
Debug.LogError($"Dependency bundle location does not have AssetBundleRequestOptions");
return "";
}
return options.BundleName;
}
public static void AddSceneOperation(AsyncOperationHandle<SceneInstance> handle, IResourceLocation location, ContentStatus status)
{
IAsyncOperation sceneLoadOperation = handle.InternalOp as IAsyncOperation;
Debug.Assert(sceneLoadOperation != null, "Could not get operation for " + location.PrimaryKey);
string containingBundleName = GetContainingBundleNameForLocation(location);
AssetFrameData profileObject = new AssetFrameData();
profileObject.AssetCode = location.InternalId.GetHashCode();
profileObject.ReferenceCount = sceneLoadOperation.ReferenceCount;
profileObject.BundleCode = containingBundleName.GetHashCode();
profileObject.Status = status;
if (m_SceneData.Add(sceneLoadOperation, profileObject))
SceneLoadCounter.Value += 1;
}
public static void SceneReleased(AsyncOperationHandle<SceneInstance> handle)
{
if (handle.InternalOp is ChainOperationTypelessDepedency<SceneInstance> chainOp)
{
if (m_SceneData.Remove(chainOp.WrappedOp.InternalOp))
SceneLoadCounter.Value -= 1;
else
Debug.LogWarning($"Failed to remove scene from Addressables profiler for " + chainOp.WrappedOp.DebugName);
}
else
{
if (m_SceneData.Remove(handle.InternalOp))
SceneLoadCounter.Value -= 1;
else
Debug.LogWarning($"Failed to remove scene from Addressables profiler for " + handle.DebugName);
}
}
private static void PushToProfilerStream()
{
RefreshChangedReferenceCounts();
Profiler.EmitFrameMetaData(kResourceManagerProfilerGuid, kCatalogTag, m_CatalogData.Values);
Profiler.EmitFrameMetaData(kResourceManagerProfilerGuid, kBundleDataTag, m_BundleData.Values);
Profiler.EmitFrameMetaData(kResourceManagerProfilerGuid, kAssetDataTag, m_AssetData.Values);
Profiler.EmitFrameMetaData(kResourceManagerProfilerGuid, kSceneDataTag, m_SceneData.Values);
}
private static void RefreshChangedReferenceCounts()
{
Dictionary<IAsyncOperation, (int, float)> dataToChange = new Dictionary<IAsyncOperation, (int, float)>();
foreach (KeyValuePair<IAsyncOperation, BundleFrameData> pair in m_BundleData.Enumerate())
{
if (ShouldUpdateFrameDataWithOperationData(pair.Key, pair.Value.ReferenceCount, pair.Value.PercentComplete, out (int, float) newValues))
dataToChange.Add(pair.Key, newValues);
}
foreach (KeyValuePair<IAsyncOperation, (int, float)> pair in dataToChange)
{
var temp = m_BundleData[pair.Key];
temp.ReferenceCount = pair.Value.Item1;
temp.PercentComplete = pair.Value.Item2;
m_BundleData[pair.Key] = temp;
}
dataToChange.Clear();
foreach (KeyValuePair<IAsyncOperation,AssetFrameData> pair in m_AssetData.Enumerate())
{
if (ShouldUpdateFrameDataWithOperationData(pair.Key, pair.Value.ReferenceCount, pair.Value.PercentComplete, out (int, float) newValues))
dataToChange.Add(pair.Key, newValues);
}
foreach (KeyValuePair<IAsyncOperation, (int, float)> pair in dataToChange)
{
var temp = m_AssetData[pair.Key];
temp.ReferenceCount = pair.Value.Item1;
temp.PercentComplete = pair.Value.Item2;
m_AssetData[pair.Key] = temp;
}
dataToChange.Clear();
foreach (KeyValuePair<IAsyncOperation,AssetFrameData> pair in m_SceneData.Enumerate())
{
if (ShouldUpdateFrameDataWithOperationData(pair.Key, pair.Value.ReferenceCount, pair.Value.PercentComplete, out (int, float) newValues))
dataToChange.Add(pair.Key, newValues);
}
foreach (KeyValuePair<IAsyncOperation, (int, float)> pair in dataToChange)
{
var temp = m_SceneData[pair.Key];
temp.ReferenceCount = pair.Value.Item1;
temp.PercentComplete = pair.Value.Item2;
m_SceneData[pair.Key] = temp;
}
}
// Because the ProfilerFrameData keeps track of both a dictionary and array, and not updated often,
// check if done on if to update the collection
private static bool ShouldUpdateFrameDataWithOperationData(IAsyncOperation activeOperation, int frameReferenceCount, float framePercentComplete, out (int, float) newDataOut)
{
int currentReferenceCount = activeOperation.ReferenceCount;
switch (activeOperation.Status)
{
case AsyncOperationStatus.Succeeded:
break;
case AsyncOperationStatus.Failed:
currentReferenceCount = 0;
break;
case AsyncOperationStatus.None:
bool inProgress = !activeOperation.IsDone && activeOperation.IsRunning;
if (!inProgress)
currentReferenceCount = 0;
break;
}
float currentPercentComplete = activeOperation.PercentComplete;
newDataOut = (currentReferenceCount, currentPercentComplete);
return currentReferenceCount != frameReferenceCount
|| !Mathf.Approximately(currentPercentComplete, framePercentComplete);
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe634a3decf6743b4918c03e2ae2642c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 2c38782eb8fd84ba1b42d3abaf4717a8
folderAsset: yes
timeCreated: 1503087283
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.ResourceManagement.ResourceLocations
{
/// <summary>
/// Interface for computing size of loading a location.
/// </summary>
public interface ILocationSizeData
{
/// <summary>
/// Compute the numder of bytes need to download for the specified location.
/// </summary>
/// <param name="location">The location to compute the size for.</param>
/// <param name="resourceManager">The object that contains all the resource locations.</param>
/// <returns>The size in bytes of the data needed to be downloaded.</returns>
long ComputeSize(IResourceLocation location, ResourceManager resourceManager);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f2308a41f0f71948bf45196f5b2d8ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.ResourceManagement.ResourceLocations
{
/// <summary>
/// Contains enough information to load an asset (what/where/how/dependencies)
/// </summary>
public interface IResourceLocation
{
/// <summary>
/// Internal name used by the provider to load this location
/// </summary>
/// <value>The identifier.</value>
string InternalId { get; }
/// <summary>
/// Matches the provider used to provide/load this location
/// </summary>
/// <value>The provider id.</value>
string ProviderId { get; }
/// <summary>
/// Gets the dependencies to other IResourceLocations
/// </summary>
/// <value>The dependencies.</value>
IList<IResourceLocation> Dependencies { get; }
/// <summary>
/// The hash of this location combined with the specified type.
/// </summary>
/// <param name="resultType">The type of the result.</param>
/// <returns>The combined hash of the location and the type.</returns>
int Hash(Type resultType);
/// <summary>
/// The precomputed hash code of the dependencies.
/// </summary>
int DependencyHashCode { get; }
/// <summary>
/// Gets the dependencies to other IResourceLocations
/// </summary>
/// <value>The dependencies.</value>
bool HasDependencies { get; }
/// <summary>
/// Gets any data object associated with this locations
/// </summary>
/// <value>The object.</value>
object Data { get; }
/// <summary>
/// Primary address for this location.
/// </summary>
string PrimaryKey { get; }
/// <summary>
/// The type of the resource for th location.
/// </summary>
Type ResourceType { get; }
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 3e78249b603b049aa9dfd59ec52d2f30
timeCreated: 1503083765
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
namespace UnityEngine.ResourceManagement.ResourceLocations
{
/// <summary>
/// Basic implementation of IResourceLocation
/// </summary>
public class ResourceLocationBase : IResourceLocation
{
string m_Name;
string m_Id;
string m_ProviderId;
object m_Data;
int m_DependencyHashCode;
int m_HashCode;
Type m_Type;
List<IResourceLocation> m_Dependencies;
string m_PrimaryKey;
/// <summary>
/// Internal id.
/// </summary>
public string InternalId
{
get { return m_Id; }
}
/// <summary>
/// Provider Id. This is usually set to the FullName property of the type of the provider class.
/// </summary>
public string ProviderId
{
get { return m_ProviderId; }
}
/// <summary>
/// List of dependencies that must be loaded before this location. This value may be null.
/// </summary>
public IList<IResourceLocation> Dependencies
{
get { return m_Dependencies; }
}
/// <summary>
/// Convenience method to see if there are any dependencies.
/// </summary>
public bool HasDependencies
{
get { return m_Dependencies != null && m_Dependencies.Count > 0; }
}
/// <summary>
/// Data that is intended for the provider. Objects can be serialized during the build process to be used by the provider. An example of usage is cache usage data for AssetBundleProvider.
/// </summary>
public object Data
{
get { return m_Data; }
set { m_Data = value; }
}
/// <inheritdoc/>
public string PrimaryKey
{
get { return m_PrimaryKey; }
set { m_PrimaryKey = value; }
}
/// <summary>
/// Precomputed hash code of dependencies.
/// </summary>
public int DependencyHashCode
{
get { return m_DependencyHashCode; }
}
/// <summary>
/// The type of the resource for th location.
/// </summary>
public Type ResourceType
{
get { return m_Type; }
}
/// <summary>
/// Compute the hash of this location for the specified type.
/// </summary>
/// <param name="t">The type to hash with.</param>
/// <returns>The combined hash code of the location and type.</returns>
public int Hash(Type t)
{
return (m_HashCode * 31 + t.GetHashCode()) * 31 + DependencyHashCode;
}
/// <summary>
/// Returns the Internal name used by the provider to load this location
/// </summary>
/// <returns></returns>
public override string ToString()
{
return m_Id;
}
/// <summary>
/// Construct a new ResourceLocationBase.
/// </summary>
/// <param name="name">The name of the location. This is usually set to the primary key, or "address" of the location.</param>
/// <param name="id">The internal id of the location. This is used by the IResourceProvider to identify the object to provide. For example this may contain the file path or url of an asset.</param>
/// <param name="providerId">The provider id. This is set to the FullName of the type of the provder class.</param>
/// <param name="t">The type of the object to provide.</param>
/// <param name="dependencies">Locations for the dependencies of this location.</param>
public ResourceLocationBase(string name, string id, string providerId, Type t, params IResourceLocation[] dependencies)
{
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrEmpty(providerId))
throw new ArgumentNullException(nameof(providerId));
m_PrimaryKey = name;
m_HashCode = (name.GetHashCode() * 31 + id.GetHashCode()) * 31 + providerId.GetHashCode();
m_Name = name;
m_Id = id;
m_ProviderId = providerId;
m_Dependencies = new List<IResourceLocation>(dependencies);
m_Type = t == null ? typeof(object) : t;
ComputeDependencyHash();
}
/// <summary>
/// Compute the dependency hash for this location
/// </summary>
public void ComputeDependencyHash() // TODO: dependency hash is no longer just objects
{
m_DependencyHashCode = m_Dependencies.Count > 0 ? 17 : 0;
foreach (var d in m_Dependencies)
m_DependencyHashCode = m_DependencyHashCode * 31 + d.Hash(typeof(object));
}
}
internal class LocationWrapper : IResourceLocation
{
IResourceLocation m_InternalLocation;
public LocationWrapper(IResourceLocation location)
{
m_InternalLocation = location;
}
public string InternalId => m_InternalLocation.InternalId;
public string ProviderId => m_InternalLocation.ProviderId;
public IList<IResourceLocation> Dependencies => m_InternalLocation.Dependencies;
public int DependencyHashCode => m_InternalLocation.DependencyHashCode;
public bool HasDependencies => m_InternalLocation.HasDependencies;
public object Data => m_InternalLocation.Data;
public string PrimaryKey => m_InternalLocation.PrimaryKey;
public Type ResourceType => m_InternalLocation.ResourceType;
public int Hash(Type resultType)
{
return m_InternalLocation.Hash(resultType);
}
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 9fb0f68aa5e81c448815f0b79325a30f
timeCreated: 1504897908
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 48d49afb756d3456da9e2b0b66b2221d
timeCreated: 1508958551
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 6a6004f87009b4fad9ad9582dc0c7016
folderAsset: yes
timeCreated: 1503084977
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,929 @@
#if UNITY_2022_1_OR_NEWER
#define UNLOAD_BUNDLE_ASYNC
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using UnityEngine.Networking;
using UnityEngine.Profiling;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.Serialization;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
internal class DownloadOnlyLocation : LocationWrapper
{
public DownloadOnlyLocation(IResourceLocation location) : base(location)
{
}
}
/// <summary>
/// Used to indication how Assets are loaded from the AssetBundle on the first load request.
/// </summary>
public enum AssetLoadMode
{
/// <summary>
/// Only load the requested Asset and Dependencies
/// </summary>
RequestedAssetAndDependencies = 0,
/// <summary>
/// Load all assets inside the AssetBundle
/// </summary>
AllPackedAssetsAndDependencies,
}
/// <summary>
/// Wrapper for asset bundles.
/// </summary>
public interface IAssetBundleResource
{
/// <summary>
/// Retrieves the asset bundle.
/// </summary>
/// <returns>Returns the asset bundle.</returns>
AssetBundle GetAssetBundle();
}
/// <summary>
/// Contains cache information to be used by the AssetBundleProvider
/// </summary>
[Serializable]
public class AssetBundleRequestOptions : ILocationSizeData
{
[FormerlySerializedAs("m_hash")]
[SerializeField]
string m_Hash = "";
/// <summary>
/// Hash value of the asset bundle.
/// </summary>
public string Hash
{
get { return m_Hash; }
set { m_Hash = value; }
}
[FormerlySerializedAs("m_crc")]
[SerializeField]
uint m_Crc;
/// <summary>
/// CRC value of the bundle.
/// </summary>
public uint Crc
{
get { return m_Crc; }
set { m_Crc = value; }
}
[FormerlySerializedAs("m_timeout")]
[SerializeField]
int m_Timeout;
/// <summary>
/// Attempt to abort after the number of seconds in timeout have passed, where the UnityWebRequest has received no data.
/// </summary>
public int Timeout
{
get { return m_Timeout; }
set { m_Timeout = value; }
}
[FormerlySerializedAs("m_chunkedTransfer")]
[SerializeField]
bool m_ChunkedTransfer;
/// <summary>
/// Indicates whether the UnityWebRequest system should employ the HTTP/1.1 chunked-transfer encoding method.
/// </summary>
public bool ChunkedTransfer
{
get { return m_ChunkedTransfer; }
set { m_ChunkedTransfer = value; }
}
[FormerlySerializedAs("m_redirectLimit")]
[SerializeField]
int m_RedirectLimit = -1;
/// <summary>
/// Indicates the number of redirects which this UnityWebRequest will follow before halting with a “Redirect Limit Exceeded” system error.
/// </summary>
public int RedirectLimit
{
get { return m_RedirectLimit; }
set { m_RedirectLimit = value; }
}
[FormerlySerializedAs("m_retryCount")]
[SerializeField]
int m_RetryCount;
/// <summary>
/// Indicates the number of times the request will be retried.
/// </summary>
public int RetryCount
{
get { return m_RetryCount; }
set { m_RetryCount = value; }
}
[SerializeField]
string m_BundleName = null;
/// <summary>
/// The name of the original bundle. This does not contain the appended hash.
/// </summary>
public string BundleName
{
get { return m_BundleName; }
set { m_BundleName = value; }
}
[SerializeField]
AssetLoadMode m_AssetLoadMode = AssetLoadMode.RequestedAssetAndDependencies;
/// <summary>
/// Determines how Assets are loaded when accessed.
/// </summary>
/// <remarks>
/// Requested Asset And Dependencies, will only load the requested Asset (Recommended).
/// All Packed Assets And Dependencies, will load all Assets that are packed together. Best used when loading all Assets into memory is required.
///</remarks>
public AssetLoadMode AssetLoadMode
{
get { return m_AssetLoadMode; }
set { m_AssetLoadMode = value; }
}
[SerializeField]
long m_BundleSize;
/// <summary>
/// The size of the bundle, in bytes.
/// </summary>
public long BundleSize
{
get { return m_BundleSize; }
set { m_BundleSize = value; }
}
[SerializeField]
bool m_UseCrcForCachedBundles;
/// <summary>
/// If false, the CRC will not be used when loading bundles from the cache.
/// </summary>
public bool UseCrcForCachedBundle
{
get { return m_UseCrcForCachedBundles; }
set { m_UseCrcForCachedBundles = value; }
}
[SerializeField]
bool m_UseUWRForLocalBundles;
/// <summary>
/// If true, UnityWebRequest will be used even if the bundle is stored locally.
/// </summary>
public bool UseUnityWebRequestForLocalBundles
{
get { return m_UseUWRForLocalBundles; }
set { m_UseUWRForLocalBundles = value; }
}
[SerializeField]
bool m_ClearOtherCachedVersionsWhenLoaded;
/// <summary>
/// If false, the CRC will not be used when loading bundles from the cache.
/// </summary>
public bool ClearOtherCachedVersionsWhenLoaded
{
get { return m_ClearOtherCachedVersionsWhenLoaded; }
set { m_ClearOtherCachedVersionsWhenLoaded = value; }
}
/// <summary>
/// Computes the amount of data needed to be downloaded for this bundle.
/// </summary>
/// <param name="location">The location of the bundle.</param>
/// <param name="resourceManager">The object that contains all the resource locations.</param>
/// <returns>The size in bytes of the bundle that is needed to be downloaded. If the local cache contains the bundle or it is a local bundle, 0 will be returned.</returns>
public virtual long ComputeSize(IResourceLocation location, ResourceManager resourceManager)
{
var id = resourceManager == null ? location.InternalId : resourceManager.TransformInternalId(location);
if (!ResourceManagerConfig.IsPathRemote(id))
return 0;
var locHash = Hash128.Parse(Hash);
#if ENABLE_CACHING
if (locHash.isValid) //If we have a hash, ensure that our desired version is cached.
{
if (Caching.IsVersionCached(new CachedAssetBundle(BundleName, locHash)))
return 0;
return BundleSize;
}
#endif //ENABLE_CACHING
return BundleSize;
}
}
/// <summary>
/// Provides methods for loading an AssetBundle from a local or remote location.
/// </summary>
public class AssetBundleResource : IAssetBundleResource, IUpdateReceiver
{
/// <summary>
/// Options for where an AssetBundle can be loaded from.
/// </summary>
public enum LoadType
{
/// <summary>
/// Cannot determine where the AssetBundle is located.
/// </summary>
None,
/// <summary>
/// Load the AssetBundle from a local file location.
/// </summary>
Local,
/// <summary>
/// Download the AssetBundle from a web server.
/// </summary>
Web
}
AssetBundle m_AssetBundle;
AsyncOperation m_RequestOperation;
WebRequestQueueOperation m_WebRequestQueueOperation;
internal ProvideHandle m_ProvideHandle;
internal AssetBundleRequestOptions m_Options;
[NonSerialized]
bool m_RequestCompletedCallbackCalled = false;
int m_Retries;
BundleSource m_Source = BundleSource.None;
long m_BytesToDownload;
long m_DownloadedBytes;
bool m_Completed = false;
#if UNLOAD_BUNDLE_ASYNC
AssetBundleUnloadOperation m_UnloadOperation;
#endif
const int k_WaitForWebRequestMainThreadSleep = 1;
string m_TransformedInternalId;
AssetBundleRequest m_PreloadRequest;
bool m_PreloadCompleted = false;
ulong m_LastDownloadedByteCount = 0;
float m_TimeoutTimer = 0;
int m_TimeoutOverFrames = 0;
private bool HasTimedOut => m_TimeoutTimer >= m_Options.Timeout && m_TimeoutOverFrames > 5;
internal long BytesToDownload
{
get
{
if (m_BytesToDownload == -1)
{
if (m_Options != null)
m_BytesToDownload = m_Options.ComputeSize(m_ProvideHandle.Location, m_ProvideHandle.ResourceManager);
else
m_BytesToDownload = 0;
}
return m_BytesToDownload;
}
}
internal UnityWebRequest CreateWebRequest(IResourceLocation loc)
{
var url = m_ProvideHandle.ResourceManager.TransformInternalId(loc);
return CreateWebRequest(url);
}
internal UnityWebRequest CreateWebRequest(string url)
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
Uri uri = new Uri(url.Replace(" ", "%20"));
#else
Uri uri = new Uri(Uri.EscapeUriString(url));
#endif
if (m_Options == null)
{
m_Source = BundleSource.Download;
#if ENABLE_ADDRESSABLE_PROFILER
AddBundleToProfiler(Profiling.ContentStatus.Downloading, m_Source);
#endif
return UnityWebRequestAssetBundle.GetAssetBundle(uri);
}
UnityWebRequest webRequest;
if (!string.IsNullOrEmpty(m_Options.Hash))
{
CachedAssetBundle cachedBundle = new CachedAssetBundle(m_Options.BundleName, Hash128.Parse(m_Options.Hash));
#if ENABLE_CACHING
bool cached = Caching.IsVersionCached(cachedBundle);
m_Source = cached ? BundleSource.Cache : BundleSource.Download;
if (m_Options.UseCrcForCachedBundle || m_Source == BundleSource.Download)
webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, cachedBundle, m_Options.Crc);
else
webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, cachedBundle);
#else
webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, cachedBundle, m_Options.Crc);
#endif
}
else
{
m_Source = BundleSource.Download;
webRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, m_Options.Crc);
}
if (m_Options.RedirectLimit > 0)
webRequest.redirectLimit = m_Options.RedirectLimit;
if (m_ProvideHandle.ResourceManager.CertificateHandlerInstance != null)
{
webRequest.certificateHandler = m_ProvideHandle.ResourceManager.CertificateHandlerInstance;
webRequest.disposeCertificateHandlerOnDispose = false;
}
m_ProvideHandle.ResourceManager.WebRequestOverride?.Invoke(webRequest);
return webRequest;
}
/// <summary>
/// Creates a request for loading all assets from an AssetBundle.
/// </summary>
/// <returns>Returns the request.</returns>
public AssetBundleRequest GetAssetPreloadRequest()
{
if (m_PreloadCompleted || GetAssetBundle() == null)
return null;
if (m_Options.AssetLoadMode == AssetLoadMode.AllPackedAssetsAndDependencies)
{
#if !UNITY_2021_1_OR_NEWER
if (AsyncOperationHandle.IsWaitingForCompletion)
{
m_AssetBundle.LoadAllAssets();
m_PreloadCompleted = true;
return null;
}
#endif
if (m_PreloadRequest == null)
{
m_PreloadRequest = m_AssetBundle.LoadAllAssetsAsync();
m_PreloadRequest.completed += operation => m_PreloadCompleted = true;
}
return m_PreloadRequest;
}
return null;
}
float PercentComplete()
{
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
}
DownloadStatus GetDownloadStatus()
{
if (m_Options == null)
return default;
var status = new DownloadStatus() {TotalBytes = BytesToDownload, IsDone = PercentComplete() >= 1f};
if (BytesToDownload > 0)
{
if (m_WebRequestQueueOperation != null && string.IsNullOrEmpty(m_WebRequestQueueOperation.m_WebRequest.error))
m_DownloadedBytes = (long)(m_WebRequestQueueOperation.m_WebRequest.downloadedBytes);
else if (m_RequestOperation != null && m_RequestOperation is UnityWebRequestAsyncOperation operation && string.IsNullOrEmpty(operation.webRequest.error))
m_DownloadedBytes = (long)operation.webRequest.downloadedBytes;
}
status.DownloadedBytes = m_DownloadedBytes;
return status;
}
/// <summary>
/// Get the asset bundle object managed by this resource. This call may force the bundle to load if not already loaded.
/// </summary>
/// <returns>The asset bundle.</returns>
public AssetBundle GetAssetBundle()
{
return m_AssetBundle;
}
#if ENABLE_ADDRESSABLE_PROFILER
private void AddBundleToProfiler(Profiling.ContentStatus status, BundleSource source)
{
if (!Profiler.enabled)
return;
if (!m_ProvideHandle.IsValid)
return;
if (status == Profiling.ContentStatus.Active && m_AssetBundle == null)
Profiling.ProfilerRuntime.BundleReleased(m_Options.BundleName);
else
Profiling.ProfilerRuntime.AddBundleOperation(m_ProvideHandle, m_Options, status, source);
}
private void RemoveBundleFromProfiler()
{
if (m_Options == null)
return;
Profiling.ProfilerRuntime.BundleReleased(m_Options.BundleName);
}
#endif
#if UNLOAD_BUNDLE_ASYNC
void OnUnloadOperationComplete(AsyncOperation op)
{
m_UnloadOperation = null;
BeginOperation();
}
#endif
#if UNLOAD_BUNDLE_ASYNC
/// <summary>
/// Stores AssetBundle loading information, starts loading the bundle.
/// </summary>
/// <param name="provideHandle">The container for AssetBundle loading information.</param>
/// <param name="unloadOp">The async operation for unloading the AssetBundle.</param>
public void Start(ProvideHandle provideHandle, AssetBundleUnloadOperation unloadOp)
#else
/// <summary>
/// Stores AssetBundle loading information, starts loading the bundle.
/// </summary>
/// <param name="provideHandle">The container for information regarding loading the AssetBundle.</param>
public void Start(ProvideHandle provideHandle)
#endif
{
m_Retries = 0;
m_AssetBundle = null;
m_RequestOperation = null;
m_RequestCompletedCallbackCalled = false;
m_ProvideHandle = provideHandle;
m_Options = m_ProvideHandle.Location.Data as AssetBundleRequestOptions;
m_BytesToDownload = -1;
m_ProvideHandle.SetProgressCallback(PercentComplete);
m_ProvideHandle.SetDownloadProgressCallbacks(GetDownloadStatus);
m_ProvideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
#if UNLOAD_BUNDLE_ASYNC
m_UnloadOperation = unloadOp;
if (m_UnloadOperation != null && !m_UnloadOperation.isDone)
m_UnloadOperation.completed += OnUnloadOperationComplete;
else
#endif
BeginOperation();
}
private bool WaitForCompletionHandler()
{
#if UNLOAD_BUNDLE_ASYNC
if (m_UnloadOperation != null && !m_UnloadOperation.isDone)
{
m_UnloadOperation.completed -= OnUnloadOperationComplete;
m_UnloadOperation.WaitForCompletion();
m_UnloadOperation = null;
BeginOperation();
}
#endif
if (m_RequestOperation == null)
{
if (m_WebRequestQueueOperation == null)
return false;
else
WebRequestQueue.WaitForRequestToBeActive(m_WebRequestQueueOperation, k_WaitForWebRequestMainThreadSleep);
}
//We don't want to wait for request op to complete if it's a LoadFromFileAsync. Only UWR will complete in a tight loop like this.
if (m_RequestOperation is UnityWebRequestAsyncOperation op)
{
while (!UnityWebRequestUtilities.IsAssetBundleDownloaded(op))
System.Threading.Thread.Sleep(k_WaitForWebRequestMainThreadSleep);
#if ENABLE_ASYNC_ASSETBUNDLE_UWR
if (m_Source == BundleSource.Cache)
{
var downloadHandler = (DownloadHandlerAssetBundle)op?.webRequest?.downloadHandler;
if (downloadHandler.autoLoadAssetBundle)
m_AssetBundle = downloadHandler.assetBundle;
}
#endif
if (!m_RequestCompletedCallbackCalled)
{
m_RequestOperation.completed -= WebRequestOperationCompleted;
WebRequestOperationCompleted(m_RequestOperation);
}
}
if (!m_Completed && m_Source == BundleSource.Local) {
// we don't have to check for done with local files as calling
// m_requestOperation.assetBundle is blocking and will wait for the file to load
if (!m_RequestCompletedCallbackCalled)
{
m_RequestOperation.completed -= LocalRequestOperationCompleted;
LocalRequestOperationCompleted(m_RequestOperation);
}
}
if (!m_Completed && m_RequestOperation.isDone)
{
m_ProvideHandle.Complete(this, m_AssetBundle != null, null);
m_Completed = true;
}
return m_Completed;
}
void AddCallbackInvokeIfDone(AsyncOperation operation, Action<AsyncOperation> callback)
{
if (operation.isDone)
callback(operation);
else
operation.completed += callback;
}
/// <summary>
/// Determines where an AssetBundle can be loaded from.
/// </summary>
/// <param name="handle">The container for AssetBundle loading information.</param>
/// <param name="loadType">Specifies where an AssetBundle can be loaded from.</param>
/// <param name="path">The file path or url where the AssetBundle is located.</param>
public static void GetLoadInfo(ProvideHandle handle, out LoadType loadType, out string path)
{
GetLoadInfo(handle.Location, handle.ResourceManager, out loadType, out path);
}
internal static void GetLoadInfo(IResourceLocation location, ResourceManager resourceManager, out LoadType loadType, out string path)
{
var options = location?.Data as AssetBundleRequestOptions;
if (options == null)
{
loadType = LoadType.None;
path = null;
return;
}
path = resourceManager.TransformInternalId(location);
if (Application.platform == RuntimePlatform.Android && path.StartsWith("jar:", StringComparison.Ordinal))
loadType = options.UseUnityWebRequestForLocalBundles ? LoadType.Web : LoadType.Local;
else if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
loadType = LoadType.Web;
else if (options.UseUnityWebRequestForLocalBundles)
{
path = "file:///" + Path.GetFullPath(path);
loadType = LoadType.Web;
}
else
loadType = LoadType.Local;
if (loadType == LoadType.Web)
path = path.Replace('\\', '/');
}
private void BeginOperation()
{
m_DownloadedBytes = 0;
GetLoadInfo(m_ProvideHandle, out LoadType loadType, out m_TransformedInternalId);
if (loadType == LoadType.Local)
{
m_Source = BundleSource.Local;
#if !UNITY_2021_1_OR_NEWER
if (AsyncOperationHandle.IsWaitingForCompletion)
CompleteBundleLoad(AssetBundle.LoadFromFile(m_TransformedInternalId, m_Options == null ? 0 : m_Options.Crc));
else
#endif
{
m_RequestOperation = AssetBundle.LoadFromFileAsync(m_TransformedInternalId, m_Options == null ? 0 : m_Options.Crc);
#if ENABLE_ADDRESSABLE_PROFILER
AddBundleToProfiler(Profiling.ContentStatus.Loading, m_Source);
#endif
AddCallbackInvokeIfDone(m_RequestOperation, LocalRequestOperationCompleted);
}
}
else if (loadType == LoadType.Web)
{
var req = CreateWebRequest(m_TransformedInternalId);
#if ENABLE_ASYNC_ASSETBUNDLE_UWR
((DownloadHandlerAssetBundle)req.downloadHandler).autoLoadAssetBundle = !(m_ProvideHandle.Location is DownloadOnlyLocation);
#endif
req.disposeDownloadHandlerOnDispose = false;
m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req);
if (m_WebRequestQueueOperation.IsDone)
{
BeginWebRequestOperation(m_WebRequestQueueOperation.Result);
}
else
{
#if ENABLE_ADDRESSABLE_PROFILER
AddBundleToProfiler(Profiling.ContentStatus.Queue, m_Source);
#endif
m_WebRequestQueueOperation.OnComplete += asyncOp => BeginWebRequestOperation(asyncOp);
}
}
else
{
m_Source = BundleSource.None;
m_RequestOperation = null;
m_ProvideHandle.Complete<AssetBundleResource>(null, false,
new RemoteProviderException(string.Format("Invalid path in AssetBundleProvider: '{0}'.", m_TransformedInternalId), m_ProvideHandle.Location));
m_Completed = true;
}
}
private void BeginWebRequestOperation(AsyncOperation asyncOp)
{
m_TimeoutTimer = 0;
m_TimeoutOverFrames = 0;
m_LastDownloadedByteCount = 0;
m_RequestOperation = asyncOp;
if (m_RequestOperation == null || m_RequestOperation.isDone)
WebRequestOperationCompleted(m_RequestOperation);
else
{
if (m_Options.Timeout > 0)
m_ProvideHandle.ResourceManager.AddUpdateReceiver(this);
#if ENABLE_ADDRESSABLE_PROFILER
AddBundleToProfiler(m_Source == BundleSource.Cache ? Profiling.ContentStatus.Loading : Profiling.ContentStatus.Downloading, m_Source );
#endif
m_RequestOperation.completed += WebRequestOperationCompleted;
}
}
/// <inheritdoc/>
public void Update(float unscaledDeltaTime)
{
if (m_RequestOperation != null && m_RequestOperation is UnityWebRequestAsyncOperation operation && !operation.isDone)
{
if (m_LastDownloadedByteCount != operation.webRequest.downloadedBytes)
{
m_TimeoutTimer = 0;
m_TimeoutOverFrames = 0;
m_LastDownloadedByteCount = operation.webRequest.downloadedBytes;
}
else
{
m_TimeoutTimer += unscaledDeltaTime;
if (HasTimedOut)
operation.webRequest.Abort();
m_TimeoutOverFrames++;
}
}
}
private void LocalRequestOperationCompleted(AsyncOperation op)
{
if (m_RequestCompletedCallbackCalled)
{
return;
}
m_RequestCompletedCallbackCalled = true;
CompleteBundleLoad((op as AssetBundleCreateRequest).assetBundle);
}
private void CompleteBundleLoad(AssetBundle bundle)
{
m_AssetBundle = bundle;
#if ENABLE_ADDRESSABLE_PROFILER
AddBundleToProfiler(Profiling.ContentStatus.Active, m_Source);
#endif
if (m_AssetBundle != null)
m_ProvideHandle.Complete(this, true, null);
else
m_ProvideHandle.Complete<AssetBundleResource>(null, false,
new RemoteProviderException(string.Format("Invalid path in AssetBundleProvider: '{0}'.", m_TransformedInternalId), m_ProvideHandle.Location));
m_Completed = true;
}
private void WebRequestOperationCompleted(AsyncOperation op)
{
if (m_RequestCompletedCallbackCalled)
return;
m_RequestCompletedCallbackCalled = true;
if (m_Options.Timeout > 0)
m_ProvideHandle.ResourceManager.RemoveUpdateReciever(this);
UnityWebRequestAsyncOperation remoteReq = op as UnityWebRequestAsyncOperation;
var webReq = remoteReq?.webRequest;
var downloadHandler = webReq?.downloadHandler as DownloadHandlerAssetBundle;
UnityWebRequestResult uwrResult = null;
if (webReq != null && !UnityWebRequestUtilities.RequestHasErrors(webReq, out uwrResult))
{
if (!m_Completed)
{
#if ENABLE_ADDRESSABLE_PROFILER
AddBundleToProfiler(Profiling.ContentStatus.Active, m_Source);
#endif
m_AssetBundle = downloadHandler.assetBundle;
downloadHandler.Dispose();
downloadHandler = null;
m_ProvideHandle.Complete(this, true, null);
m_Completed = true;
}
#if ENABLE_CACHING
if (!string.IsNullOrEmpty(m_Options.Hash) && m_Options.ClearOtherCachedVersionsWhenLoaded)
Caching.ClearOtherCachedVersions(m_Options.BundleName, Hash128.Parse(m_Options.Hash));
#endif
}
else
{
if (HasTimedOut)
uwrResult.Error = "Request timeout";
webReq = m_WebRequestQueueOperation.m_WebRequest;
if (uwrResult == null)
uwrResult = new UnityWebRequestResult(m_WebRequestQueueOperation.m_WebRequest);
downloadHandler = webReq.downloadHandler as DownloadHandlerAssetBundle;
downloadHandler.Dispose();
downloadHandler = null;
bool forcedRetry = false;
string message = $"Web request failed, retrying ({m_Retries}/{m_Options.RetryCount})...\n{uwrResult}";
#if ENABLE_CACHING
if (!string.IsNullOrEmpty(m_Options.Hash))
{
#if ENABLE_ADDRESSABLE_PROFILER
if (m_Source == BundleSource.Cache)
#endif
{
message = $"Web request failed to load from cache. The cached AssetBundle will be cleared from the cache and re-downloaded. Retrying...\n{uwrResult}";
Caching.ClearCachedVersion(m_Options.BundleName, Hash128.Parse(m_Options.Hash));
// When attempted to load from cache we always retry on first attempt and failed
if (m_Retries == 0 && uwrResult.ShouldRetryDownloadError())
{
Debug.LogFormat(message);
BeginOperation();
m_Retries++; //Will prevent us from entering an infinite loop of retrying if retry count is 0
forcedRetry = true;
}
}
}
#endif
if (!forcedRetry)
{
if (m_Retries < m_Options.RetryCount && uwrResult.ShouldRetryDownloadError())
{
m_Retries++;
Debug.LogFormat(message);
BeginOperation();
}
else
{
var exception = new RemoteProviderException($"Unable to load asset bundle from : {webReq.url}", m_ProvideHandle.Location, uwrResult);
m_ProvideHandle.Complete<AssetBundleResource>(null, false, exception);
m_Completed = true;
#if ENABLE_ADDRESSABLE_PROFILER
RemoveBundleFromProfiler();
#endif
}
}
}
webReq.Dispose();
}
#if UNLOAD_BUNDLE_ASYNC
/// <summary>
/// Starts an async operation that unloads all resources associated with the AssetBundle.
/// </summary>
/// <param name="unloadOp">The async operation.</param>
/// <returns>Returns true if the async operation object is valid.</returns>
public bool Unload(out AssetBundleUnloadOperation unloadOp)
#else
/// <summary>
/// Unloads all resources associated with the AssetBundle.
/// </summary>
public void Unload()
#endif
{
#if UNLOAD_BUNDLE_ASYNC
unloadOp = null;
if (m_AssetBundle != null)
{
unloadOp = m_AssetBundle.UnloadAsync(true);
m_AssetBundle = null;
}
#else
if (m_AssetBundle != null)
{
m_AssetBundle.Unload(true);
m_AssetBundle = null;
}
#endif
m_RequestOperation = null;
#if ENABLE_ADDRESSABLE_PROFILER
RemoveBundleFromProfiler();
#endif
#if UNLOAD_BUNDLE_ASYNC
return unloadOp != null;
#endif
}
}
/// <summary>
/// IResourceProvider for asset bundles. Loads bundles via UnityWebRequestAssetBundle API if the internalId starts with "http". If not, it will load the bundle via AssetBundle.LoadFromFileAsync.
/// </summary>
[DisplayName("AssetBundle Provider")]
public class AssetBundleProvider : ResourceProviderBase
{
#if UNLOAD_BUNDLE_ASYNC
private static Dictionary<string, AssetBundleUnloadOperation> m_UnloadingBundles = new Dictionary<string, AssetBundleUnloadOperation>();
/// <summary>
/// Stores async operations that unload the requested AssetBundles.
/// </summary>
protected internal static Dictionary<string, AssetBundleUnloadOperation> UnloadingBundles
{
get { return m_UnloadingBundles; }
internal set { m_UnloadingBundles = value; }
}
internal static int UnloadingAssetBundleCount => m_UnloadingBundles.Count;
internal static int AssetBundleCount => AssetBundle.GetAllLoadedAssetBundles().Count() - UnloadingAssetBundleCount;
internal static void WaitForAllUnloadingBundlesToComplete()
{
if (UnloadingAssetBundleCount > 0)
{
var bundles = m_UnloadingBundles.Values.ToArray();
foreach (var b in bundles)
b.WaitForCompletion();
}
}
#else
internal static void WaitForAllUnloadingBundlesToComplete()
{
}
#endif
/// <inheritdoc/>
public override void Provide(ProvideHandle providerInterface)
{
#if UNLOAD_BUNDLE_ASYNC
if (m_UnloadingBundles.TryGetValue(providerInterface.Location.InternalId, out var unloadOp))
{
if (unloadOp.isDone)
unloadOp = null;
}
new AssetBundleResource().Start(providerInterface, unloadOp);
#else
new AssetBundleResource().Start(providerInterface);
#endif
}
/// <inheritdoc/>
public override Type GetDefaultType(IResourceLocation location)
{
return typeof(IAssetBundleResource);
}
/// <summary>
/// Releases the asset bundle via AssetBundle.Unload(true).
/// </summary>
/// <param name="location">The location of the asset to release</param>
/// <param name="asset">The asset in question</param>
public override void Release(IResourceLocation location, object asset)
{
if (location == null)
throw new ArgumentNullException("location");
if (asset == null)
{
Debug.LogWarningFormat("Releasing null asset bundle from location {0}. This is an indication that the bundle failed to load.", location);
return;
}
var bundle = asset as AssetBundleResource;
if (bundle != null)
{
#if UNLOAD_BUNDLE_ASYNC
if (bundle.Unload(out var unloadOp))
{
m_UnloadingBundles.Add(location.InternalId, unloadOp);
unloadOp.completed += op => m_UnloadingBundles.Remove(location.InternalId);
}
#else
bundle.Unload();
#endif
return;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86a4f4dd6530757418ab820c2c781436
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,130 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEditor;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Provides assets loaded via the AssetDatabase API. This provider is only available in the editor and is used for fast iteration or to simulate asset bundles when in play mode.
/// </summary>
[DisplayName("Assets from AssetDatabase Provider")]
public class AssetDatabaseProvider : ResourceProviderBase
{
float m_LoadDelay = .1f;
internal static Object LoadAssetSubObject(string assetPath, string subObjectName, Type type)
{
var objs = LoadAssetsWithSubAssets(assetPath);
foreach (var o in objs)
{
if (o.name == subObjectName)
{
if (type.IsAssignableFrom(o.GetType()))
return o;
}
}
return null;
}
private static Object LoadMainAssetAtPath(string assetPath)
{
return AssetDatabase.LoadMainAssetAtPath(assetPath);
}
internal static object LoadAssetAtPath(string assetPath, ProvideHandle provideHandle)
{
Object obj = AssetDatabase.LoadAssetAtPath(assetPath, provideHandle.Location.ResourceType);
obj = obj != null && provideHandle.Type.IsAssignableFrom(obj.GetType()) ? obj : null;
return obj;
}
/// <summary>
/// Default constructor.
/// </summary>
public AssetDatabaseProvider()
{
}
/// <summary>
/// Constructor that allows for a sepcified delay for all requests.
/// </summary>
/// <param name="delay">Time in seconds for each delay call.</param>
public AssetDatabaseProvider(float delay = .25f)
{
m_LoadDelay = delay;
}
internal static Object[] LoadAssetsWithSubAssets(string assetPath)
{
var subObjects = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath);
var allObjects = new Object[subObjects.Length + 1];
allObjects[0] = LoadMainAssetAtPath(assetPath);
for (int i = 0; i < subObjects.Length; i++)
allObjects[i + 1] = subObjects[i];
return allObjects;
}
class InternalOp
{
ProvideHandle m_ProvideHandle;
bool m_Loaded;
public void Start(ProvideHandle provideHandle, float loadDelay)
{
m_Loaded = false;
m_ProvideHandle = provideHandle;
m_ProvideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
if (loadDelay < 0)
LoadImmediate();
else
DelayedActionManager.AddAction((Action)LoadImmediate, loadDelay);
}
private bool WaitForCompletionHandler()
{
LoadImmediate();
return true;
}
void LoadImmediate()
{
if (m_Loaded)
return;
m_Loaded = true;
string assetPath = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
object result = null;
if (m_ProvideHandle.Type.IsArray)
result = ResourceManagerConfig.CreateArrayResult(m_ProvideHandle.Type, LoadAssetsWithSubAssets(assetPath));
else if (m_ProvideHandle.Type.IsGenericType && typeof(IList<>) == m_ProvideHandle.Type.GetGenericTypeDefinition())
result = ResourceManagerConfig.CreateListResult(m_ProvideHandle.Type, LoadAssetsWithSubAssets(assetPath));
else
{
if (ResourceManagerConfig.ExtractKeyAndSubKey(assetPath, out string mainPath, out string subKey))
result = LoadAssetSubObject(mainPath, subKey, m_ProvideHandle.Type);
else
result = LoadAssetAtPath(assetPath, m_ProvideHandle);
}
m_ProvideHandle.Complete(result, result != null,
result == null ? new Exception($"Unable to load asset of type {m_ProvideHandle.Type} from location {m_ProvideHandle.Location}.") : null);
}
}
/// <inheritdoc/>
public override bool CanProvide(Type t, IResourceLocation location)
{
return base.CanProvide(t, location);
}
public override void Provide(ProvideHandle provideHandle)
{
new InternalOp().Start(provideHandle, m_LoadDelay);
}
}
}
#endif

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: f0364baebb057874782f9bd6e4dcfa78
timeCreated: 1502505318
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,30 @@
using System.ComponentModel;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.U2D;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Provides sprites from atlases
/// </summary>
[DisplayName("Sprites from Atlases Provider")]
public class AtlasSpriteProvider : ResourceProviderBase
{
/// <inheritdoc/>
public override void Provide(ProvideHandle providerInterface)
{
var atlas = providerInterface.GetDependency<SpriteAtlas>(0);
if (atlas == null)
{
providerInterface.Complete<Sprite>(null, false, new System.Exception($"Sprite atlas failed to load for location {providerInterface.Location.PrimaryKey}."));
return;
}
var key = providerInterface.ResourceManager.TransformInternalId(providerInterface.Location);
ResourceManagerConfig.ExtractKeyAndSubKey(key, out string mainKey, out string subKey);
string spriteKey = string.IsNullOrEmpty(subKey) ? mainKey : subKey;
var sprite = atlas.GetSprite(spriteKey);
providerInterface.Complete(sprite, sprite != null, sprite != null ? null : new System.Exception($"Sprite failed to load for location {providerInterface.Location.PrimaryKey}."));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 82e826e5c9181f2449a34ca76c3837e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,24 @@
using System;
using System.ComponentModel;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Converts ray binary data into the requested object.
/// </summary>
[DisplayName("Binary Asset Provider")]
internal class BinaryAssetProvider<TAdapter> : BinaryDataProvider where TAdapter : BinaryStorageBuffer.ISerializationAdapter, new()
{
/// <summary>
/// Converts raw bytes into requested object type via BinaryStorageBuffer.ISerializationAdapter
/// </summary>
/// <param name="type">The object type the text is converted to.</param>
/// <param name="data">The data to convert.</param>
/// <returns>Returns the converted object.</returns>
public override object Convert(Type type, byte[] data)
{
return new BinaryStorageBuffer.Reader(data, 512, new TAdapter()).ReadObject(type, 0, false);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fe4baf3821dbe44ca354c8bbae559f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show more