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

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:

View file

@ -0,0 +1,203 @@
using System;
using System.ComponentModel;
using System.IO;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Provides raw text from a local or remote URL.
/// </summary>
[DisplayName("Binary Data Provider")]
internal class BinaryDataProvider : ResourceProviderBase
{
/// <summary>
/// Controls whether errors are logged - this is disabled when trying to load from the local cache since failures are expected
/// </summary>
public bool IgnoreFailures { get; set; }
internal class InternalOp
{
BinaryDataProvider m_Provider;
UnityWebRequestAsyncOperation m_RequestOperation;
WebRequestQueueOperation m_RequestQueueOperation;
ProvideHandle m_PI;
bool m_IgnoreFailures;
private bool m_Complete = false;
private int m_Timeout = 0;
private float GetPercentComplete()
{
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
}
public void Start(ProvideHandle provideHandle, BinaryDataProvider rawProvider)
{
m_PI = provideHandle;
m_PI.SetWaitForCompletionCallback(WaitForCompletionHandler);
provideHandle.SetProgressCallback(GetPercentComplete);
m_Provider = rawProvider;
// override input options with options from Location if included
if (m_PI.Location.Data is ProviderLoadRequestOptions providerData)
{
m_IgnoreFailures = providerData.IgnoreFailures;
m_Timeout = providerData.WebRequestTimeout;
}
else
{
m_IgnoreFailures = rawProvider.IgnoreFailures;
m_Timeout = 0;
}
var path = m_PI.ResourceManager.TransformInternalId(m_PI.Location);
if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
{
SendWebRequest(path);
}
else if (File.Exists(path))
{
#if NET_4_6
if (path.Length >= 260)
path = @"\\?\" + path;
#endif
if (path.EndsWith(".json"))
throw new Exception($"Trying to read non binary data at path '{path}'.");
var data = File.ReadAllBytes(path);
object result = ConvertBytes(data);
m_PI.Complete(result, result != null, result == null ? new Exception($"Unable to load asset of type {m_PI.Type} from location {m_PI.Location}.") : null);
m_Complete = true;
}
else
{
Exception exception = null;
//Don't log errors when loading from the persistentDataPath since these files are expected to not exist until created
if (m_IgnoreFailures)
{
m_PI.Complete<object>(null, true, exception);
m_Complete = true;
}
else
{
exception = new Exception(string.Format("Invalid path in " + nameof(TextDataProvider) + " : '{0}'.", path));
m_PI.Complete<object>(null, false, exception);
m_Complete = true;
}
}
}
bool WaitForCompletionHandler()
{
if (m_Complete)
return true;
if (m_RequestOperation != null)
{
if (m_RequestOperation.isDone && !m_Complete)
RequestOperation_completed(m_RequestOperation);
else if (!m_RequestOperation.isDone)
return false;
}
return m_Complete;
}
private void RequestOperation_completed(AsyncOperation op)
{
if (m_Complete)
return;
var webOp = op as UnityWebRequestAsyncOperation;
byte[] binaryResult = null;
Exception exception = null;
if (webOp != null)
{
var webReq = webOp.webRequest;
if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult))
binaryResult = webReq.downloadHandler.data;
else
exception = new RemoteProviderException($"{nameof(TextDataProvider)} : unable to load from url : {webReq.url}", m_PI.Location, uwrResult);
webReq.Dispose();
}
else
{
exception = new RemoteProviderException(nameof(TextDataProvider) + " unable to load from unknown url", m_PI.Location);
}
CompleteOperation(binaryResult, exception);
}
protected void CompleteOperation(byte[] data, Exception exception)
{
object result = null;
if (data != null && data.Length != 0)
result = ConvertBytes(data);
m_PI.Complete(result, result != null || m_IgnoreFailures, exception);
m_Complete = true;
}
private object ConvertBytes(byte[] data)
{
try
{
return m_Provider.Convert(m_PI.Type, data);
}
catch (Exception e)
{
if (!m_IgnoreFailures)
Debug.LogException(e);
return null;
}
}
protected virtual void SendWebRequest(string path)
{
UnityWebRequest request = new UnityWebRequest(path, UnityWebRequest.kHttpVerbGET, new DownloadHandlerBuffer(), null);
if (m_Timeout > 0)
request.timeout = m_Timeout;
m_PI.ResourceManager.WebRequestOverride?.Invoke(request);
m_RequestQueueOperation = WebRequestQueue.QueueRequest(request);
if (m_RequestQueueOperation.IsDone)
{
m_RequestOperation = m_RequestQueueOperation.Result;
if (m_RequestOperation.isDone)
RequestOperation_completed(m_RequestOperation);
else
m_RequestOperation.completed += RequestOperation_completed;
}
else
{
m_RequestQueueOperation.OnComplete += asyncOperation =>
{
m_RequestOperation = asyncOperation;
m_RequestOperation.completed += RequestOperation_completed;
};
}
}
}
/// <summary>
/// Method to convert the text into the object type requested. Usually the text contains a JSON formatted serialized object.
/// </summary>
/// <param name="type">The object type the text is converted to.</param>
/// <param name="data">The byte array to be converted.</param>
/// <returns>The converted object.</returns>
public virtual object Convert(Type type, byte[] data)
{
return data;
}
/// <summary>
/// Provides raw text data from the location.
/// </summary>
/// <param name="provideHandle">The data needed by the provider to perform the load.</param>
public override void Provide(ProvideHandle provideHandle)
{
new InternalOp().Start(provideHandle, this);
}
}
}

View file

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

View file

@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.ResourceManagement.AsyncOperations;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Provides assets stored in an asset bundle.
/// </summary>
[DisplayName("Assets from Bundles Provider")]
public class BundledAssetProvider : ResourceProviderBase
{
internal class InternalOp
{
AssetBundle m_AssetBundle;
AssetBundleRequest m_PreloadRequest;
AssetBundleRequest m_RequestOperation;
object m_Result;
ProvideHandle m_ProvideHandle;
string subObjectName = null;
internal static T LoadBundleFromDependecies<T>(IList<object> results) where T : class, IAssetBundleResource
{
if (results == null || results.Count == 0)
return default(T);
IAssetBundleResource bundle = null;
bool firstBundleWrapper = true;
for (int i = 0; i < results.Count; i++)
{
var abWrapper = results[i] as IAssetBundleResource;
if (abWrapper != null)
{
//only use the first asset bundle, even if it is invalid
abWrapper.GetAssetBundle();
if (firstBundleWrapper)
bundle = abWrapper;
firstBundleWrapper = false;
}
}
return bundle as T;
}
public void Start(ProvideHandle provideHandle)
{
provideHandle.SetProgressCallback(ProgressCallback);
provideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
subObjectName = null;
m_ProvideHandle = provideHandle;
m_RequestOperation = null;
List<object> deps = new List<object>(); // TODO: garbage. need to pass actual count and reuse the list
m_ProvideHandle.GetDependencies(deps);
var bundleResource = LoadBundleFromDependecies<IAssetBundleResource>(deps);
if (bundleResource == null)
{
m_ProvideHandle.Complete<AssetBundle>(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location));
}
else
{
m_AssetBundle = bundleResource.GetAssetBundle();
if (m_AssetBundle == null)
{
m_ProvideHandle.Complete<AssetBundle>(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location));
return;
}
var assetBundleResource = bundleResource as AssetBundleResource;
if (assetBundleResource != null)
m_PreloadRequest = assetBundleResource.GetAssetPreloadRequest();
if (m_PreloadRequest == null || m_PreloadRequest.isDone)
BeginAssetLoad();
else
m_PreloadRequest.completed += operation => BeginAssetLoad();
}
}
private void BeginAssetLoad()
{
if (m_AssetBundle == null)
{
m_ProvideHandle.Complete<AssetBundle>(null, false, new Exception("Unable to load dependent bundle from location " + m_ProvideHandle.Location));
}
else
{
var assetPath = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
if (m_ProvideHandle.Type.IsArray)
{
#if !UNITY_2021_1_OR_NEWER
if (AsyncOperationHandle.IsWaitingForCompletion)
{
GetArrayResult(m_AssetBundle.LoadAssetWithSubAssets(assetPath, m_ProvideHandle.Type.GetElementType()));
CompleteOperation();
}
else
#endif
m_RequestOperation = m_AssetBundle.LoadAssetWithSubAssetsAsync(assetPath, m_ProvideHandle.Type.GetElementType());
}
else if (m_ProvideHandle.Type.IsGenericType && typeof(IList<>) == m_ProvideHandle.Type.GetGenericTypeDefinition())
{
#if !UNITY_2021_1_OR_NEWER
if (AsyncOperationHandle.IsWaitingForCompletion)
{
GetListResult(m_AssetBundle.LoadAssetWithSubAssets(assetPath, m_ProvideHandle.Type.GetGenericArguments()[0]));
CompleteOperation();
}
else
#endif
m_RequestOperation = m_AssetBundle.LoadAssetWithSubAssetsAsync(assetPath, m_ProvideHandle.Type.GetGenericArguments()[0]);
}
else
{
if (ResourceManagerConfig.ExtractKeyAndSubKey(assetPath, out string mainPath, out string subKey))
{
subObjectName = subKey;
#if !UNITY_2021_1_OR_NEWER
if (AsyncOperationHandle.IsWaitingForCompletion)
{
GetAssetSubObjectResult(m_AssetBundle.LoadAssetWithSubAssets(mainPath, m_ProvideHandle.Type));
CompleteOperation();
}
else
#endif
m_RequestOperation = m_AssetBundle.LoadAssetWithSubAssetsAsync(mainPath, m_ProvideHandle.Type);
}
else
{
#if !UNITY_2021_1_OR_NEWER
if (AsyncOperationHandle.IsWaitingForCompletion)
{
GetAssetResult(m_AssetBundle.LoadAsset(assetPath, m_ProvideHandle.Type));
CompleteOperation();
}
else
#endif
m_RequestOperation = m_AssetBundle.LoadAssetAsync(assetPath, m_ProvideHandle.Type);
}
}
if (m_RequestOperation != null)
{
if (m_RequestOperation.isDone)
ActionComplete(m_RequestOperation);
else
{
#if ENABLE_ADDRESSABLE_PROFILER
if (UnityEngine.Profiling.Profiler.enabled && m_ProvideHandle.IsValid)
Profiling.ProfilerRuntime.AddAssetOperation(m_ProvideHandle, Profiling.ContentStatus.Loading);
#endif
m_RequestOperation.completed += ActionComplete;
}
}
}
}
private bool WaitForCompletionHandler()
{
if (m_PreloadRequest != null && !m_PreloadRequest.isDone)
return m_PreloadRequest.asset == null;
if (m_Result != null)
return true;
if (m_RequestOperation == null)
return false;
if (m_RequestOperation.isDone)
return true;
return m_RequestOperation.asset != null;
}
private void ActionComplete(AsyncOperation obj)
{
if (m_RequestOperation != null)
{
if (m_ProvideHandle.Type.IsArray)
GetArrayResult(m_RequestOperation.allAssets);
else if (m_ProvideHandle.Type.IsGenericType && typeof(IList<>) == m_ProvideHandle.Type.GetGenericTypeDefinition())
GetListResult(m_RequestOperation.allAssets);
else if (string.IsNullOrEmpty(subObjectName))
GetAssetResult(m_RequestOperation.asset);
else
GetAssetSubObjectResult(m_RequestOperation.allAssets);
}
CompleteOperation();
}
private void GetArrayResult(Object[] allAssets)
{
m_Result = ResourceManagerConfig.CreateArrayResult(m_ProvideHandle.Type, allAssets);
}
private void GetListResult(Object[] allAssets)
{
m_Result = ResourceManagerConfig.CreateListResult(m_ProvideHandle.Type, allAssets);
}
private void GetAssetResult(Object asset)
{
m_Result = (asset != null && m_ProvideHandle.Type.IsAssignableFrom(asset.GetType())) ? asset : null;
}
private void GetAssetSubObjectResult(Object[] allAssets)
{
foreach (var o in allAssets)
{
if (o.name == subObjectName)
{
if (m_ProvideHandle.Type.IsAssignableFrom(o.GetType()))
{
m_Result = o;
break;
}
}
}
}
void CompleteOperation()
{
#if ENABLE_ADDRESSABLE_PROFILER
if (UnityEngine.Profiling.Profiler.enabled && m_Result != null && m_ProvideHandle.IsValid)
Profiling.ProfilerRuntime.AddAssetOperation(m_ProvideHandle, Profiling.ContentStatus.Active);
#endif
Exception e = m_Result == null
? new Exception($"Unable to load asset of type {m_ProvideHandle.Type} from location {m_ProvideHandle.Location}.")
: null;
m_ProvideHandle.Complete(m_Result, m_Result != null, e);
}
public float ProgressCallback()
{
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
}
}
/// <inheritdoc/>
public override void Provide(ProvideHandle provideHandle)
{
new InternalOp().Start(provideHandle);
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a286e4526d8a69d4cb47c5d75f5acc83
timeCreated: 1491321970
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Class that contains properties to apply to instantiated objects.
/// </summary>
public struct InstantiationParameters
{
Vector3 m_Position;
Quaternion m_Rotation;
Transform m_Parent;
bool m_InstantiateInWorldPosition;
bool m_SetPositionRotation;
/// <summary>
/// Position in world space to instantiate object.
/// </summary>
public Vector3 Position
{
get { return m_Position; }
}
/// <summary>
/// Rotation in world space to instantiate object.
/// </summary>
public Quaternion Rotation
{
get { return m_Rotation; }
}
/// <summary>
/// Transform to set as the parent of the instantiated object.
/// </summary>
public Transform Parent
{
get { return m_Parent; }
}
/// <summary>
/// When setting the parent Transform, this sets whether to preserve instance transform relative to world space or relative to the parent.
/// </summary>
public bool InstantiateInWorldPosition
{
get { return m_InstantiateInWorldPosition; }
}
/// <summary>
/// Flag to tell the IInstanceProvider whether to set the position and rotation on new instances.
/// </summary>
public bool SetPositionRotation
{
get { return m_SetPositionRotation; }
}
/// <summary>
/// Create a new InstantationParameters class that will set the parent transform and use the prefab transform.
/// </summary>
/// <param name="parent">Transform to set as the parent of the instantiated object.</param>
/// <param name="instantiateInWorldSpace">Flag to tell the IInstanceProvider whether to set the position and rotation on new instances.</param>
public InstantiationParameters(Transform parent, bool instantiateInWorldSpace)
{
m_Position = Vector3.zero;
m_Rotation = Quaternion.identity;
m_Parent = parent;
m_InstantiateInWorldPosition = instantiateInWorldSpace;
m_SetPositionRotation = false;
}
/// <summary>
/// Create a new InstantationParameters class that will set the position, rotation, and Transform parent of the instance.
/// </summary>
/// <param name="position">Position relative to the parent to set on the instance.</param>
/// <param name="rotation">Rotation relative to the parent to set on the instance.</param>
/// <param name="parent">Transform to set as the parent of the instantiated object.</param>
public InstantiationParameters(Vector3 position, Quaternion rotation, Transform parent)
{
m_Position = position;
m_Rotation = rotation;
m_Parent = parent;
m_InstantiateInWorldPosition = false;
m_SetPositionRotation = true;
}
/// <summary>
/// Instantiate an object with the parameters of this object.
/// </summary>
/// <typeparam name="TObject">Object type. This type must be of type UnityEngine.Object.</typeparam>
/// <param name="source">Object to instantiate.</param>
/// <returns>Returns the instantiated object.</returns>
public TObject Instantiate<TObject>(TObject source) where TObject : Object
{
TObject result;
if (m_Parent == null)
{
if (m_SetPositionRotation)
result = Object.Instantiate(source, m_Position, m_Rotation);
else
result = Object.Instantiate(source);
}
else
{
if (m_SetPositionRotation)
result = Object.Instantiate(source, m_Position, m_Rotation, m_Parent);
else
result = Object.Instantiate(source, m_Parent, m_InstantiateInWorldPosition);
}
return result;
}
}
/// <summary>
/// Interface that provides instances of objects. This is used in ResourceManager.Instantiate* calls.
/// </summary>
public interface IInstanceProvider
{
/// <summary>
/// Provide an instance of the gameobject contained in the prefabHandle.
/// </summary>
/// <param name="resourceManager">The object that contains all the resource locations.</param>
/// <param name="prefabHandle">The operation handle for the prefab to instantiate.</param>
/// <param name="instantiateParameters">The parameters to use for instantation.</param>
/// <returns>The instantiated object.</returns>
GameObject ProvideInstance(ResourceManager resourceManager, AsyncOperationHandle<GameObject> prefabHandle, InstantiationParameters instantiateParameters);
/// <summary>
/// Release an instance.
/// </summary>
/// <param name="resourceManager">The object that contains all the resource locations.</param>
/// <param name="instance">The instance to release.</param>
void ReleaseInstance(ResourceManager resourceManager, GameObject instance);
}
}

View file

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

View file

@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.SceneManagement;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Options for resource provider behavior.
/// </summary>
public enum ProviderBehaviourFlags
{
/// <summary>
/// Indicates that the provider does not have extra specified behavior.
/// </summary>
None = 0,
/// <summary>
/// Indicates that the provider will still fulfill requests even with failed dependencies.
/// </summary>
CanProvideWithFailedDependencies = 1
}
/// <summary>
/// Container for all data need by providers to fulfill requests.
/// </summary>
public struct ProvideHandle
{
int m_Version;
IGenericProviderOperation m_InternalOp;
ResourceManager m_ResourceManager;
internal ProvideHandle(ResourceManager rm, IGenericProviderOperation op)
{
m_ResourceManager = rm;
m_InternalOp = op;
m_Version = op.ProvideHandleVersion;
}
internal bool IsValid => m_InternalOp != null && m_InternalOp.ProvideHandleVersion == m_Version;
internal IGenericProviderOperation InternalOp
{
get
{
if (m_InternalOp.ProvideHandleVersion != m_Version)
{
throw new Exception(ProviderOperation<object>.kInvalidHandleMsg);
}
return m_InternalOp;
}
}
/// <summary>
/// The ResourceManager used to create the operation.
/// </summary>
public ResourceManager ResourceManager
{
get { return m_ResourceManager; }
}
/// <summary>
/// The requested object type.
/// </summary>
public Type Type
{
get { return InternalOp.RequestedType; }
}
/// <summary>
/// The location for the request.
/// </summary>
public IResourceLocation Location
{
get { return InternalOp.Location; }
}
/// <summary>
/// Number of dependencies.
/// </summary>
public int DependencyCount
{
get { return InternalOp.DependencyCount; }
}
/// <summary>
/// Get a specific dependency object.
/// </summary>
/// <typeparam name="TDepObject">The dependency type.</typeparam>
/// <param name="index">The index of the dependency.</param>
/// <returns>The dependency object.</returns>
public TDepObject GetDependency<TDepObject>(int index)
{
return InternalOp.GetDependency<TDepObject>(index);
}
/// <summary>
/// Get the depedency objects.
/// </summary>
/// <param name="list">The list of dependecies to fill in.</param>
public void GetDependencies(IList<object> list)
{
InternalOp.GetDependencies(list);
}
/// <summary>
/// Set the func for handling progress requests.
/// </summary>
/// <param name="callback">The callback function.</param>
public void SetProgressCallback(Func<float> callback)
{
InternalOp.SetProgressCallback(callback);
}
/// <summary>
/// Set the func for handling download progress requests.
/// </summary>
/// <param name="callback">The callback function.</param>
public void SetDownloadProgressCallbacks(Func<DownloadStatus> callback)
{
InternalOp.SetDownloadProgressCallback(callback);
}
/// <summary>
/// Set the func for handling a request to wait for the completion of the operation
/// </summary>
/// <param name="callback">The callback function.</param>
public void SetWaitForCompletionCallback(Func<bool> callback)
{
InternalOp.SetWaitForCompletionCallback(callback);
}
/// <summary>
/// Called to complete the operation.
/// </summary>
/// <typeparam name="T">The type of object requested.</typeparam>
/// <param name="result">The result object.</param>
/// <param name="status">True if the operation was successful, false otherwise.</param>
/// <param name="exception">The exception if the operation failed.</param>
public void Complete<T>(T result, bool status, Exception exception)
{
InternalOp.ProviderCompleted<T>(result, status, exception);
}
}
/// <summary>
/// Resoure Providers handle loading (Provide) and unloading (Release) of objects
/// </summary>
public interface IResourceProvider
{
/// <summary>
/// Unique identifier for this provider, used by Resource Locations to find a suitable Provider
/// </summary>
/// <value>The provider identifier.</value>
string ProviderId { get; }
/// <summary>
/// The default type of object that this provider can provide.
/// </summary>
/// <param name="location">The location that can be used to determine the type.</param>
/// <returns>The type of object that can be provided.</returns>
Type GetDefaultType(IResourceLocation location);
/// <summary>
/// Determine if this provider can provide the specified object type from the specified location.
/// </summary>
/// <param name="type">The type of object.</param>
/// <param name="location">The resource location of the object.</param>
/// <returns>True if this provider can create the specified object.</returns>
bool CanProvide(Type type, IResourceLocation location);
/// <summary>
/// Tells the provide that it needs to provide a resource and report the results through the passed provideHandle. When this is called, all dependencies have completed and are available through the provideHandle.
/// </summary>
/// <param name="provideHandle">A handle used to update the operation.</param>
void Provide(ProvideHandle provideHandle);
/// <summary>
/// Release and/or unload the given resource location and asset
/// </summary>
/// <param name="location">Location to release.</param>
/// <param name="asset">Asset to unload.</param>
void Release(IResourceLocation location, object asset);
/// <summary>
/// Custom flags for the provider.
/// </summary>
ProviderBehaviourFlags BehaviourFlags { get; }
}
}

View file

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

View file

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.SceneManagement;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Wrapper for scenes. This is used to allow access to the AsyncOperation and delayed activation.
/// </summary>
public struct SceneInstance
{
Scene m_Scene;
internal AsyncOperation m_Operation;
/// <summary>
/// The scene instance.
/// </summary>
public Scene Scene
{
get { return m_Scene; }
internal set { m_Scene = value; }
}
/// <summary>
/// Activate the scene via the AsyncOperation.
/// </summary>
[Obsolete("Activate() has been deprecated. Please use ActivateAsync().")]
public void Activate()
{
m_Operation.allowSceneActivation = true;
}
/// <summary>
/// Activate the scene via the AsyncOperation. This is the scene loading AsyncOperation provided by the engine.
/// The documentation for AsyncOperation can be found here: https://docs.unity3d.com/ScriptReference/AsyncOperation.html
/// </summary>
/// <returns>The scene load operation.</returns>
public AsyncOperation ActivateAsync()
{
m_Operation.allowSceneActivation = true;
return m_Operation;
}
///<inheritdoc cref="Scene"/>
public override int GetHashCode()
{
return Scene.GetHashCode();
}
/// <inheritdoc cref="Scene"/>
public override bool Equals(object obj)
{
if (!(obj is SceneInstance))
return false;
return Scene.Equals(((SceneInstance)obj).Scene);
}
}
/// <summary>
/// Interface for scene providers.
/// </summary>
public interface ISceneProvider
{
/// <summary>
/// Load a scene at a specified resource location.
/// </summary>
/// <param name="resourceManager">The resource manager to use for loading dependencies.</param>
/// <param name="location">The location of the scene.</param>
/// <param name="loadMode">Load mode for the scene.</param>
/// <param name="activateOnLoad">If true, the scene is activated as soon as it finished loading. Otherwise it needs to be activated via the returned SceneInstance object.</param>
/// <param name="priority">The loading priority for the load.</param>
/// <returns>An operation handle for the loading of the scene. The scene is wrapped in a SceneInstance object to support delayed activation.</returns>
AsyncOperationHandle<SceneInstance> ProvideScene(ResourceManager resourceManager, IResourceLocation location, LoadSceneMode loadMode, bool activateOnLoad, int priority);
/// <summary>
/// Load a scene at a specified resource location.
/// </summary>
/// <param name="resourceManager">The resource manager to use for loading dependencies.</param>
/// <param name="location">The location of the scene.</param>
/// <param name="loadSceneParameters">Load parameters for the scene.</param>
/// <param name="activateOnLoad">If true, the scene is activated as soon as it finished loading. Otherwise it needs to be activated via the returned SceneInstance object.</param>
/// <param name="priority">The loading priority for the load.</param>
/// <returns>An operation handle for the loading of the scene. The scene is wrapped in a SceneInstance object to support delayed activation.</returns>
AsyncOperationHandle<SceneInstance> ProvideScene(ResourceManager resourceManager, IResourceLocation location, LoadSceneParameters loadSceneParameters, bool activateOnLoad, int priority);
/// <summary>
/// Release a scene.
/// </summary>
/// <param name="resourceManager">The resource manager to use for loading dependencies.</param>
/// <param name="sceneLoadHandle">The operation handle used to load the scene.</param>
/// <returns>An operation handle for the unload.</returns>
AsyncOperationHandle<SceneInstance> ReleaseScene(ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle);
}
internal interface ISceneProvider2 : ISceneProvider
{
/// <summary>
/// Release a scene.
/// </summary>
/// <param name="resourceManager">The resource manager to use for loading dependencies.</param>
/// <param name="sceneLoadHandle">The operation handle used to load the scene.</param>
/// <returns>An operation handle for the unload.</returns>
AsyncOperationHandle<SceneInstance> ReleaseScene(ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle, UnloadSceneOptions unloadOptions);
}
static internal class SceneProviderExtensions
{
public static AsyncOperationHandle<SceneInstance> ReleaseScene(this ISceneProvider provider, ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle,
UnloadSceneOptions unloadOptions)
{
if (provider is ISceneProvider2)
return ((ISceneProvider2)provider).ReleaseScene(resourceManager, sceneLoadHandle, unloadOptions);
return provider.ReleaseScene(resourceManager, sceneLoadHandle);
}
}
}

View file

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

View file

@ -0,0 +1,14 @@
namespace UnityEngine.ResourceManagement
{
/// <summary>
/// Providers that implement this interface will received Update calls from the ResourceManager each frame
/// </summary>
public interface IUpdateReceiver
{
/// <summary>
/// This will be called once per frame by the ResourceManager
/// </summary>
/// <param name="unscaledDeltaTime">The unscaled delta time since the last invocation of this function</param>
void Update(float unscaledDeltaTime);
}
}

View file

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

View file

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.SceneManagement;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Basic implementation of IInstanceProvider.
/// </summary>
public class InstanceProvider : IInstanceProvider
{
Dictionary<GameObject, AsyncOperationHandle<GameObject>> m_InstanceObjectToPrefabHandle = new Dictionary<GameObject, AsyncOperationHandle<GameObject>>();
/// <inheritdoc/>
public GameObject ProvideInstance(ResourceManager resourceManager, AsyncOperationHandle<GameObject> prefabHandle, InstantiationParameters instantiateParameters)
{
GameObject result = instantiateParameters.Instantiate(prefabHandle.Result);
m_InstanceObjectToPrefabHandle.Add(result, prefabHandle);
return result;
}
/// <inheritdoc/>
public void ReleaseInstance(ResourceManager resourceManager, GameObject instance)
{
AsyncOperationHandle<GameObject> resource;
if (!m_InstanceObjectToPrefabHandle.TryGetValue(instance, out resource))
{
Debug.LogWarningFormat("Releasing unknown GameObject {0} to InstanceProvider.", instance);
}
else
{
resourceManager.Release(resource);
m_InstanceObjectToPrefabHandle.Remove(instance);
}
if (instance != null)
{
if (Application.isPlaying)
Object.Destroy(instance);
else
Object.DestroyImmediate(instance);
}
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 80a4732d85a576b4da4c5492957b9189
timeCreated: 1491321970
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,23 @@
using System;
using System.ComponentModel;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Converts JSON serialized text into the requested object.
/// </summary>
[DisplayName("JSON Asset Provider")]
public class JsonAssetProvider : TextDataProvider
{
/// <summary>
/// Converts raw text into requested object type via JSONUtility.FromJson.
/// </summary>
/// <param name="type">The object type the text is converted to.</param>
/// <param name="text">The text to convert.</param>
/// <returns>Returns the converted object.</returns>
public override object Convert(Type type, string text)
{
return JsonUtility.FromJson(text, type);
}
}
}

View file

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

View file

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Provides assets loaded via Resources.LoadAsync API.
/// </summary>
[DisplayName("Assets from Legacy Resources")]
public class LegacyResourcesProvider : ResourceProviderBase
{
internal class InternalOp
{
ResourceRequest m_RequestOperation;
ProvideHandle m_ProvideHandle;
public void Start(ProvideHandle provideHandle)
{
m_ProvideHandle = provideHandle;
provideHandle.SetProgressCallback(PercentComplete);
provideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
m_RequestOperation = Resources.LoadAsync(m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location), m_ProvideHandle.Type);
m_RequestOperation.completed += AsyncOperationCompleted;
}
private bool WaitForCompletionHandler()
{
if (!m_RequestOperation.isDone && Mathf.Approximately(m_RequestOperation.progress, 1f))
{
m_RequestOperation.completed -= AsyncOperationCompleted;
AsyncOperationCompleted(m_RequestOperation);
return true;
}
return m_RequestOperation != null && m_RequestOperation.isDone;
}
private void AsyncOperationCompleted(AsyncOperation op)
{
var request = op as ResourceRequest;
object result = request != null ? request.asset : null;
result = result != null && m_ProvideHandle.Type.IsAssignableFrom(result.GetType()) ? result : null;
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);
}
public float PercentComplete()
{
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
}
}
/// <inheritdoc/>
public override void Provide(ProvideHandle pi)
{
Type t = pi.Type;
bool isList = t.IsGenericType && typeof(IList<>) == t.GetGenericTypeDefinition();
var internalId = pi.ResourceManager.TransformInternalId(pi.Location);
if (t.IsArray || isList)
{
object result = null;
if (t.IsArray)
result = ResourceManagerConfig.CreateArrayResult(t, Resources.LoadAll(internalId, t.GetElementType()));
else
result = ResourceManagerConfig.CreateListResult(t, Resources.LoadAll(internalId, t.GetGenericArguments()[0]));
pi.Complete(result, result != null, result == null ? new Exception($"Unable to load asset of type {pi.Type} from location {pi.Location}.") : null);
}
else
{
if (ResourceManagerConfig.ExtractKeyAndSubKey(internalId, out string mainPath, out string subKey))
{
var objs = Resources.LoadAll(mainPath, pi.Type);
object result = null;
foreach (var o in objs)
{
if (o.name == subKey)
{
if (pi.Type.IsAssignableFrom(o.GetType()))
{
result = o;
break;
}
}
}
pi.Complete(result, result != null, result == null ? new Exception($"Unable to load asset of type {pi.Type} from location {pi.Location}.") : null);
}
else
{
new InternalOp().Start(pi);
}
}
}
/// <inheritdoc/>
public override void Release(IResourceLocation location, object asset)
{
if (location == null)
throw new ArgumentNullException("location");
var obj = asset as Object;
//GameObjects cannot be resleased via Object.Destroy because they are considered an asset
//but they can't be unloaded via Resources.UnloadAsset since they are NOT an asset?
if (obj != null && !(obj is GameObject))
Resources.UnloadAsset(obj);
}
}
}

View file

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

View file

@ -0,0 +1,165 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Base class for IResourceProvider.
/// </summary>
public abstract class ResourceProviderBase : IResourceProvider, IInitializableObject
{
/// <summary>
/// The unique identifier of the provider.
/// </summary>
protected string m_ProviderId;
/// <summary>
/// The extra behavior of the provider.
/// </summary>
protected ProviderBehaviourFlags m_BehaviourFlags = ProviderBehaviourFlags.None;
/// <inheritdoc/>
public virtual string ProviderId
{
get
{
if (string.IsNullOrEmpty(m_ProviderId))
m_ProviderId = GetType().FullName;
return m_ProviderId;
}
}
/// <inheritdoc/>
public virtual bool Initialize(string id, string data)
{
m_ProviderId = id;
return !string.IsNullOrEmpty(m_ProviderId);
}
/// <inheritdoc/>
public virtual bool CanProvide(Type t, IResourceLocation location)
{
return GetDefaultType(location).IsAssignableFrom(t);
}
/// <summary>
/// Converts information about the resource provider to a formatted string.
/// </summary>
/// <returns>Returns information about the resource provider.</returns>
public override string ToString()
{
return ProviderId;
}
/// <summary>
/// Release the specified object that was created from the specified location.
/// </summary>
/// <param name="location">The location of the object</param>
/// <param name="obj">The object to release.</param>
public virtual void Release(IResourceLocation location, object obj)
{
}
/// <summary>
/// Get the default type of object that this provider can provide.
/// </summary>
/// <param name="location"></param>
/// <returns></returns>
public virtual Type GetDefaultType(IResourceLocation location)
{
return typeof(object);
}
/// <summary>
/// Provide the object specified in the provideHandle.
/// </summary>
/// <param name="provideHandle">Contains all data needed to provide the requested object.</param>
public abstract void Provide(ProvideHandle provideHandle);
/// <inheritdoc/>
public virtual AsyncOperationHandle<bool> InitializeAsync(ResourceManager rm, string id, string data)
{
BaseInitAsyncOp baseInitOp = new BaseInitAsyncOp();
baseInitOp.Init(() => Initialize(id, data));
return rm.StartOperation(baseInitOp, default);
}
ProviderBehaviourFlags IResourceProvider.BehaviourFlags
{
get { return m_BehaviourFlags; }
}
class BaseInitAsyncOp : AsyncOperationBase<bool>
{
private Func<bool> m_CallBack;
public void Init(Func<bool> callback)
{
m_CallBack = callback;
}
///<inheritdoc />
protected override bool InvokeWaitForCompletion()
{
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
return true;
}
protected override void Execute()
{
if (m_CallBack != null)
Complete(m_CallBack(), true, "");
else
Complete(true, true, "");
}
}
}
/// <summary>
/// Contains options used in Resource Provider load requests. ProviderLoadRequestOptions are used to specify
/// parameters such as whether or not to ignore load failures and UnityWebRequest timeouts.
/// </summary>
[Serializable]
public class ProviderLoadRequestOptions
{
[SerializeField]
private bool m_IgnoreFailures = false;
private int m_WebRequestTimeout = 0;
/// <summary>
/// Creates a memberwise clone of a given ProviderLoadRequestOption.
/// </summary>
/// <returns>The newly created ProviderLoadRequestOption object</returns>
public ProviderLoadRequestOptions Copy()
{
return (ProviderLoadRequestOptions)this.MemberwiseClone();
}
/// <summary>
/// IgnoreFailures for provider load requests
/// </summary>
public bool IgnoreFailures
{
get { return m_IgnoreFailures; }
set { m_IgnoreFailures = value; }
}
/// <summary>
/// UnityWebRequest Timeout
/// </summary>
public int WebRequestTimeout
{
get => m_WebRequestTimeout;
set => m_WebRequestTimeout = value;
}
}
}

View file

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

View file

@ -0,0 +1,308 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using UnityEngine.Assertions.Must;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.SceneManagement;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Implementation if ISceneProvider
/// </summary>
public class SceneProvider : ISceneProvider2
{
class SceneOp : AsyncOperationBase<SceneInstance>, IUpdateReceiver
{
bool m_ActivateOnLoad;
SceneInstance m_Inst;
IResourceLocation m_Location;
LoadSceneParameters m_LoadSceneParameters;
int m_Priority;
private AsyncOperationHandle<IList<AsyncOperationHandle>> m_DepOp;
ResourceManager m_ResourceManager;
public SceneOp(ResourceManager rm)
{
m_ResourceManager = rm;
}
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
{
return m_DepOp.IsValid() ? m_DepOp.InternalGetDownloadStatus(visited) : new DownloadStatus() { IsDone = IsDone };
}
public void Init(IResourceLocation location, LoadSceneMode loadSceneMode, bool activateOnLoad, int priority, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp)
{
Init(location, new LoadSceneParameters(loadSceneMode), activateOnLoad, priority, depOp);
}
public void Init(IResourceLocation location, LoadSceneParameters loadSceneParameters, bool activateOnLoad, int priority, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp)
{
m_DepOp = depOp;
if (m_DepOp.IsValid())
m_DepOp.Acquire();
m_Location = location;
m_LoadSceneParameters = loadSceneParameters;
m_ActivateOnLoad = activateOnLoad;
m_Priority = priority;
}
///<inheritdoc />
protected override bool InvokeWaitForCompletion()
{
if (m_DepOp.IsValid() && !m_DepOp.IsDone)
m_DepOp.WaitForCompletion();
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
var timer = new Stopwatch();
timer.Start();
while (!IsDone)
{
((IUpdateReceiver)this).Update(Time.unscaledDeltaTime);
//We need the operation to complete but it'll take a frame to activate the scene (post 0.9 progress).
if (m_Inst.m_Operation.progress == 0 && timer.ElapsedMilliseconds > 5000)
throw new Exception(
"Infinite loop detected within LoadSceneAsync.WaitForCompletion. For more information see the notes under the Scenes section of the \"Synchronous Addressables\" page of the Addressables documentation, or consider using asynchronous scene loading code.");
if (m_Inst.m_Operation.allowSceneActivation && Mathf.Approximately(m_Inst.m_Operation.progress, .9f))
{
Result = m_Inst;
return true;
}
}
return IsDone;
}
/// <inheritdoc />
public override void GetDependencies(List<AsyncOperationHandle> deps)
{
if (m_DepOp.IsValid())
deps.Add(m_DepOp);
}
protected override string DebugName
{
get { return string.Format("Scene({0})", m_Location == null ? "Invalid" : ShortenPath(m_ResourceManager.TransformInternalId(m_Location), false)); }
}
protected override void Execute()
{
var loadingFromBundle = false;
if (m_DepOp.IsValid())
{
foreach (var d in m_DepOp.Result)
{
var abResource = d.Result as IAssetBundleResource;
if (abResource != null && abResource.GetAssetBundle() != null)
loadingFromBundle = true;
}
}
if (!m_DepOp.IsValid() || m_DepOp.OperationException == null)
{
m_Inst = InternalLoadScene(m_Location, loadingFromBundle, m_LoadSceneParameters, m_ActivateOnLoad, m_Priority);
((IUpdateReceiver)this).Update(0.0f);
}
else
{
Complete(m_Inst, false, m_DepOp.OperationException);
}
HasExecuted = true;
}
internal SceneInstance InternalLoadScene(IResourceLocation location, bool loadingFromBundle, LoadSceneParameters loadSceneParameters, bool activateOnLoad, int priority)
{
var internalId = m_ResourceManager.TransformInternalId(location);
var op = InternalLoad(internalId, loadingFromBundle, loadSceneParameters);
op.allowSceneActivation = activateOnLoad;
op.priority = priority;
return new SceneInstance() { m_Operation = op, Scene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1) };
}
AsyncOperation InternalLoad(string path, bool loadingFromBundle, LoadSceneParameters loadSceneParameters)
{
#if !UNITY_EDITOR
#if ENABLE_ADDRESSABLE_PROFILER
Profiling.ProfilerRuntime.AddSceneOperation(Handle, m_Location, Profiling.ContentStatus.Loading);
#endif
return SceneManager.LoadSceneAsync(path, loadSceneParameters);
#else
if (loadingFromBundle)
{
#if ENABLE_ADDRESSABLE_PROFILER
Profiling.ProfilerRuntime.AddSceneOperation(Handle, m_Location, Profiling.ContentStatus.Loading);
#endif
return SceneManager.LoadSceneAsync(path, loadSceneParameters);
}
else
{
if (!path.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase) && !path.StartsWith("Packages/", StringComparison.OrdinalIgnoreCase))
path = "Assets/" + path;
if (path.LastIndexOf(".unity", StringComparison.OrdinalIgnoreCase) == -1)
path += ".unity";
return UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(path, loadSceneParameters);
}
#endif
}
protected override void Destroy()
{
//the scene will be unloaded via the UnloadSceneOp as it waits correctlyf or the unload to complete before releasing the load op
if (m_DepOp.IsValid())
m_DepOp.Release();
base.Destroy();
}
protected override float Progress
{
get
{
float depOpWeight = 0.9f;
float loadOpWeight = 0.1f;
float progress = 0f;
//We will always have an instance operation but this will be null until the dependant operation is completed.
if (m_Inst.m_Operation != null)
progress += m_Inst.m_Operation.progress * loadOpWeight;
if (!m_DepOp.IsDone)
progress += m_DepOp.PercentComplete * depOpWeight;
else
progress += depOpWeight;
return progress;
}
}
void IUpdateReceiver.Update(float unscaledDeltaTime)
{
if (m_Inst.m_Operation != null)
{
if (m_Inst.m_Operation.isDone || (!m_Inst.m_Operation.allowSceneActivation && Mathf.Approximately(m_Inst.m_Operation.progress, .9f)))
{
m_ResourceManager.RemoveUpdateReciever(this);
#if ENABLE_ADDRESSABLE_PROFILER
Profiling.ProfilerRuntime.AddSceneOperation(Handle, m_Location, Profiling.ContentStatus.Active);
#endif
Complete(m_Inst, true, null);
}
}
}
}
class UnloadSceneOp : AsyncOperationBase<SceneInstance>
{
SceneInstance m_Instance;
AsyncOperationHandle<SceneInstance> m_sceneLoadHandle;
UnloadSceneOptions m_UnloadOptions;
public void Init(AsyncOperationHandle<SceneInstance> sceneLoadHandle, UnloadSceneOptions options)
{
if (sceneLoadHandle.ReferenceCount > 0)
{
m_sceneLoadHandle = sceneLoadHandle;
m_Instance = m_sceneLoadHandle.Result;
}
m_UnloadOptions = options;
}
protected override void Execute()
{
if (m_sceneLoadHandle.IsValid() && m_Instance.Scene.isLoaded)
{
var unloadOp = SceneManager.UnloadSceneAsync(m_Instance.Scene, m_UnloadOptions);
if (unloadOp == null)
UnloadSceneCompletedNoRelease(null);
else
unloadOp.completed += UnloadSceneCompletedNoRelease;
}
else
UnloadSceneCompleted(null);
HasExecuted = true;
}
///<inheritdoc />
protected override bool InvokeWaitForCompletion()
{
m_RM?.Update(Time.unscaledDeltaTime);
if (!HasExecuted)
InvokeExecute();
Debug.LogWarning("Cannot unload a Scene with WaitForCompletion. Scenes must be unloaded asynchronously.");
return true;
}
private void UnloadSceneCompleted(AsyncOperation obj)
{
Complete(m_Instance, true, "");
if (m_sceneLoadHandle.IsValid())
m_sceneLoadHandle.Release();
}
private void UnloadSceneCompletedNoRelease(AsyncOperation obj)
{
Complete(m_Instance, true, "");
}
protected override float Progress
{
get { return m_sceneLoadHandle.PercentComplete; }
}
}
/// <inheritdoc/>
public AsyncOperationHandle<SceneInstance> ProvideScene(ResourceManager resourceManager, IResourceLocation location, LoadSceneMode loadSceneMode, bool activateOnLoad, int priority)
{
return ProvideScene(resourceManager, location, new LoadSceneParameters(loadSceneMode), activateOnLoad, priority);
}
/// <inheritdoc/>
public AsyncOperationHandle<SceneInstance> ProvideScene(ResourceManager resourceManager, IResourceLocation location, LoadSceneParameters loadSceneParameters, bool activateOnLoad, int priority)
{
AsyncOperationHandle<IList<AsyncOperationHandle>> depOp = default(AsyncOperationHandle<IList<AsyncOperationHandle>>);
if (location.HasDependencies)
depOp = resourceManager.ProvideResourceGroupCached(location.Dependencies, location.DependencyHashCode, typeof(IAssetBundleResource), null);
SceneOp op = new SceneOp(resourceManager);
op.Init(location, loadSceneParameters, activateOnLoad, priority, depOp);
var handle = resourceManager.StartOperation<SceneInstance>(op, depOp);
if (depOp.IsValid())
depOp.Release();
return handle;
}
/// <inheritdoc/>
public AsyncOperationHandle<SceneInstance> ReleaseScene(ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle)
{
return ((ISceneProvider2)(this)).ReleaseScene(resourceManager, sceneLoadHandle, UnloadSceneOptions.None);
}
/// <inheritdoc/>
AsyncOperationHandle<SceneInstance> ISceneProvider2.ReleaseScene(ResourceManager resourceManager, AsyncOperationHandle<SceneInstance> sceneLoadHandle, UnloadSceneOptions unloadOptions)
{
var unloadOp = new UnloadSceneOp();
unloadOp.Init(sceneLoadHandle, unloadOptions);
#if ENABLE_ADDRESSABLE_PROFILER
Profiling.ProfilerRuntime.SceneReleased(sceneLoadHandle);
#endif
return resourceManager.StartOperation(unloadOp, sceneLoadHandle);
}
}
}

View file

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

View file

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

View file

@ -0,0 +1,611 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.Serialization;
namespace UnityEngine.ResourceManagement.ResourceProviders.Simulation
{
abstract class VBAsyncOperation
{
public abstract DownloadStatus GetDownloadStatus();
public abstract bool WaitForCompletion();
}
class VBAsyncOperation<TObject> : VBAsyncOperation
{
protected TObject m_Result;
protected AsyncOperationStatus m_Status;
protected Exception m_Error;
protected object m_Context;
DelegateList<VBAsyncOperation<TObject>> m_CompletedAction;
Action<VBAsyncOperation<TObject>> m_OnDestroyAction;
public override DownloadStatus GetDownloadStatus() => default;
public override bool WaitForCompletion() => true;
public override string ToString()
{
var instId = "";
var or = m_Result as Object;
if (or != null)
instId = "(" + or.GetInstanceID() + ")";
return string.Format("{0}, result='{1}', status='{2}', location={3}.", base.ToString(), (m_Result + instId), m_Status, m_Context);
}
public event Action<VBAsyncOperation<TObject>> Completed
{
add
{
if (IsDone)
{
DelayedActionManager.AddAction(value, 0, this);
}
else
{
if (m_CompletedAction == null)
m_CompletedAction = DelegateList<VBAsyncOperation<TObject>>.CreateWithGlobalCache();
m_CompletedAction.Add(value);
}
}
remove { m_CompletedAction.Remove(value); }
}
public AsyncOperationStatus Status
{
get { return m_Status; }
protected set { m_Status = value; }
}
/// <inheritdoc />
public Exception OperationException
{
get { return m_Error; }
protected set
{
m_Error = value;
if (m_Error != null && ResourceManager.ExceptionHandler != null)
ResourceManager.ExceptionHandler(new AsyncOperationHandle(null), value);
}
}
public TObject Result
{
get { return m_Result; }
}
public virtual bool IsDone
{
get { return Status == AsyncOperationStatus.Failed || Status == AsyncOperationStatus.Succeeded; }
}
/// <inheritdoc />
public virtual float PercentComplete
{
get { return IsDone ? 1f : 0f; }
}
/// <inheritdoc />
public object Context
{
get { return m_Context; }
set { m_Context = value; }
}
public void InvokeCompletionEvent()
{
if (m_CompletedAction != null)
{
m_CompletedAction.Invoke(this);
m_CompletedAction.Clear();
}
}
public virtual void SetResult(TObject result)
{
m_Result = result;
m_Status = (m_Result == null) ? AsyncOperationStatus.Failed : AsyncOperationStatus.Succeeded;
}
public VBAsyncOperation<TObject> StartCompleted(object context, object key, TObject val, Exception error = null)
{
Context = context;
OperationException = error;
m_Result = val;
m_Status = (m_Result == null) ? AsyncOperationStatus.Failed : AsyncOperationStatus.Succeeded;
DelayedActionManager.AddAction((Action)InvokeCompletionEvent);
return this;
}
}
/// <summary>
/// Contains data needed to simulate a bundled asset
/// </summary>
[Serializable]
public class VirtualAssetBundleEntry
{
[FormerlySerializedAs("m_name")]
[SerializeField]
string m_Name;
/// <summary>
/// The name of the asset.
/// </summary>
public string Name
{
get { return m_Name; }
}
[FormerlySerializedAs("m_size")]
[SerializeField]
long m_Size;
/// <summary>
/// The file size of the asset, in bytes.
/// </summary>
public long Size
{
get { return m_Size; }
}
[SerializeField]
internal string m_AssetPath;
/// <summary>
/// Construct a new VirtualAssetBundleEntry
/// </summary>
public VirtualAssetBundleEntry()
{
}
/// <summary>
/// Construct a new VirtualAssetBundleEntry
/// </summary>
/// <param name="name">The name of the asset.</param>
/// <param name="size">The size of the asset, in bytes.</param>
public VirtualAssetBundleEntry(string name, long size)
{
m_Name = name;
m_Size = size;
}
}
/// <summary>
/// Contains data need to simulate an asset bundle.
/// </summary>
[Serializable]
public class VirtualAssetBundle : ISerializationCallbackReceiver, IAssetBundleResource
{
[FormerlySerializedAs("m_name")]
[SerializeField]
string m_Name;
[FormerlySerializedAs("m_isLocal")]
[SerializeField]
bool m_IsLocal;
[FormerlySerializedAs("m_dataSize")]
[SerializeField]
long m_DataSize;
[FormerlySerializedAs("m_headerSize")]
[SerializeField]
long m_HeaderSize;
[FormerlySerializedAs("m_latency")]
[SerializeField]
float m_Latency;
[SerializeField]
uint m_Crc;
[SerializeField]
string m_Hash;
[FormerlySerializedAs("m_serializedAssets")]
[SerializeField]
List<VirtualAssetBundleEntry> m_SerializedAssets = new List<VirtualAssetBundleEntry>();
long m_HeaderBytesLoaded;
long m_DataBytesLoaded;
LoadAssetBundleOp m_BundleLoadOperation;
List<IVirtualLoadable> m_AssetLoadOperations = new List<IVirtualLoadable>();
Dictionary<string, VirtualAssetBundleEntry> m_AssetMap;
/// <summary>
/// The name of the bundle.
/// </summary>
public string Name
{
get { return m_Name; }
}
/// <summary>
/// The assets contained in the bundle.
/// </summary>
public List<VirtualAssetBundleEntry> Assets
{
get { return m_SerializedAssets; }
}
const long k_SynchronousBytesPerSecond = (long)1024 * 1024 * 1024 * 10; // 10 Gb/s
/// <summary>
/// Construct a new VirtualAssetBundle object.
/// </summary>
public VirtualAssetBundle()
{
}
/// <summary>
/// The percent of data that has been loaded.
/// </summary>
public float PercentComplete
{
get
{
if (m_HeaderSize + m_DataSize <= 0)
return 1;
return (float)(m_HeaderBytesLoaded + m_DataBytesLoaded) / (m_HeaderSize + m_DataSize);
}
}
/// <summary>
/// Construct a new VirtualAssetBundle
/// </summary>
/// <param name="name">The name of the bundle.</param>
/// <param name="local">Is the bundle local or remote. This is used to determine which bandwidth value to use when simulating loading.</param>
public VirtualAssetBundle(string name, bool local, uint crc, string hash)
{
m_Latency = .1f;
m_Name = name;
m_IsLocal = local;
m_HeaderBytesLoaded = 0;
m_DataBytesLoaded = 0;
m_Crc = crc;
m_Hash = hash;
}
/// <summary>
/// Set the size of the bundle.
/// </summary>
/// <param name="dataSize">The size of the data.</param>
/// <param name="headerSize">The size of the header.</param>
public void SetSize(long dataSize, long headerSize)
{
m_HeaderSize = headerSize;
m_DataSize = dataSize;
}
/// <summary>
/// Not used
/// </summary>
public void OnBeforeSerialize()
{
}
/// <summary>
/// Load serialized data into runtime structures.
/// </summary>
public void OnAfterDeserialize()
{
m_AssetMap = new Dictionary<string, VirtualAssetBundleEntry>();
foreach (var a in m_SerializedAssets)
m_AssetMap.Add(a.Name, a);
}
class LoadAssetBundleOp : VBAsyncOperation<VirtualAssetBundle>
{
VirtualAssetBundle m_Bundle;
float m_TimeInLoadingState;
bool m_crcHashValidated;
public LoadAssetBundleOp(IResourceLocation location, VirtualAssetBundle bundle)
{
Context = location;
m_Bundle = bundle;
m_TimeInLoadingState = 0.0f;
}
public override bool WaitForCompletion()
{
SetResult(m_Bundle);
InvokeCompletionEvent();
return true;
}
public override DownloadStatus GetDownloadStatus()
{
if (m_Bundle.m_IsLocal)
return new DownloadStatus() {IsDone = IsDone};
return new DownloadStatus() {DownloadedBytes = m_Bundle.m_DataBytesLoaded, TotalBytes = m_Bundle.m_DataSize, IsDone = IsDone};
}
public override float PercentComplete
{
get
{
if (IsDone)
return 1f;
return m_Bundle.PercentComplete;
}
}
public void Update(long localBandwidth, long remoteBandwidth, float unscaledDeltaTime)
{
if (m_Result != null)
return;
if (!m_crcHashValidated)
{
var location = Context as IResourceLocation;
var reqOptions = location.Data as AssetBundleRequestOptions;
if (reqOptions != null)
{
if (reqOptions.Crc != 0 && m_Bundle.m_Crc != reqOptions.Crc)
{
var err = string.Format("Error while downloading Asset Bundle: CRC Mismatch. Provided {0}, calculated {1} from data. Will not load Asset Bundle.", reqOptions.Crc,
m_Bundle.m_Crc);
SetResult(null);
OperationException = new Exception(err);
InvokeCompletionEvent();
}
if (!m_Bundle.m_IsLocal)
{
if (!string.IsNullOrEmpty(reqOptions.Hash))
{
if (string.IsNullOrEmpty(m_Bundle.m_Hash) || m_Bundle.m_Hash != reqOptions.Hash)
{
Debug.LogWarningFormat("Mismatched hash in bundle {0}.", m_Bundle.Name);
}
//TODO: implement virtual cache that would persist between runs.
//if(vCache.hashBundle(m_Bundle.Name, reqOptions.Hash))
// m_m_Bundle.IsLocal = true;
}
}
}
m_crcHashValidated = true;
}
m_TimeInLoadingState += unscaledDeltaTime;
if (m_TimeInLoadingState > m_Bundle.m_Latency)
{
long localBytes = (long)Math.Ceiling(unscaledDeltaTime * localBandwidth);
long remoteBytes = (long)Math.Ceiling(unscaledDeltaTime * remoteBandwidth);
if (m_Bundle.LoadData(localBytes, remoteBytes))
{
SetResult(m_Bundle);
InvokeCompletionEvent();
}
}
}
}
bool LoadData(long localBytes, long remoteBytes)
{
if (m_IsLocal)
{
m_HeaderBytesLoaded += localBytes;
if (m_HeaderBytesLoaded < m_HeaderSize)
return false;
m_HeaderBytesLoaded = m_HeaderSize;
return true;
}
else
{
if (m_DataBytesLoaded < m_DataSize)
{
m_DataBytesLoaded += remoteBytes;
if (m_DataBytesLoaded < m_DataSize)
return false;
m_DataBytesLoaded = m_DataSize;
return false;
}
m_HeaderBytesLoaded += localBytes;
if (m_HeaderBytesLoaded < m_HeaderSize)
return false;
m_HeaderBytesLoaded = m_HeaderSize;
return true;
}
}
internal bool Unload()
{
if (m_BundleLoadOperation == null)
Debug.LogWarningFormat("Simulated assetbundle {0} is already unloaded.", m_Name);
m_HeaderBytesLoaded = 0;
m_BundleLoadOperation = null;
return true;
}
internal VBAsyncOperation<VirtualAssetBundle> StartLoad(IResourceLocation location)
{
if (m_BundleLoadOperation != null)
{
if (m_BundleLoadOperation.IsDone)
Debug.LogWarningFormat("Simulated assetbundle {0} is already loaded.", m_Name);
else
Debug.LogWarningFormat("Simulated assetbundle {0} is already loading.", m_Name);
return m_BundleLoadOperation;
}
m_HeaderBytesLoaded = 0;
return (m_BundleLoadOperation = new LoadAssetBundleOp(location, this));
}
/// <summary>
/// Load an asset via its location. The asset will actually be loaded via the AssetDatabase API.
/// </summary>
/// <typeparam name="TObject"></typeparam>
/// <param name="location"></param>
/// <returns></returns>
internal VBAsyncOperation<object> LoadAssetAsync(ProvideHandle provideHandle, IResourceLocation location)
{
if (location == null)
throw new ArgumentException("IResourceLocation location cannot be null.");
if (m_BundleLoadOperation == null)
return new VBAsyncOperation<object>().StartCompleted(location, location, null, new ResourceManagerException("LoadAssetAsync called on unloaded bundle " + m_Name));
if (!m_BundleLoadOperation.IsDone)
return new VBAsyncOperation<object>().StartCompleted(location, location, null, new ResourceManagerException("LoadAssetAsync called on loading bundle " + m_Name));
VirtualAssetBundleEntry assetInfo;
var assetPath = location.InternalId;
if (ResourceManagerConfig.ExtractKeyAndSubKey(assetPath, out string mainPath, out string subKey))
assetPath = mainPath;
//this needs to use the non translated internal id since that was how the table was built.
if (!m_AssetMap.TryGetValue(assetPath, out assetInfo))
return new VBAsyncOperation<object>().StartCompleted(location, location, null,
new ResourceManagerException(string.Format("Unable to load asset {0} from simulated bundle {1}.", location.InternalId, Name)));
var op = new LoadAssetOp(location, assetInfo, provideHandle);
m_AssetLoadOperations.Add(op);
return op;
}
internal void CountBandwidthUsage(ref long localCount, ref long remoteCount)
{
if (m_BundleLoadOperation != null && m_BundleLoadOperation.IsDone)
{
localCount += m_AssetLoadOperations.Count;
return;
}
if (m_IsLocal)
{
localCount++;
}
else
{
if (m_DataBytesLoaded < m_DataSize)
remoteCount++;
else
localCount++;
}
}
interface IVirtualLoadable
{
bool Load(long localBandwidth, long remoteBandwidth, float unscaledDeltaTime);
}
// TODO: This is only needed internally. We can change this to not derive off of AsyncOperationBase and simplify the code
class LoadAssetOp : VBAsyncOperation<object>, IVirtualLoadable
{
long m_BytesLoaded;
float m_LastUpdateTime;
VirtualAssetBundleEntry m_AssetInfo;
ProvideHandle m_provideHandle;
public LoadAssetOp(IResourceLocation location, VirtualAssetBundleEntry assetInfo, ProvideHandle ph)
{
m_provideHandle = ph;
Context = location;
m_AssetInfo = assetInfo;
m_LastUpdateTime = Time.realtimeSinceStartup;
}
public override bool WaitForCompletion()
{
//TODO: this needs to just wait on the resourcemanager update loop to ensure proper loading order
while (!IsDone)
{
Load(k_SynchronousBytesPerSecond, k_SynchronousBytesPerSecond, .1f);
System.Threading.Thread.Sleep(100);
}
return true;
}
public override float PercentComplete
{
get { return Mathf.Clamp01(m_BytesLoaded / (float)m_AssetInfo.Size); }
}
public bool Load(long localBandwidth, long remoteBandwidth, float unscaledDeltaTime)
{
if (IsDone)
return false;
var now = m_LastUpdateTime + unscaledDeltaTime;
if (now > m_LastUpdateTime)
{
m_BytesLoaded += (long)Math.Ceiling((now - m_LastUpdateTime) * localBandwidth);
m_LastUpdateTime = now;
}
if (m_BytesLoaded < m_AssetInfo.Size)
return true;
if (!(Context is IResourceLocation))
return false;
var location = Context as IResourceLocation;
var assetPath = m_AssetInfo.m_AssetPath;
object result = null;
var pt = m_provideHandle.Type;
if (pt.IsArray)
result = ResourceManagerConfig.CreateArrayResult(pt, AssetDatabaseProvider.LoadAssetsWithSubAssets(assetPath));
else if (pt.IsGenericType && typeof(IList<>) == pt.GetGenericTypeDefinition())
result = ResourceManagerConfig.CreateListResult(pt, AssetDatabaseProvider.LoadAssetsWithSubAssets(assetPath));
else
{
if (ResourceManagerConfig.ExtractKeyAndSubKey(location.InternalId, out string mainPath, out string subKey))
result = AssetDatabaseProvider.LoadAssetSubObject(assetPath, subKey, pt);
else
result = AssetDatabaseProvider.LoadAssetAtPath(assetPath, m_provideHandle);
}
SetResult(result);
InvokeCompletionEvent();
return false;
}
}
//return true until complete
internal bool UpdateAsyncOperations(long localBandwidth, long remoteBandwidth, float unscaledDeltaTime)
{
if (m_BundleLoadOperation == null)
return false;
if (!m_BundleLoadOperation.IsDone)
{
m_BundleLoadOperation.Update(localBandwidth, remoteBandwidth, unscaledDeltaTime);
return true;
}
foreach (var o in m_AssetLoadOperations)
{
if (!o.Load(localBandwidth, remoteBandwidth, unscaledDeltaTime))
{
m_AssetLoadOperations.Remove(o);
break;
}
}
return m_AssetLoadOperations.Count > 0;
}
/// <summary>
/// Implementation of IAssetBundleResource API
/// </summary>
/// <returns>Always returns null.</returns>
public AssetBundle GetAssetBundle()
{
return null;
}
}
}
#endif

View file

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

View file

@ -0,0 +1,198 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.ResourceProviders.Simulation
{
/// <summary>
/// Simulates the loading behavior of an asset bundle. Internally it uses the AssetDatabase API. This provider will only work in the editor.
/// </summary>
[DisplayName("Virtual AssetBundle Provider")]
public class VirtualAssetBundleProvider : ResourceProviderBase, IUpdateReceiver
{
VirtualAssetBundleRuntimeData m_BundleData;
private VirtualAssetBundleProvider()
{
m_ProviderId = typeof(AssetBundleProvider).FullName;
}
/// <inheritdoc/>
public override Type GetDefaultType(IResourceLocation location)
{
return typeof(IAssetBundleResource);
}
/// <summary>
/// Construct a new VirtualAssetBundleProvider object;
/// <param name="data">Contains information on virtual bundle layout</param>
/// </summary>
public VirtualAssetBundleProvider(VirtualAssetBundleRuntimeData data)
{
InitializeInternal(typeof(AssetBundleProvider).FullName, data);
}
private bool InitializeInternal(string providerId, VirtualAssetBundleRuntimeData data)
{
m_ProviderId = providerId;
m_BundleData = data;
foreach (var b in m_BundleData.AssetBundles)
m_AllBundles.Add(b.Name, b);
return !string.IsNullOrEmpty(m_ProviderId);
}
/// <summary>
/// Initilization data is passed when when constructed from serialized data
/// </summary>
/// <param name="id">The provider id</param>
/// <param name="data">The data string, this is assumed to be the virtual bundle data path</param>
/// <returns>true if the data is as expected</returns>
public override bool Initialize(string id, string data)
{
VirtualAssetBundleRuntimeData bundleData = JsonUtility.FromJson<VirtualAssetBundleRuntimeData>(data);
return InitializeInternal(id, bundleData);
}
class InternalOp
{
VBAsyncOperation<VirtualAssetBundle> m_RequestOperation;
VirtualAssetBundleProvider m_Provider;
ProvideHandle m_PI;
public float GetPercentComplete()
{
return m_RequestOperation != null ? m_RequestOperation.PercentComplete : 0.0f;
}
DownloadStatus GetDownloadStatus()
{
return m_RequestOperation != null ? m_RequestOperation.GetDownloadStatus() : new DownloadStatus() {IsDone = GetPercentComplete() >= 1f};
}
public void Start(ProvideHandle provideHandle, VirtualAssetBundleProvider provider)
{
m_Provider = provider;
m_PI = provideHandle;
m_PI.SetWaitForCompletionCallback(WaitForCompletionHandler);
m_RequestOperation = m_Provider.LoadAsync(m_PI.Location);
m_PI.SetProgressCallback(GetPercentComplete);
m_PI.SetDownloadProgressCallbacks(GetDownloadStatus);
m_RequestOperation.Completed += bundleOp =>
{
object result = (bundleOp.Result != null && m_PI.Type.IsAssignableFrom(bundleOp.Result.GetType())) ? bundleOp.Result : null;
m_PI.Complete(result, (result != null && bundleOp.OperationException == null), bundleOp.OperationException);
};
}
private bool WaitForCompletionHandler()
{
return m_RequestOperation.WaitForCompletion();
}
}
public override void Provide(ProvideHandle provideHandle)
{
new InternalOp().Start(provideHandle, this);
}
/// <inheritdoc/>
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;
}
Unload(location);
}
bool m_UpdatingActiveBundles;
Dictionary<string, VirtualAssetBundle> m_PendingOperations = new Dictionary<string, VirtualAssetBundle>();
Dictionary<string, VirtualAssetBundle> m_AllBundles = new Dictionary<string, VirtualAssetBundle>();
Dictionary<string, VirtualAssetBundle> m_ActiveBundles = new Dictionary<string, VirtualAssetBundle>();
internal bool Unload(IResourceLocation location)
{
if (location == null)
throw new ArgumentException("IResourceLocation location cannot be null.");
VirtualAssetBundle bundle;
if (!m_AllBundles.TryGetValue(location.InternalId, out bundle))
{
Debug.LogWarningFormat("Unable to unload virtual bundle {0}.", location);
return false;
}
if (m_UpdatingActiveBundles)
m_PendingOperations.Add(location.InternalId, null);
else
m_ActiveBundles.Remove(location.InternalId);
return bundle.Unload();
}
internal VBAsyncOperation<VirtualAssetBundle> LoadAsync(IResourceLocation location)
{
if (location == null)
throw new ArgumentException("IResourceLocation location cannot be null.");
VirtualAssetBundle bundle;
if (!m_AllBundles.TryGetValue(location.InternalId, out bundle))
return new VBAsyncOperation<VirtualAssetBundle>().StartCompleted(location, location, default(VirtualAssetBundle),
new ResourceManagerException(string.Format("Unable to unload virtual bundle {0}.", location)));
try
{
if (m_UpdatingActiveBundles)
m_PendingOperations.Add(location.InternalId, bundle);
else
m_ActiveBundles.Add(location.InternalId, bundle);
}
catch (Exception ex)
{
Debug.LogException(ex);
}
return bundle.StartLoad(location);
}
internal void Update(float unscaledDeltaTime)
{
long localCount = 0;
long remoteCount = 0;
foreach (VirtualAssetBundle bundle in m_ActiveBundles.Values)
bundle.CountBandwidthUsage(ref localCount, ref remoteCount);
long localBw = localCount > 1 ? (m_BundleData.LocalLoadSpeed / localCount) : m_BundleData.LocalLoadSpeed;
long remoteBw = remoteCount > 1 ? (m_BundleData.RemoteLoadSpeed / remoteCount) : m_BundleData.RemoteLoadSpeed;
m_UpdatingActiveBundles = true;
foreach (VirtualAssetBundle bundle in m_ActiveBundles.Values)
bundle.UpdateAsyncOperations(localBw, remoteBw, unscaledDeltaTime);
m_UpdatingActiveBundles = false;
if (m_PendingOperations.Count > 0)
{
foreach (var o in m_PendingOperations)
{
if (o.Value == null)
m_ActiveBundles.Remove(o.Key);
else
m_ActiveBundles.Add(o.Key, o.Value);
}
m_PendingOperations.Clear();
}
}
void IUpdateReceiver.Update(float unscaledDeltaTime)
{
Update(unscaledDeltaTime);
}
}
}
#endif

View file

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

View file

@ -0,0 +1,70 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine.Serialization;
namespace UnityEngine.ResourceManagement.ResourceProviders.Simulation
{
/// <summary>
/// Serialized data containing the asset bundle layout.
/// </summary>
[Serializable]
public class VirtualAssetBundleRuntimeData
{
[FormerlySerializedAs("m_simulatedAssetBundles")]
[SerializeField]
List<VirtualAssetBundle> m_SimulatedAssetBundles = new List<VirtualAssetBundle>();
[FormerlySerializedAs("m_remoteLoadSpeed")]
[SerializeField]
long m_RemoteLoadSpeed = 1234567890123456; //once we expose this to the user a good default value would be 1024 * 100;
[FormerlySerializedAs("m_localLoadSpeed")]
[SerializeField]
long m_LocalLoadSpeed = 1234567890123456; //once we expose this to the user a good default value would be 1024 * 1024 * 10;
/// <summary>
/// The list of asset bundles to simulate.
/// </summary>
public List<VirtualAssetBundle> AssetBundles
{
get { return m_SimulatedAssetBundles; }
}
/// <summary>
/// Bandwidth value (in bytes per second) to simulate loading from a remote location.
/// </summary>
public long RemoteLoadSpeed
{
get { return m_RemoteLoadSpeed; }
}
/// <summary>
/// Bandwidth value (in bytes per second) to simulate loading from a local location.
/// </summary>
public long LocalLoadSpeed
{
get { return m_LocalLoadSpeed; }
}
/// <summary>
/// Construct a new VirtualAssetBundleRuntimeData object.
/// </summary>
public VirtualAssetBundleRuntimeData()
{
}
/// <summary>
/// Construct a new VirtualAssetBundleRuntimeData object.
/// </summary>
/// <param name="localSpeed">Bandwidth value (in bytes per second) to simulate loading from a local location.</param>
/// <param name="remoteSpeed">Bandwidth value (in bytes per second) to simulate loading from a remote location.</param>
public VirtualAssetBundleRuntimeData(long localSpeed, long remoteSpeed)
{
m_LocalLoadSpeed = localSpeed;
m_RemoteLoadSpeed = remoteSpeed;
}
}
}
#endif

View file

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

View file

@ -0,0 +1,88 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.ResourceProviders.Simulation
{
/// <summary>
/// Custom version of AssetBundleRequestOptions used to compute needed download size while bypassing the cache. In the future a virtual cache may be implemented.
/// </summary>
[Serializable]
public class VirtualAssetBundleRequestOptions : AssetBundleRequestOptions
{
/// <inheritdoc/>
public override long ComputeSize(IResourceLocation location, ResourceManager resourceManager)
{
var id = resourceManager == null ? location.InternalId : resourceManager.TransformInternalId(location);
if (!ResourceManagerConfig.IsPathRemote(id))
return 0;
return BundleSize;
}
}
/// <summary>
/// Provides assets from virtual asset bundles. Actual loads are done through the AssetDatabase API.
/// </summary>
[DisplayName("Assets from Virtual Bundles")]
public class VirtualBundledAssetProvider : ResourceProviderBase
{
/// <summary>
/// Default copnstructor.
/// </summary>
public VirtualBundledAssetProvider()
{
m_ProviderId = typeof(BundledAssetProvider).FullName;
}
class InternalOp
{
VBAsyncOperation<object> m_RequestOperation;
ProvideHandle m_PI;
public void Start(ProvideHandle provideHandle, VirtualAssetBundle bundle)
{
m_PI = provideHandle;
provideHandle.SetWaitForCompletionCallback(WaitForCompletionHandler);
m_RequestOperation = bundle.LoadAssetAsync(m_PI, m_PI.Location);
m_RequestOperation.Completed += RequestOperation_Completed;
}
private bool WaitForCompletionHandler()
{
return m_RequestOperation.WaitForCompletion();
}
private void RequestOperation_Completed(VBAsyncOperation<object> obj)
{
bool success = (obj.Result != null && m_PI.Type.IsAssignableFrom(obj.Result.GetType())) && obj.OperationException == null;
m_PI.Complete(obj.Result, success, obj.OperationException);
}
public float GetPercentComplete()
{
return m_RequestOperation != null ? m_RequestOperation.PercentComplete : 0.0f;
}
}
public override void Provide(ProvideHandle provideHandle)
{
List<object> deps = new List<object>(); // TODO: garbage. need to pass actual count and reuse the list
provideHandle.GetDependencies(deps);
VirtualAssetBundle bundle = deps[0] as VirtualAssetBundle;
if (bundle == null)
{
provideHandle.Complete<object>(null, false, new Exception($"Unable to load asset of type {provideHandle.Type} from location {provideHandle.Location}."));
}
else
{
new InternalOp().Start(provideHandle, bundle);
}
}
}
}
#endif

View file

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

View file

@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.ResourceProviders
{
/// <summary>
/// Provides raw text from a local or remote URL.
/// </summary>
[DisplayName("Text Data Provider")]
public class TextDataProvider : ResourceProviderBase
{
/// <summary>
/// Controls whether errors are logged - this is disabled when trying to load from the local cache since failures are expected
/// </summary>
public bool IgnoreFailures { get; set; }
internal class InternalOp
{
TextDataProvider m_Provider;
UnityWebRequestAsyncOperation m_RequestOperation;
WebRequestQueueOperation m_RequestQueueOperation;
ProvideHandle m_PI;
bool m_IgnoreFailures;
private bool m_Complete = false;
private int m_Timeout = 0;
private float GetPercentComplete()
{
return m_RequestOperation != null ? m_RequestOperation.progress : 0.0f;
}
public void Start(ProvideHandle provideHandle, TextDataProvider rawProvider)
{
m_PI = provideHandle;
m_PI.SetWaitForCompletionCallback(WaitForCompletionHandler);
provideHandle.SetProgressCallback(GetPercentComplete);
m_Provider = rawProvider;
// override input options with options from Location if included
if (m_PI.Location.Data is ProviderLoadRequestOptions providerData)
{
m_IgnoreFailures = providerData.IgnoreFailures;
m_Timeout = providerData.WebRequestTimeout;
}
else
{
m_IgnoreFailures = rawProvider.IgnoreFailures;
m_Timeout = 0;
}
var path = m_PI.ResourceManager.TransformInternalId(m_PI.Location);
if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
{
SendWebRequest(path);
}
else if (File.Exists(path))
{
#if NET_4_6
if (path.Length >= 260)
path = @"\\?\" + path;
#endif
var text = File.ReadAllText(path);
object result = ConvertText(text);
m_PI.Complete(result, result != null, result == null ? new Exception($"Unable to load asset of type {m_PI.Type} from location {m_PI.Location}.") : null);
m_Complete = true;
}
else
{
Exception exception = null;
//Don't log errors when loading from the persistentDataPath since these files are expected to not exist until created
if (m_IgnoreFailures)
{
m_PI.Complete<object>(null, true, exception);
m_Complete = true;
}
else
{
exception = new Exception(string.Format("Invalid path in " + nameof(TextDataProvider) + " : '{0}'.", path));
m_PI.Complete<object>(null, false, exception);
m_Complete = true;
}
}
}
bool WaitForCompletionHandler()
{
if (m_Complete)
return true;
if (m_RequestOperation != null)
{
if (m_RequestOperation.isDone && !m_Complete)
RequestOperation_completed(m_RequestOperation);
else if (!m_RequestOperation.isDone)
return false;
}
return m_Complete;
}
private void RequestOperation_completed(AsyncOperation op)
{
if (m_Complete)
return;
var webOp = op as UnityWebRequestAsyncOperation;
string textResult = null;
Exception exception = null;
if (webOp != null)
{
var webReq = webOp.webRequest;
if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult))
textResult = webReq.downloadHandler.text;
else
exception = new RemoteProviderException($"{nameof(TextDataProvider)} : unable to load from url : {webReq.url}", m_PI.Location, uwrResult);
webReq.Dispose();
}
else
{
exception = new RemoteProviderException(nameof(TextDataProvider) + " unable to load from unknown url", m_PI.Location);
}
CompleteOperation(textResult, exception);
}
protected void CompleteOperation(string text, Exception exception)
{
object result = null;
if (!string.IsNullOrEmpty(text))
result = ConvertText(text);
m_PI.Complete(result, result != null || m_IgnoreFailures, exception);
m_Complete = true;
}
private object ConvertText(string text)
{
try
{
return m_Provider.Convert(m_PI.Type, text);
}
catch (Exception e)
{
if (!m_IgnoreFailures)
Debug.LogException(e);
return null;
}
}
protected virtual void SendWebRequest(string path)
{
path = path.Replace('\\', '/');
UnityWebRequest request = new UnityWebRequest(path, UnityWebRequest.kHttpVerbGET, new DownloadHandlerBuffer(), null);
if (m_Timeout > 0)
request.timeout = m_Timeout;
m_PI.ResourceManager.WebRequestOverride?.Invoke(request);
m_RequestQueueOperation = WebRequestQueue.QueueRequest(request);
if (m_RequestQueueOperation.IsDone)
{
m_RequestOperation = m_RequestQueueOperation.Result;
if (m_RequestOperation.isDone)
RequestOperation_completed(m_RequestOperation);
else
m_RequestOperation.completed += RequestOperation_completed;
}
else
{
m_RequestQueueOperation.OnComplete += asyncOperation =>
{
m_RequestOperation = asyncOperation;
m_RequestOperation.completed += RequestOperation_completed;
};
}
}
}
/// <summary>
/// Method to convert the text into the object type requested. Usually the text contains a JSON formatted serialized object.
/// </summary>
/// <param name="type">The object type the text is converted to.</param>
/// <param name="text">The text to be converted.</param>
/// <returns>The converted object.</returns>
public virtual object Convert(Type type, string text)
{
return text;
}
/// <summary>
/// Provides raw text data from the location.
/// </summary>
/// <param name="provideHandle">The data needed by the provider to perform the load.</param>
public override void Provide(ProvideHandle provideHandle)
{
new InternalOp().Start(provideHandle, this);
}
}
}

View file

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

View file

@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement
{
/// <summary>
/// Represents a web request stored in the <seealso cref="WebRequestQueue"/>.
/// </summary>
public class WebRequestQueueOperation
{
private bool m_Completed = false;
/// <summary>
/// Stores the async operation object returned from sending the web request.
/// </summary>
public UnityWebRequestAsyncOperation Result;
/// <summary>
/// Event that is invoked when the async operation is complete.
/// </summary>
public Action<UnityWebRequestAsyncOperation> OnComplete;
/// <summary>
/// Indicates that the async operation is complete.
/// </summary>
public bool IsDone
{
get { return m_Completed || Result != null; }
}
internal UnityWebRequest m_WebRequest;
/// <summary>
/// The web request.
/// </summary>
public UnityWebRequest WebRequest
{
get { return m_WebRequest; }
internal set { m_WebRequest = value; }
}
/// <summary>
/// Initializes and returns an instance of WebRequestQueueOperation.
/// </summary>
/// <param name="request">The web request associated with this object.</param>
public WebRequestQueueOperation(UnityWebRequest request)
{
m_WebRequest = request;
}
internal void Complete(UnityWebRequestAsyncOperation asyncOp)
{
m_Completed = true;
Result = asyncOp;
OnComplete?.Invoke(Result);
}
}
/// <summary>
/// Represents a queue of web requests. Completed requests are removed from the queue.
/// </summary>
public static class WebRequestQueue
{
internal static int s_MaxRequest = 3;
internal static Queue<WebRequestQueueOperation> s_QueuedOperations = new Queue<WebRequestQueueOperation>();
internal static List<UnityWebRequestAsyncOperation> s_ActiveRequests = new List<UnityWebRequestAsyncOperation>();
/// <summary>
/// Sets the max number of web requests running at the same time.
/// </summary>
/// <param name="maxRequests">The max number of web requests.</param>
public static void SetMaxConcurrentRequests(int maxRequests)
{
if (maxRequests < 1)
throw new ArgumentException("MaxRequests must be 1 or greater.", "maxRequests");
s_MaxRequest = maxRequests;
}
/// <summary>
/// Adds a web request to the queue.
/// </summary>
/// <param name="request">The web request.</param>
/// <returns>Returns an object representing the web request.</returns>
public static WebRequestQueueOperation QueueRequest(UnityWebRequest request)
{
WebRequestQueueOperation queueOperation = new WebRequestQueueOperation(request);
if (s_ActiveRequests.Count < s_MaxRequest)
BeginWebRequest(queueOperation);
else
s_QueuedOperations.Enqueue(queueOperation);
return queueOperation;
}
/// <summary>
/// Synchronously waits for a particular web request to be completed.
/// </summary>
/// <param name="request">The web request.</param>
/// <param name="millisecondsTimeout">The amount of time in milliseconds spent waiting per iteration before checking if the request is complete.</param>
public static void WaitForRequestToBeActive(WebRequestQueueOperation request, int millisecondsTimeout)
{
var completedRequests = new List<UnityWebRequestAsyncOperation>();
while (s_QueuedOperations.Contains(request))
{
completedRequests.Clear();
foreach (UnityWebRequestAsyncOperation webRequestAsyncOp in s_ActiveRequests)
{
if (UnityWebRequestUtilities.IsAssetBundleDownloaded(webRequestAsyncOp))
completedRequests.Add(webRequestAsyncOp);
}
foreach (UnityWebRequestAsyncOperation webRequestAsyncOp in completedRequests)
{
bool requestIsActive = s_QueuedOperations.Peek() == request;
webRequestAsyncOp.completed -= OnWebAsyncOpComplete;
OnWebAsyncOpComplete(webRequestAsyncOp);
if (requestIsActive)
return;
}
System.Threading.Thread.Sleep(millisecondsTimeout);
}
}
private static void OnWebAsyncOpComplete(AsyncOperation operation)
{
s_ActiveRequests.Remove(operation as UnityWebRequestAsyncOperation);
if (s_QueuedOperations.Count > 0)
{
var nextQueuedOperation = s_QueuedOperations.Dequeue();
BeginWebRequest(nextQueuedOperation);
}
}
static void BeginWebRequest(WebRequestQueueOperation queueOperation)
{
var request = queueOperation.m_WebRequest;
UnityWebRequestAsyncOperation webRequestAsyncOp = null;
try
{
webRequestAsyncOp = request.SendWebRequest();
if (webRequestAsyncOp != null)
{
s_ActiveRequests.Add(webRequestAsyncOp);
if (webRequestAsyncOp.isDone)
OnWebAsyncOpComplete(webRequestAsyncOp);
else
webRequestAsyncOp.completed += OnWebAsyncOpComplete;
}
else
{
OnWebAsyncOpComplete(null);
}
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
queueOperation.Complete(webRequestAsyncOp);
}
}
}

View file

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

View file

@ -0,0 +1,32 @@
{
"name": "Unity.ResourceManager",
"rootNamespace": "",
"references": [
"GUID:b46779583a009f04ba9f5f31d0e7e6ac"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "Unity",
"expression": "2021.2.0a19",
"define": "ENABLE_ASYNC_ASSETBUNDLE_UWR"
},
{
"name": "com.unity.services.ccd.management",
"expression": "[2.0.0,3.0.0)",
"define": "ENABLE_CCD"
},
{
"name": "com.unity.profiling.core",
"expression": "[1.0.2,3.0.0)",
"define": "ENABLE_ADDRESSABLE_PROFILER"
}
],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 84651a3751eca9349aac36a66bba901b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

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

View file

@ -0,0 +1,855 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace UnityEngine.ResourceManagement.Util
{
internal class BinaryStorageBuffer
{
class BuiltinTypesSerializer :
ISerializationAdapter<int>,
ISerializationAdapter<bool>,
ISerializationAdapter<long>,
ISerializationAdapter<string>,
ISerializationAdapter<Hash128>
{
public IEnumerable<ISerializationAdapter> Dependencies => null;
struct ObjectToStringRemap
{
public uint stringId;
public char separator;
}
public object Deserialize(Reader reader, Type t, uint offset)
{
if (offset == uint.MaxValue)
return null;
if (t == typeof(int)) return reader.ReadValue<int>(offset);
if (t == typeof(bool)) return reader.ReadValue<bool>(offset);
if (t == typeof(long)) return reader.ReadValue<long>(offset);
if (t == typeof(Hash128)) return reader.ReadValue<Hash128>(offset);
if (t == typeof(string))
{
var remap = reader.ReadValue<ObjectToStringRemap>(offset);
return reader.ReadString(remap.stringId, remap.separator, false);
}
return null;
}
char FindBestSeparator(string str, params char[] seps)
{
int bestCount = 0;
char bestSep = (char)0;
foreach (var s in seps)
{
var sepCount = str.Count(c => c == s);
if (sepCount > bestCount)
{
bestCount = sepCount;
bestSep = s;
}
}
var parts = str.Split(bestSep);
int validParts = 0;
foreach (var p in parts)
if (p.Length > 8)
validParts++;
if (validParts < 2)
bestSep = (char)0;
return bestSep;
}
public uint Serialize(Writer writer, object val)
{
if (val == null)
return uint.MaxValue;
var t = val.GetType();
if (t == typeof(int)) return writer.Write((int)val);
if (t == typeof(bool)) return writer.Write((bool)val);
if (t == typeof(long)) return writer.Write((long)val);
if (t == typeof(Hash128)) return writer.Write((Hash128)val);
if (t == typeof(string))
{
var str = val as string;
var bestSep = FindBestSeparator(str, '/', '.', '-', '_', '\\', ',');
return writer.Write(new ObjectToStringRemap { stringId = writer.WriteString((string)val), separator = bestSep });
}
return uint.MaxValue;
}
}
class TypeSerializer :
ISerializationAdapter<Type>
{
struct Data
{
public uint assemblyId;
public uint classId;
}
public IEnumerable<BinaryStorageBuffer.ISerializationAdapter> Dependencies => null;
public object Deserialize(Reader reader, Type type, uint offset)
{
try
{
var d = reader.ReadValue<Data>(offset);
var assemblyName = reader.ReadString(d.assemblyId, '.');
var className = reader.ReadString(d.classId, '.');
var assembly = Assembly.Load(assemblyName);
return assembly == null ? null : assembly.GetType(className);
}
catch (Exception e)
{
Debug.LogException(e);
return null;
}
}
public uint Serialize(Writer writer, object val)
{
if (val == null)
return uint.MaxValue;
var t = val as Type;
return writer.Write(new Data
{
assemblyId = writer.WriteString(t.Assembly.FullName, '.'),
classId = writer.WriteString(t.FullName, '.')
});
}
}
private unsafe static void ComputeHash(void* pData, ulong size, Hash128* hash)
{
if (pData == null || size == 0)
{
*hash = default;
return;
}
HashUnsafeUtilities.ComputeHash128(pData, size, hash);
}
static void AddSerializationAdapter(Dictionary<Type, ISerializationAdapter> serializationAdapters, ISerializationAdapter adapter, bool forceOverride = false)
{
bool added = false;
foreach (var i in adapter.GetType().GetInterfaces())
{
if (i.IsGenericType && typeof(ISerializationAdapter).IsAssignableFrom(i))
{
var aType = i.GenericTypeArguments[0];
if (serializationAdapters.ContainsKey(aType))
{
if (forceOverride)
{
var prevAdapter = serializationAdapters[aType];
serializationAdapters.Remove(aType);
// serializationAdapters.Add(aType, adapter);
serializationAdapters[aType] = adapter;
added = true;
Debug.Log($"Replacing adapter for type {aType}: {prevAdapter} -> {adapter}");
}
else
{
Debug.Log($"Failed to register adapter for type {aType}: {adapter}, {serializationAdapters[aType]} is already registered.");
}
}
else
{
serializationAdapters[aType] = adapter;
added = true;
}
}
}
if (added)
{
var deps = adapter.Dependencies;
if (deps != null)
foreach (var d in deps)
AddSerializationAdapter(serializationAdapters, d);
}
}
static bool GetSerializationAdapter(Dictionary<Type, ISerializationAdapter> serializationAdapters, Type t, out ISerializationAdapter adapter)
{
if (!serializationAdapters.TryGetValue(t, out adapter))
{
foreach (var k in serializationAdapters)
if (k.Key.IsAssignableFrom(t))
return (adapter = k.Value) != null;
Debug.LogError($"Unable to find serialization adapter for type {t}.");
}
return adapter != null;
}
const uint kUnicodeStringFlag = 0x80000000;
const uint kDynamicStringFlag = 0x40000000;
const uint kClearFlagsMask = 0x3fffffff;
struct DynamicString
{
public uint stringId;
public uint nextId;
}
struct ObjectTypeData
{
public uint typeId;
public uint objectId;
}
public interface ISerializationAdapter
{
IEnumerable<ISerializationAdapter> Dependencies { get; }
uint Serialize(Writer writer, object val);
object Deserialize(Reader reader, Type t, uint offset);
}
public interface ISerializationAdapter<T> : ISerializationAdapter
{
}
public unsafe class Reader
{
byte[] m_Buffer;
Dictionary<Type, ISerializationAdapter> m_Adapters;
LRUCache<uint, object> m_Cache;
StringBuilder stringBuilder;
private void Init(byte[] data, int maxCachedObjects, params ISerializationAdapter[] adapters)
{
m_Buffer = data;
stringBuilder = new StringBuilder(1024);
m_Cache = new LRUCache<uint, object>(maxCachedObjects);
m_Adapters = new Dictionary<Type, ISerializationAdapter>();
foreach (var a in adapters)
BinaryStorageBuffer.AddSerializationAdapter(m_Adapters, a);
BinaryStorageBuffer.AddSerializationAdapter(m_Adapters, new TypeSerializer());
BinaryStorageBuffer.AddSerializationAdapter(m_Adapters, new BuiltinTypesSerializer());
}
public void AddSerializationAdapter(ISerializationAdapter a)
{
BinaryStorageBuffer.AddSerializationAdapter(m_Adapters, a);
}
public Reader(byte[] data, int maxCachedObjects = 1024, params ISerializationAdapter[] adapters)
{
Init(data, maxCachedObjects, adapters);
}
public Reader(Stream inputStream, uint bufferSize, int maxCachedObjects, params ISerializationAdapter[] adapters)
{
var data = new byte[bufferSize == 0 ? inputStream.Length : bufferSize];
inputStream.Read(data, 0, data.Length);
Init(data, maxCachedObjects, adapters);
}
bool TryGetCachedValue<T>(uint offset, out T val)
{
if(m_Cache.TryGet(offset, out var obj))
{
val = (T)obj;
return true;
}
val = default;
return false;
}
public object[] ReadObjectArray(uint id, bool cacheValues = true)
{
if (id == uint.MaxValue)
return null;
var ids = ReadValueArray<uint>(id, cacheValues);
var objs = new object[ids.Length];
for (int i = 0; i < ids.Length; i++)
objs[i] = ReadObject(ids[i], cacheValues);
return objs;
}
public object[] ReadObjectArray(Type t, uint id, bool cacheValues = true)
{
if (id == uint.MaxValue)
return null;
var ids = ReadValueArray<uint>(id, cacheValues);
var objs = new object[ids.Length];
for (int i = 0; i < ids.Length; i++)
objs[i] = ReadObject(t, ids[i], cacheValues);
return objs;
}
public T[] ReadObjectArray<T>(uint id, bool cacheValues = true)
{
if (id == uint.MaxValue)
return null;
var ids = ReadValueArray<uint>(id, cacheValues);
var objs = new T[ids.Length];
for (int i = 0; i < ids.Length; i++)
objs[i] = ReadObject<T>(ids[i], cacheValues);
return objs;
}
public object ReadObject(uint id, bool cacheValue = true)
{
if (id == uint.MaxValue)
return null;
var td = ReadValue<ObjectTypeData>(id);
var type = ReadObject<Type>(td.typeId);
return ReadObject(type, td.objectId, cacheValue);
}
public T ReadObject<T>(uint offset, bool cacheValue = true) => (T)ReadObject(typeof(T), offset, cacheValue);
public object ReadObject(Type t, uint id, bool cacheValue = true)
{
if (id == uint.MaxValue)
return null;
if (TryGetCachedValue<object>(id, out var val))
return val;
if (!GetSerializationAdapter(m_Adapters, t, out var adapter))
return null;
object res = default;
try
{
res = adapter.Deserialize(this, t, id);
}
catch (Exception e)
{
Debug.LogException(e);
return null;
}
if (cacheValue && res != null)
m_Cache.TryAdd(id, res);
return res;
}
public T[] ReadValueArray<T>(uint id, bool cacheValue = true) where T : unmanaged
{
if (id == uint.MaxValue)
return null;
if (id - sizeof(uint) >= m_Buffer.Length)
throw new Exception($"Data offset {id} is out of bounds of buffer with length of {m_Buffer.Length}.");
fixed (byte* pData = &m_Buffer[id - sizeof(uint)])
{
if (TryGetCachedValue<T[]>(id, out var vals))
return vals;
uint size = 0;
UnsafeUtility.MemCpy(&size, pData, sizeof(uint));
if((id + size) > m_Buffer.Length)
throw new Exception($"Data size {size} is out of bounds of buffer with length of {m_Buffer.Length}.");
var elCount = size / sizeof(T);
var valsT = new T[elCount];
fixed (T* pVals = valsT)
UnsafeUtility.MemCpy(pVals, &pData[sizeof(uint)], size);
if(cacheValue)
m_Cache.TryAdd(id, valsT);
return valsT;
}
}
public T ReadValue<T>(uint id) where T : unmanaged
{
if (id == uint.MaxValue)
return default;
if (id >= m_Buffer.Length)
throw new Exception($"Data offset {id} is out of bounds of buffer with length of {m_Buffer.Length}.");
fixed (byte *pData = m_Buffer)
return *(T*)&pData[id];
}
public string ReadString(uint id, char sep = (char)0, bool cacheValue = true)
{
if (id == uint.MaxValue)
return null;
if (sep == (char)0)
return ReadAutoEncodedString(id, cacheValue);
return ReadDynamicString(id, sep, cacheValue);
}
string ReadStringInternal(uint offset, Encoding enc, bool cacheValue = true)
{
if (offset - sizeof(uint) >= m_Buffer.Length)
throw new Exception($"Data offset {offset} is out of bounds of buffer with length of {m_Buffer.Length}.");
if (TryGetCachedValue<string>(offset, out var val))
return val;
fixed (byte* pData = m_Buffer)
{
var strDataLength = *(uint*)&pData[offset - sizeof(uint)];
if (offset + strDataLength > m_Buffer.Length)
throw new Exception($"Data offset {offset}, len {strDataLength} is out of bounds of buffer with length of {m_Buffer.Length}.");
var valStr = enc.GetString(&pData[offset], (int)strDataLength);
if(cacheValue)
m_Cache.TryAdd(offset, valStr);
return valStr;
}
}
string ReadAutoEncodedString(uint id, bool cacheValue)
{
if ((id & kUnicodeStringFlag) == kUnicodeStringFlag)
return ReadStringInternal((uint)(id & kClearFlagsMask), Encoding.Unicode, cacheValue);
return ReadStringInternal(id, Encoding.ASCII, cacheValue);
}
string ReadDynamicString(uint id, char sep, bool cacheValue)
{
if ((id & kDynamicStringFlag) == kDynamicStringFlag)
{
if (!TryGetCachedValue<string>(id, out var str))
{
var ds = ReadValue<DynamicString>((uint)(id & kClearFlagsMask));
stringBuilder.Append(ReadAutoEncodedString(ds.stringId, cacheValue));
while (ds.nextId != uint.MaxValue)
{
ds = ReadValue<DynamicString>(ds.nextId);
stringBuilder.Append(sep);
stringBuilder.Append(ReadAutoEncodedString(ds.stringId, cacheValue));
}
str = stringBuilder.ToString();
stringBuilder.Clear();
if (cacheValue)
m_Cache.TryAdd(id, str);
}
return str;
}
else
{
return ReadAutoEncodedString(id, cacheValue);
}
}
}
public unsafe class Writer
{
class Chunk
{
public uint position;
public byte[] data;
}
uint totalBytes;
uint defaulChunkSize;
List<Chunk> chunks;
Dictionary<Hash128, uint> existingValues;
Dictionary<Type, ISerializationAdapter> serializationAdapters;
public uint Length => totalBytes;
public Writer(int chunkSize = 1024*1024, params ISerializationAdapter[] adapters)
{
defaulChunkSize = (uint)(chunkSize > 0 ? chunkSize : 1024 * 1024);
existingValues = new Dictionary<Hash128, uint>();
chunks = new List<Chunk>(10);
chunks.Add(new Chunk { position = 0 });
serializationAdapters = new Dictionary<Type, ISerializationAdapter>();
AddSerializationAdapter(serializationAdapters, new TypeSerializer());
AddSerializationAdapter(serializationAdapters, new BuiltinTypesSerializer());
foreach (var a in adapters)
AddSerializationAdapter(serializationAdapters, a, true);
}
Chunk FindChunkWithSpace(uint length)
{
var chunk = chunks[chunks.Count - 1];
if (chunk.data == null)
chunk.data = new byte[length > defaulChunkSize ? length : defaulChunkSize];
if (length > chunk.data.Length - chunk.position)
{
chunk = new Chunk { position = 0, data = new byte[length > defaulChunkSize ? length : defaulChunkSize] };
chunks.Add(chunk);
}
return chunk;
}
uint WriteInternal(void* pData, uint dataSize, bool prefixSize)
{
Hash128 hash;
ComputeHash(pData, (ulong)dataSize, &hash);
if (existingValues.TryGetValue(hash, out var existingOffset))
return existingOffset;
var addedBytes = prefixSize ? dataSize + sizeof(uint) : dataSize;
var chunk = FindChunkWithSpace(addedBytes);
fixed (byte* pChunkData = &chunk.data[chunk.position])
{
var id = totalBytes;
if (prefixSize)
{
UnsafeUtility.MemCpy(pChunkData, &dataSize, sizeof(uint));
if(dataSize > 0)
UnsafeUtility.MemCpy(&pChunkData[sizeof(uint)], pData, dataSize);
id += sizeof(uint);
}
else
{
if (dataSize == 0)
return uint.MaxValue;
UnsafeUtility.MemCpy(pChunkData, pData, dataSize);
}
totalBytes += addedBytes;
chunk.position += addedBytes;
existingValues[hash] = id;
return id;
}
}
uint ReserveInternal(uint dataSize, bool prefixSize)
{
//reserved data cannot reuse previously hashed values, but it can be reused for future writes
var addedBytes = prefixSize ? dataSize + sizeof(uint) : dataSize;
var chunk = FindChunkWithSpace(addedBytes);
totalBytes += addedBytes;
chunk.position += addedBytes;
return totalBytes - dataSize;
}
void WriteInternal(uint id, void* pData, uint dataSize, bool prefixSize)
{
//reserved data cannot reuse previously hashed values, but it can be reused for future writes
var addedBytes = prefixSize ? dataSize + sizeof(uint) : dataSize;
Hash128 hash;
ComputeHash(pData, (ulong)dataSize, &hash);
existingValues[hash] = id;
var chunkOffset = id;
foreach (var c in chunks)
{
if (chunkOffset < c.position)
{
fixed (byte* pChunkData = c.data)
{
if (prefixSize)
UnsafeUtility.MemCpy(&pChunkData[chunkOffset - sizeof(uint)], &dataSize, sizeof(uint));
UnsafeUtility.MemCpy(&pChunkData[chunkOffset], pData, dataSize);
return;
}
}
chunkOffset -= c.position;
}
}
public uint Reserve<T>() where T : unmanaged => ReserveInternal((uint)sizeof(T), false);
public uint Write<T>(in T val) where T : unmanaged
{
fixed (T* pData = &val)
return WriteInternal(pData, (uint)sizeof(T), false);
}
public uint Write<T>(T val) where T : unmanaged
{
return WriteInternal(&val, (uint)sizeof(T), false);
}
public uint Write<T>(uint offset, in T val) where T : unmanaged
{
fixed (T* pData = &val)
WriteInternal(offset, pData, (uint)sizeof(T), false);
return offset;
}
public uint Write<T>(uint offset, T val) where T : unmanaged
{
WriteInternal(offset, &val, (uint)sizeof(T), false);
return offset;
}
public uint Reserve<T>(uint count) where T : unmanaged => ReserveInternal((uint)sizeof(T) * count, true);
public uint Write<T>(T[] values, bool hashElements = true) where T : unmanaged
{
fixed (T* pData = values)
{
uint size = (uint)(values.Length * sizeof(T));
Hash128 hash;
ComputeHash(pData, (ulong)size, &hash);
if (existingValues.TryGetValue(hash, out var existingOffset))
return existingOffset;
var chunk = FindChunkWithSpace(size + sizeof(uint));
fixed (byte* pChunkData = &chunk.data[chunk.position])
{
var id = totalBytes + sizeof(uint);
UnsafeUtility.MemCpy(pChunkData, &size, sizeof(uint));
UnsafeUtility.MemCpy(&pChunkData[sizeof(uint)], pData, size);
var addedBytes = size + sizeof(uint);
totalBytes += addedBytes;
chunk.position += addedBytes;
existingValues[hash] = id;
if (hashElements && sizeof(T) > sizeof(uint))
{
for (int i = 0; i < values.Length; i++)
{
hash = default;
ComputeHash(&pData[i], (ulong)sizeof(T), &hash);
existingValues[hash] = id + (uint)(i * sizeof(T));
}
}
return id;
}
}
}
public uint Write<T>(uint offset, T[] values, bool hashElements = true) where T : unmanaged
{
var dataSize = (uint)(values.Length * sizeof(T));
var chunkOffset = offset;
fixed (T* pValues = values)
{
foreach (var c in chunks)
{
if (chunkOffset < c.position)
{
fixed (byte* pChunkData = c.data)
{
UnsafeUtility.MemCpy(&pChunkData[chunkOffset - sizeof(uint)], &dataSize, sizeof(uint));
UnsafeUtility.MemCpy(&pChunkData[chunkOffset], pValues, dataSize);
if (hashElements && sizeof(T) > sizeof(uint))
{
for (int i = 0; i < values.Length; i++)
{
Hash128 hash;
var v = values[i];
ComputeHash(&v, (ulong)sizeof(T), &hash);
existingValues[hash] = offset + (uint)i * (uint)sizeof(T);
}
}
return offset;
}
}
chunkOffset -= c.position;
}
}
return uint.MaxValue;
}
public uint WriteObjects<T>(IEnumerable<T> objs, bool serizalizeTypeData)
{
if (objs == null)
return uint.MaxValue;
var count = objs.Count();
var ids = new uint[count];
var index = 0;
foreach (var o in objs)
ids[index++] = WriteObject(o, serizalizeTypeData);
return Write(ids);
}
public uint WriteObject(object obj, bool serializeTypeData)
{
if (obj == null)
return uint.MaxValue;
var objType = obj.GetType();
if (!GetSerializationAdapter(serializationAdapters, objType, out var adapter))
return uint.MaxValue;
var id = adapter.Serialize(this, obj);
if (serializeTypeData)
id = Write(new ObjectTypeData { typeId = WriteObject(objType, false), objectId = id });
return id;
}
public uint WriteString(string str, char sep = (char)0)
{
if (str == null)
return uint.MaxValue;
return sep == (char)0 ? WriteAutoEncodedString(str) : WriteDynamicString(str, sep);
}
uint WriteStringInternal(string val, Encoding enc)
{
if (val == null)
return uint.MaxValue;
byte[] tmp = enc.GetBytes(val);
fixed (byte* pBytes = tmp)
return WriteInternal(pBytes, (uint)tmp.Length, true);
}
public byte[] SerializeToByteArray()
{
var data = new byte[totalBytes];
fixed (byte* pData = data)
{
uint offset = 0;
foreach (var c in chunks)
{
fixed (byte* pChunk = c.data)
UnsafeUtility.MemCpy(&pData[offset], pChunk, c.position);
offset += c.position;
}
}
return data;
}
public uint SerializeToStream(Stream str)
{
foreach (var c in chunks)
str.Write(c.data, 0, (int)c.position);
return totalBytes;
}
static bool IsUnicode(string str)
{
for (int i = 0; i < str.Length; i++)
if (str[i] > 255)
return true;
return false;
}
uint WriteAutoEncodedString(string str)
{
if (str == null)
return uint.MaxValue;
if (IsUnicode(str))
return WriteUnicodeString(str);
else
return WriteStringInternal(str, Encoding.ASCII);
}
uint WriteUnicodeString(string str)
{
var id = WriteStringInternal(str, Encoding.Unicode);
return (kUnicodeStringFlag | id);
}
static uint ComputeStringSize(string str, out bool isUnicode)
{
if (isUnicode = IsUnicode(str))
return (uint)Encoding.Unicode.GetByteCount(str);
return (uint)Encoding.ASCII.GetByteCount(str);
}
uint RecurseDynamicStringParts(StringParts[] parts, int index, char sep, uint minSize)
{
while (index < parts.Length - 1)
{
var currPartSize = parts[index].dataSize;
var nextPartSize = parts[index + 1].dataSize;
if (currPartSize < minSize || nextPartSize < minSize)
{
parts[index + 1].str = $"{parts[index].str}{sep}{parts[index + 1].str}";
index++;
}
else
{
break;
}
}
var strId = parts[index].isUnicode ? WriteUnicodeString(parts[index].str) : WriteStringInternal(parts[index].str, Encoding.ASCII);
var nxtId = (index < parts.Length - 1 ? RecurseDynamicStringParts(parts, index + 1, sep, minSize) : uint.MaxValue);
var id = Write(new DynamicString { stringId = strId, nextId = nxtId });
return id;
}
struct StringParts
{
public string str;
public uint dataSize;
public bool isUnicode;
}
unsafe uint WriteDynamicString(string str, char sep)
{
if (str == null)
return uint.MaxValue;
var minSize = (uint)sizeof(DynamicString);
var split = str.Split(sep);
var parts = new StringParts[split.Length];
for (int i = 0; i < parts.Length; i++)
{
var partSize = ComputeStringSize(split[i], out var isUnicode);
parts[i] = new StringParts { str = split[i], dataSize = partSize, isUnicode = isUnicode };
}
if (parts.Length < 2 || (parts.Length == 2 && (parts[0].dataSize + parts[1].dataSize) < minSize))
{
return WriteAutoEncodedString(str);
}
else
{
return (kDynamicStringFlag | RecurseDynamicStringParts(parts, 0, sep, minSize));
}
}
}
}
internal struct LRUCache<TKey, TValue> where TKey : IEquatable<TKey>
{
public struct Entry : IEquatable<Entry>
{
public LinkedListNode<TKey> lruNode;
public TValue Value;
public bool Equals(Entry other) => Value.Equals(other);
public override int GetHashCode() => Value.GetHashCode();
}
int entryLimit;
Dictionary<TKey, Entry> cache;
LinkedList<TKey> lru;
public LRUCache(int limit)
{
entryLimit = limit;
cache = new Dictionary<TKey, Entry>();
lru = new LinkedList<TKey>();
}
public bool TryAdd(TKey id, TValue obj)
{
if (obj == null || entryLimit <= 0)
return false;
cache.Add(id, new Entry { Value = obj, lruNode = lru.AddFirst(id) });
while (lru.Count > entryLimit)
{
cache.Remove(lru.Last.Value);
lru.RemoveLast();
}
return true;
}
public bool TryGet(TKey offset, out TValue val)
{
if (cache.TryGetValue(offset, out var entry))
{
val = entry.Value;
if (entry.lruNode.Previous != null)
{
lru.Remove(entry.lruNode);
lru.AddFirst(entry.lruNode);
}
return true;
}
val = default;
return false;
}
}
}

View file

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

View file

@ -0,0 +1,125 @@
using UnityEditor;
namespace UnityEngine.ResourceManagement.Util
{
/// <summary>
/// Creates a singleton.
/// </summary>
/// <typeparam name="T">The singleton type.</typeparam>
[ExecuteInEditMode]
public abstract class ComponentSingleton<T> : MonoBehaviour where T : ComponentSingleton<T>
{
static T s_Instance;
/// <summary>
/// Indicates whether or not there is an existing instance of the singleton.
/// </summary>
public static bool Exists => s_Instance != null;
/// <summary>
/// Stores the instance of the singleton.
/// </summary>
public static T Instance
{
get
{
if (s_Instance == null)
{
s_Instance = FindInstance() ?? CreateNewSingleton();
}
return s_Instance;
}
}
static T FindInstance()
{
#if UNITY_EDITOR
foreach (T cb in Resources.FindObjectsOfTypeAll(typeof(T)))
{
var go = cb.gameObject;
if (!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave))
return cb;
}
return null;
#else
return FindObjectOfType<T>();
#endif
}
/// <summary>
/// Retrieves the name of the object.
/// </summary>
/// <returns>Returns the name of the object.</returns>
protected virtual string GetGameObjectName() => typeof(T).Name;
static T CreateNewSingleton()
{
var go = new GameObject();
if (Application.isPlaying)
{
DontDestroyOnLoad(go);
go.hideFlags = HideFlags.DontSave;
}
else
{
go.hideFlags = HideFlags.HideAndDontSave;
}
var instance = go.AddComponent<T>();
go.name = instance.GetGameObjectName();
return instance;
}
private void Awake()
{
if (s_Instance != null && s_Instance != this)
{
DestroyImmediate(gameObject);
return;
}
s_Instance = this as T;
}
/// <summary>
/// Destroys the singleton.
/// </summary>
public static void DestroySingleton()
{
if (Exists)
{
DestroyImmediate(Instance.gameObject);
s_Instance = null;
}
}
#if UNITY_EDITOR
void OnEnable()
{
EditorApplication.playModeStateChanged += PlayModeChanged;
}
void OnDisable()
{
EditorApplication.playModeStateChanged -= PlayModeChanged;
}
void PlayModeChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.ExitingPlayMode)
{
if (Exists)
{
DestroyImmediate(Instance.gameObject);
s_Instance = null;
}
}
}
#endif
}
}

View file

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

View file

@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEditor;
namespace UnityEngine.ResourceManagement.Util
{
internal class DelayedActionManager : ComponentSingleton<DelayedActionManager>
{
struct DelegateInfo
{
static int s_Id;
int m_Id;
Delegate m_Delegate;
object[] m_Target;
public DelegateInfo(Delegate d, float invocationTime, params object[] p)
{
m_Delegate = d;
m_Id = s_Id++;
m_Target = p;
InvocationTime = invocationTime;
}
public float InvocationTime { get; private set; }
public override string ToString()
{
if (m_Delegate == null || m_Delegate.Method.DeclaringType == null)
return "Null m_delegate for " + m_Id;
var n = m_Id + " (target=" + m_Delegate.Target + ") " + m_Delegate.Method.DeclaringType.Name + "." + m_Delegate.Method.Name + "(";
var sep = "";
foreach (var o in m_Target)
{
n += sep + o;
sep = ", ";
}
return n + ") @" + InvocationTime;
}
public void Invoke()
{
try
{
m_Delegate.DynamicInvoke(m_Target);
}
catch (Exception e)
{
Debug.LogErrorFormat("Exception thrown in DynamicInvoke: {0} {1}", e, this);
}
}
}
List<DelegateInfo>[] m_Actions = {new List<DelegateInfo>(), new List<DelegateInfo>()};
LinkedList<DelegateInfo> m_DelayedActions = new LinkedList<DelegateInfo>();
Stack<LinkedListNode<DelegateInfo>> m_NodeCache = new Stack<LinkedListNode<DelegateInfo>>(10);
int m_CollectionIndex;
bool m_DestroyOnCompletion;
LinkedListNode<DelegateInfo> GetNode(ref DelegateInfo del)
{
if (m_NodeCache.Count > 0)
{
var node = m_NodeCache.Pop();
node.Value = del;
return node;
}
return new LinkedListNode<DelegateInfo>(del);
}
public static void Clear()
{
if (Exists)
Instance.DestroyWhenComplete();
}
void DestroyWhenComplete()
{
m_DestroyOnCompletion = true;
}
public static void AddAction(Delegate action, float delay = 0, params object[] parameters)
{
Instance.AddActionInternal(action, delay, parameters);
}
void AddActionInternal(Delegate action, float delay, params object[] parameters)
{
var del = new DelegateInfo(action, Time.unscaledTime + delay, parameters);
if (delay > 0)
{
if (m_DelayedActions.Count == 0)
{
m_DelayedActions.AddFirst(GetNode(ref del));
}
else
{
var n = m_DelayedActions.Last;
while (n != null && n.Value.InvocationTime > del.InvocationTime)
n = n.Previous;
if (n == null)
m_DelayedActions.AddFirst(GetNode(ref del));
else
m_DelayedActions.AddBefore(n, GetNode(ref del));
}
}
else
m_Actions[m_CollectionIndex].Add(del);
}
#if UNITY_EDITOR
void Awake()
{
if (!Application.isPlaying)
{
// Debug.Log("DelayedActionManager called outside of play mode, registering with EditorApplication.update.");
EditorApplication.update += LateUpdate;
}
}
#endif
public static bool IsActive
{
get
{
if (!Exists)
return false;
if (Instance.m_DelayedActions.Count > 0)
return true;
for (int i = 0; i < Instance.m_Actions.Length; i++)
if (Instance.m_Actions[i].Count > 0)
return true;
return false;
}
}
public static bool Wait(float timeout = 0, float timeAdvanceAmount = 0)
{
if (!IsActive)
return true;
var timer = new Stopwatch();
timer.Start();
var t = Time.unscaledTime;
do
{
Instance.InternalLateUpdate(t);
if (timeAdvanceAmount >= 0)
t += timeAdvanceAmount;
else
t = Time.unscaledTime;
} while (IsActive && (timeout <= 0 || timer.Elapsed.TotalSeconds < timeout));
return !IsActive;
}
void LateUpdate()
{
InternalLateUpdate(Time.unscaledTime);
}
void InternalLateUpdate(float t)
{
int iterationCount = 0;
while (m_DelayedActions.Count > 0 && m_DelayedActions.First.Value.InvocationTime <= t)
{
m_Actions[m_CollectionIndex].Add(m_DelayedActions.First.Value);
m_NodeCache.Push(m_DelayedActions.First);
m_DelayedActions.RemoveFirst();
}
do
{
int invokeIndex = m_CollectionIndex;
m_CollectionIndex = (m_CollectionIndex + 1) % 2;
var list = m_Actions[invokeIndex];
if (list.Count > 0)
{
for (int i = 0; i < list.Count; i++)
list[i].Invoke();
list.Clear();
}
iterationCount++;
Debug.Assert(iterationCount < 100);
} while (m_Actions[m_CollectionIndex].Count > 0);
if (m_DestroyOnCompletion && !IsActive)
Destroy(gameObject);
}
private void OnApplicationQuit()
{
if (Exists)
Destroy(Instance.gameObject);
}
}
}

View file

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

View file

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using UnityEngine.ResourceManagement.Util;
internal class DelegateList<T>
{
Func<Action<T>, LinkedListNode<Action<T>>> m_acquireFunc;
Action<LinkedListNode<Action<T>>> m_releaseFunc;
LinkedList<Action<T>> m_callbacks;
bool m_invoking = false;
public DelegateList(Func<Action<T>, LinkedListNode<Action<T>>> acquireFunc, Action<LinkedListNode<Action<T>>> releaseFunc)
{
if (acquireFunc == null)
throw new ArgumentNullException("acquireFunc");
if (releaseFunc == null)
throw new ArgumentNullException("releaseFunc");
m_acquireFunc = acquireFunc;
m_releaseFunc = releaseFunc;
}
public int Count
{
get { return m_callbacks == null ? 0 : m_callbacks.Count; }
}
public void Add(Action<T> action)
{
var node = m_acquireFunc(action);
if (m_callbacks == null)
m_callbacks = new LinkedList<Action<T>>();
m_callbacks.AddLast(node);
}
public void Remove(Action<T> action)
{
if (m_callbacks == null)
return;
var node = m_callbacks.First;
while (node != null)
{
if (node.Value == action)
{
if (m_invoking)
{
node.Value = null;
}
else
{
m_callbacks.Remove(node);
m_releaseFunc(node);
}
return;
}
node = node.Next;
}
}
public void Invoke(T res)
{
if (m_callbacks == null)
return;
m_invoking = true;
var node = m_callbacks.First;
while (node != null)
{
if (node.Value != null)
{
try
{
node.Value(res);
}
catch (Exception ex)
{
UnityEngine.Debug.LogException(ex);
}
}
node = node.Next;
}
m_invoking = false;
var r = m_callbacks.First;
while (r != null)
{
var next = r.Next;
if (r.Value == null)
{
m_callbacks.Remove(r);
m_releaseFunc(r);
}
r = next;
}
}
public void Clear()
{
if (m_callbacks == null)
return;
var node = m_callbacks.First;
while (node != null)
{
var next = node.Next;
m_callbacks.Remove(node);
m_releaseFunc(node);
node = next;
}
}
public static DelegateList<T> CreateWithGlobalCache()
{
if (!GlobalLinkedListNodeCache<Action<T>>.CacheExists)
GlobalLinkedListNodeCache<Action<T>>.SetCacheSize(32);
return new DelegateList<T>(GlobalLinkedListNodeCache<Action<T>>.Acquire, GlobalLinkedListNodeCache<Action<T>>.Release);
}
}

View file

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

View file

@ -0,0 +1,201 @@
using System;
using System.Runtime.Serialization;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.Exceptions
{
/// <summary>
/// Base class for all ResourceManager related exceptions.
/// </summary>
public class ResourceManagerException : Exception
{
/// <summary>
/// Construct a new ResourceManagerException.
/// </summary>
public ResourceManagerException()
{
}
/// <summary>
/// Construct a new ResourceManagerException.
/// </summary>
/// <param name="message">Message to describe the exception.</param>
public ResourceManagerException(string message) : base(message)
{
}
/// <summary>
/// Construct a new ResourceManagerException.
/// </summary>
/// <param name="message">Message to describe the exception.</param>
/// <param name="innerException">Inner exception that caused this exception.</param>
public ResourceManagerException(string message, Exception innerException) : base(message, innerException)
{
}
/// <summary>
/// Construct a new ResourceManagerException.
/// </summary>
/// <param name="message">Message to describe the exception.</param>
/// <param name="context">Context related to the exception.</param>
protected ResourceManagerException(SerializationInfo message, StreamingContext context) : base(message, context)
{
}
/// <summary>Provides a new string object describing the exception.</summary>
/// <returns>A newly allocated managed string.</returns>
public override string ToString() => $"{GetType().Name} : {base.Message}\n{InnerException}";
}
/// <summary>
/// Exception returned when the IResourceProvider is not found for a location.
/// </summary>
public class UnknownResourceProviderException : ResourceManagerException
{
/// <summary>
/// The location that contains the provider id that was not found.
/// </summary>
public IResourceLocation Location { get; private set; }
/// <summary>
/// Construct a new UnknownResourceProviderException
/// </summary>
/// <param name="location">The location that caused the exception to be created.</param>
public UnknownResourceProviderException(IResourceLocation location)
{
Location = location;
}
/// <summary>
/// Construct a new UnknownResourceProviderException
/// </summary>
public UnknownResourceProviderException()
{
}
/// <summary>
/// Construct a new UnknownResourceProviderException
/// </summary>
/// <param name="message">Message to describe the exception.</param>
public UnknownResourceProviderException(string message) : base(message)
{
}
/// <summary>
/// Construct a new UnknownResourceProviderException
/// </summary>
/// <param name="message">Message to describe the exception.</param>
/// <param name="innerException">Inner exception that caused this exception.</param>
public UnknownResourceProviderException(string message, Exception innerException) : base(message, innerException)
{
}
/// <summary>
/// Construct a new UnknownResourceProviderException
/// </summary>
/// <param name="message">Message to describe the exception.</param>
/// <param name="context">Context related to the exception.</param>
protected UnknownResourceProviderException(SerializationInfo message, StreamingContext context) : base(message, context)
{
}
/// <summary>
/// Returns a string describing this exception.
/// </summary>
public override string Message
{
get { return base.Message + ", ProviderId=" + Location.ProviderId + ", Location=" + Location; }
}
/// <summary>
/// Returns string representation of exception.
/// </summary>
/// <returns>String representation of exception.</returns>
public override string ToString()
{
return Message;
}
}
/// <summary>
/// Class that represent an error that occured during an AsyncOperation.
/// </summary>
public class OperationException : Exception
{
/// <summary>
/// Creates a new instance of <see cref="OperationException"/>.
/// </summary>
/// <param name="message">A message describing the error.</param>
/// <param name="innerException">The exception that caused the error, if any.</param>
public OperationException(string message, Exception innerException = null) : base(message, innerException)
{
}
/// <summary>Provides a new string object describing the exception.</summary>
/// <returns>A newly allocated managed string.</returns>
public override string ToString() => $"{GetType().Name} : {base.Message}\n{InnerException}";
}
/// <summary>
/// Class that represent an error that occured during a ProviderOperation.
/// </summary>
public class ProviderException : OperationException
{
/// <summary>
/// Creates a new instance of <see cref="ProviderException"/>.
/// </summary>
/// <param name="message">A message describing the error.</param>
/// <param name="location">The resource location that the operation was trying to provide.</param>
/// <param name="innerException">The exception that caused the error, if any.</param>
public ProviderException(string message, IResourceLocation location = null, Exception innerException = null)
: base(message, innerException)
{
Location = location;
}
/// <summary>
/// The resource location that the operation was trying to provide.
/// </summary>
public IResourceLocation Location { get; }
}
/// <summary>
/// Class representing an error occured during an operation that remotely fetch data.
/// </summary>
public class RemoteProviderException : ProviderException
{
/// <summary>
/// Creates a new instance of <see cref="ProviderException"/>.
/// </summary>
/// <param name="message">A message describing the error.</param>
/// <param name="location">The resource location that the operation was trying to provide.</param>
/// <param name="uwrResult">The result of the unity web request, if any.</param>
/// <param name="innerException">The exception that caused the error, if any.</param>
public RemoteProviderException(string message, IResourceLocation location = null, UnityWebRequestResult uwrResult = null, Exception innerException = null)
: base(message, location, innerException)
{
WebRequestResult = uwrResult;
}
/// <summary>
/// Returns a string describing this exception.
/// </summary>
public override string Message => this.ToString();
/// <summary>
/// The result of the unity web request, if any.
/// </summary>
public UnityWebRequestResult WebRequestResult { get; }
/// <summary>Provides a new string object describing the exception.</summary>
/// <returns>A newly allocated managed string.</returns>
public override string ToString()
{
if (WebRequestResult != null)
return $"{GetType().Name} : {base.Message}\nUnityWebRequest result : {WebRequestResult}\n{InnerException}";
else
return base.ToString();
}
}
}

View file

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

View file

@ -0,0 +1,106 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
internal class ListWithEvents<T> : IList<T>
{
private List<T> m_List = new List<T>();
public event Action<T> OnElementAdded;
public event Action<T> OnElementRemoved;
private void InvokeAdded(T element)
{
if (OnElementAdded != null)
OnElementAdded(element);
}
private void InvokeRemoved(T element)
{
if (OnElementRemoved != null)
OnElementRemoved(element);
}
public T this[int index]
{
get { return m_List[index]; }
set
{
T oldElement = m_List[index];
m_List[index] = value;
InvokeRemoved(oldElement);
InvokeAdded(value);
}
}
public int Count
{
get { return m_List.Count; }
}
public bool IsReadOnly
{
get { return ((IList<T>)m_List).IsReadOnly; }
}
public void Add(T item)
{
m_List.Add(item);
InvokeAdded(item);
}
public void Clear()
{
foreach (T obj in m_List)
InvokeRemoved(obj);
m_List.Clear();
}
public bool Contains(T item)
{
return m_List.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
m_List.CopyTo(array, arrayIndex);
}
public IEnumerator<T> GetEnumerator()
{
return m_List.GetEnumerator();
}
public int IndexOf(T item)
{
return m_List.IndexOf(item);
}
public void Insert(int index, T item)
{
m_List.Insert(index, item);
InvokeAdded(item);
}
public bool Remove(T item)
{
bool ret = m_List.Remove(item);
if (ret)
InvokeRemoved(item);
return ret;
}
public void RemoveAt(int index)
{
T item = m_List[index];
m_List.RemoveAt(index);
InvokeRemoved(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)m_List).GetEnumerator();
}
}

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