Firstborn/Library/PackageCache/com.unity.addressables@1.19.19/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs
Schaken-Mods 7502018d20 Adding Mod Support
There is an asset in the store I grabbed. the coding is WAY above my head, I got about half of it and integrated and adapted what I can to it. im going as far as I can with it and ill come back in a few month when I understand t better.
2023-05-13 22:01:48 -05:00

551 lines
20 KiB
C#

#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