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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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