using System;
using System.Collections.Generic;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.SceneManagement;
namespace UnityEngine.ResourceManagement
{
///
/// Entry point for ResourceManager API
///
public class ResourceManager : IDisposable
{
///
/// Options for event types that will be sent by the ResourceManager
///
public enum DiagnosticEventType
{
///
/// Use to indicate that an operation failed.
///
AsyncOperationFail,
///
/// Use to indicate that an operation was created.
///
AsyncOperationCreate,
///
/// Use to indicate the percentage of completion for an operation was updated.
///
AsyncOperationPercentComplete,
///
/// Use to indicate that an operation has completed.
///
AsyncOperationComplete,
///
/// Use to indicate that the reference count of an operation was modified.
///
AsyncOperationReferenceCount,
///
/// Use to indicate that an operation was destroyed.
///
AsyncOperationDestroy,
}
internal bool postProfilerEvents = false;
///
/// Container for information associated with a Diagnostics event.
///
public struct DiagnosticEventContext
{
///
/// Operation handle for the event.
///
public AsyncOperationHandle OperationHandle { get; }
///
/// The type of diagnostic event.
///
public DiagnosticEventType Type { get; }
///
/// The value for this event.
///
public int EventValue { get; }
///
/// The IResourceLocation being provided by the operation triggering this event.
/// This value is null if the event is not while providing a resource.
///
public IResourceLocation Location { get; }
///
/// Addition data included with this event.
///
public object Context { get; }
///
/// Any error that occured.
///
public string Error { get; }
///
/// Construct a new DiagnosticEventContext.
///
/// Operation handle for the event.
/// The type of diagnostic event.
/// The value for this event.
/// Any error that occured.
/// Additional context data.
public DiagnosticEventContext(AsyncOperationHandle op, DiagnosticEventType type, int eventValue = 0, string error = null, object context = null)
{
OperationHandle = op;
Type = type;
EventValue = eventValue;
Location = op.m_InternalOp != null && op.m_InternalOp is IGenericProviderOperation gen
? gen.Location
: null;
Error = error;
Context = context;
}
}
///
/// Global exception handler. This will be called whenever an IAsyncOperation.OperationException is set to a non-null value.
///
///
///
///
public static Action ExceptionHandler { get; set; }
///
/// Functor to transform internal ids before being used by the providers.
///
///
/// Used to assign a function to the [ResourceManager](xref:UnityEngine.ResourceManagement.ResourceManager) that replaces location identifiers used at runtime.
/// This is useful when you want to load assets from a different location than the one specified in the content catalog,
/// for example downloading a remote AssetBundle from a different URL.
///
/// Assigning this value through the object will set the value on the .
///
/// The example below instantiates a GameObject from a local AssetBundle. The location identifier of the bundle is replaced with a file URI, and so the bundle is loaded via UnityWebRequest.
/// ///
///
/// A function taking an [IResourceLocation](xref:UnityEngine.ResourceManagement.ResourceLocations.IResourceLocation) and returning a transformed string location.
/// Transforming resource URLs
public Func InternalIdTransformFunc { get; set; }
///
/// Checks for an internal id transform function and uses it to modify the internal id value.
///
/// The location to transform the internal id of.
/// If a transform func is set, use it to pull the local id; otherwise, the InternalId property of the location is used.
public string TransformInternalId(IResourceLocation location)
{
return InternalIdTransformFunc == null ? location.InternalId : InternalIdTransformFunc(location);
}
///
/// Delegate that can be used to override the web request options before being sent.
///
///
/// You can assign a function to the object's WebRequestOverride property to individually modify the which is used to download files.
///
/// This can be used to add custom request headers or query strings.
///
/// This affects all downloads through Addressables including catalog files and asset bundles.
///
/// Assigning this value through the object will set the value on the .
///
/// For example you could add an Authorization header to authenticate with Cloud Content Delivery's private buckets.
///
///
/// Transforming resource URLs
public Action WebRequestOverride { get; set; }
internal bool CallbackHooksEnabled = true; // tests might need to disable the callback hooks to manually pump updating
ListWithEvents m_ResourceProviders = new ListWithEvents();
IAllocationStrategy m_allocator;
// list of all the providers in s_ResourceProviders that implement IUpdateReceiver
internal ListWithEvents m_UpdateReceivers = new ListWithEvents();
List m_UpdateReceiversToRemove = null;
//this prevents removing receivers during iteration
bool m_UpdatingReceivers = false;
//this prevents re-entrance into the Update method, which can cause stack overflow and infinite loops
bool m_InsideUpdateMethod = false;
internal int OperationCacheCount
{
get { return m_AssetOperationCache.Count; }
}
internal int InstanceOperationCount
{
get { return m_TrackedInstanceOperations.Count; }
}
//cache of type + providerId to IResourceProviders for faster lookup
internal Dictionary m_providerMap = new Dictionary();
Dictionary m_AssetOperationCache = new Dictionary();
HashSet m_TrackedInstanceOperations = new HashSet();
internal DelegateList m_UpdateCallbacks = DelegateList.CreateWithGlobalCache();
List m_DeferredCompleteCallbacks = new List();
HashSet m_AsestBundleProviders = new HashSet();
bool m_InsideExecuteDeferredCallbacksMethod = false;
List m_DeferredCallbacksToRegister = null;
private struct DeferredCallbackRegisterRequest
{
internal IAsyncOperation operation;
internal bool incrementRefCount;
}
Action m_obsoleteDiagnosticsHandler; // For use in working with Obsolete RegisterDiagnosticCallback method.
Action m_diagnosticsHandler;
Action m_ReleaseOpNonCached;
Action m_ReleaseOpCached;
Action m_ReleaseInstanceOp;
static int s_GroupOperationTypeHash = typeof(GroupOperation).GetHashCode();
static int s_InstanceOperationTypeHash = typeof(InstanceOperation).GetHashCode();
///
/// Add an update reveiver.
///
/// The object to add. The Update method will be called until the object is removed.
public void AddUpdateReceiver(IUpdateReceiver receiver)
{
if (receiver == null)
return;
m_UpdateReceivers.Add(receiver);
}
///
/// Remove update receiver.
///
/// The object to remove.
public void RemoveUpdateReciever(IUpdateReceiver receiver)
{
if (receiver == null)
return;
if (m_UpdatingReceivers)
{
if (m_UpdateReceiversToRemove == null)
m_UpdateReceiversToRemove = new List();
m_UpdateReceiversToRemove.Add(receiver);
}
else
{
m_UpdateReceivers.Remove(receiver);
}
}
///
/// The allocation strategy object.
///
public IAllocationStrategy Allocator
{
get { return m_allocator; }
set { m_allocator = value; }
}
///
/// Gets the list of configured objects. Resource Providers handle load and release operations for objects.
///
/// The resource providers list.
public IList ResourceProviders
{
get { return m_ResourceProviders; }
}
///
/// The CertificateHandler instance object.
///
public CertificateHandler CertificateHandlerInstance { get; set; }
///
/// Constructor for the resource manager.
///
/// The allocation strategy to use.
public ResourceManager(IAllocationStrategy alloc = null)
{
m_ReleaseOpNonCached = OnOperationDestroyNonCached;
m_ReleaseOpCached = OnOperationDestroyCached;
m_ReleaseInstanceOp = OnInstanceOperationDestroy;
m_allocator = alloc == null ? new LRUCacheAllocationStrategy(1000, 1000, 100, 10) : alloc;
m_ResourceProviders.OnElementAdded += OnObjectAdded;
m_ResourceProviders.OnElementRemoved += OnObjectRemoved;
m_UpdateReceivers.OnElementAdded += x => RegisterForCallbacks();
#if ENABLE_ADDRESSABLE_PROFILER
Profiling.ProfilerRuntime.Initialise();
#endif
}
private void OnObjectAdded(object obj)
{
IUpdateReceiver updateReceiver = obj as IUpdateReceiver;
if (updateReceiver != null)
AddUpdateReceiver(updateReceiver);
}
private void OnObjectRemoved(object obj)
{
IUpdateReceiver updateReceiver = obj as IUpdateReceiver;
if (updateReceiver != null)
RemoveUpdateReciever(updateReceiver);
}
bool m_RegisteredForCallbacks = false;
internal void RegisterForCallbacks()
{
if (CallbackHooksEnabled && !m_RegisteredForCallbacks)
{
m_RegisteredForCallbacks = true;
MonoBehaviourCallbackHooks.Instance.OnUpdateDelegate += Update;
}
}
///
/// Clears out the diagnostics callback handler.
///
[Obsolete("ClearDiagnosticsCallback is Obsolete, use ClearDiagnosticCallbacks instead.")]
public void ClearDiagnosticsCallback()
{
m_diagnosticsHandler = null;
m_obsoleteDiagnosticsHandler = null;
}
///
/// Clears out the diagnostics callbacks handler.
///
public void ClearDiagnosticCallbacks()
{
m_diagnosticsHandler = null;
m_obsoleteDiagnosticsHandler = null;
}
///
/// Unregister a handler for diagnostic events.
///
/// The event handler function.
public void UnregisterDiagnosticCallback(Action func)
{
if (m_diagnosticsHandler != null)
m_diagnosticsHandler -= func;
else
Debug.LogError("No Diagnostic callbacks registered, cannot remove callback.");
}
///
/// Register a handler for diagnostic events.
///
/// The event handler function.
[Obsolete]
public void RegisterDiagnosticCallback(Action func)
{
m_obsoleteDiagnosticsHandler = func;
}
///
/// Register a handler for diagnostic events.
///
/// The event handler function.
public void RegisterDiagnosticCallback(Action func)
{
m_diagnosticsHandler += func;
}
internal void PostDiagnosticEvent(DiagnosticEventContext context)
{
m_diagnosticsHandler?.Invoke(context);
if (m_obsoleteDiagnosticsHandler == null)
return;
m_obsoleteDiagnosticsHandler(context.OperationHandle, context.Type, context.EventValue, string.IsNullOrEmpty(context.Error) ? context.Context : context.Error);
}
///
/// Gets the appropriate for the given and .
///
/// The resource provider. Or null if an appropriate provider cannot be found
/// The desired object type to be loaded from the provider.
/// The resource location.
public IResourceProvider GetResourceProvider(Type t, IResourceLocation location)
{
if (location != null)
{
IResourceProvider prov = null;
var hash = location.ProviderId.GetHashCode() * 31 + (t == null ? 0 : t.GetHashCode());
if (!m_providerMap.TryGetValue(hash, out prov))
{
for (int i = 0; i < ResourceProviders.Count; i++)
{
var p = ResourceProviders[i];
if (p.ProviderId.Equals(location.ProviderId, StringComparison.Ordinal) && (t == null || p.CanProvide(t, location)))
{
m_providerMap.Add(hash, prov = p);
break;
}
}
}
return prov;
}
return null;
}
Type GetDefaultTypeForLocation(IResourceLocation loc)
{
var provider = GetResourceProvider(null, loc);
if (provider == null)
return typeof(object);
Type t = provider.GetDefaultType(loc);
return t != null ? t : typeof(object);
}
private int CalculateLocationsHash(IList locations, Type t = null)
{
if (locations == null || locations.Count == 0)
return 0;
int hash = 17;
foreach (var loc in locations)
{
Type t2 = t != null ? t : GetDefaultTypeForLocation(loc);
hash = hash * 31 + loc.Hash(t2);
}
return hash;
}
///
/// Load the at the specified .
///
/// An async operation.
/// Location to load.
/// When true, if the operation fails, dependencies will be released.
/// Object type to load.
private AsyncOperationHandle ProvideResource(IResourceLocation location, Type desiredType = null, bool releaseDependenciesOnFailure = true)
{
if (location == null)
throw new ArgumentNullException("location");
IResourceProvider provider = null;
if (desiredType == null)
{
provider = GetResourceProvider(desiredType, location);
if (provider == null)
{
var ex = new UnknownResourceProviderException(location);
return CreateCompletedOperationInternal