ZoeAppClass/Library/PackageCache/com.unity.addressables@1.1.10/Editor/Settings/AddressableAssetEntry.cs

538 lines
21 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEditor.AddressableAssets.Build;
using UnityEngine;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.Serialization;
namespace UnityEditor.AddressableAssets.Settings
{
/// <summary>
/// Contains data for an addressable asset entry.
/// </summary>
[Serializable]
public class AddressableAssetEntry : ISerializationCallbackReceiver
{
internal const string EditorSceneListName = "EditorSceneList";
internal const string EditorSceneListPath = "Scenes In Build";
internal const string ResourcesName = "Resources";
internal const string ResourcesPath = "*/Resources/";
[FormerlySerializedAs("m_guid")]
[SerializeField]
string m_GUID;
[FormerlySerializedAs("m_address")]
[SerializeField]
string m_Address;
[FormerlySerializedAs("m_readOnly")]
[SerializeField]
bool m_ReadOnly;
[FormerlySerializedAs("m_serializedLabels")]
[SerializeField]
List<string> m_SerializedLabels;
HashSet<string> m_Labels = new HashSet<string>();
internal virtual bool HasSettings() { return false; }
[NonSerialized]
AddressableAssetGroup m_ParentGroup;
/// <summary>
/// The asset group that this entry belongs to. An entry can only belong to a single group at a time.
/// </summary>
public AddressableAssetGroup parentGroup
{
get { return m_ParentGroup; }
set { m_ParentGroup = value; }
}
/// <summary>
/// The asset guid.
/// </summary>
public string guid { get { return m_GUID; } }
/// <summary>
/// The address of the entry. This is treated as the primary key in the ResourceManager system.
/// </summary>
public string address
{
get
{
return m_Address;
}
set
{
SetAddress(value);
}
}
/// <summary>
/// Set the address of the entry.
/// </summary>
/// <param name="addr">The address.</param>
/// <param name="postEvent">Post modification event.</param>
public void SetAddress(string addr, bool postEvent = true)
{
if (m_Address != addr)
{
m_Address = addr;
if (string.IsNullOrEmpty(m_Address))
m_Address = AssetPath;
SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, this, postEvent);
}
}
/// <summary>
/// Read only state of the entry.
/// </summary>
public bool ReadOnly
{
get
{
return m_ReadOnly;
}
set
{
if (m_ReadOnly != value)
{
m_ReadOnly = value;
SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, this, true);
}
}
}
/// <summary>
/// Is the asset in a resource folder.
/// </summary>
public bool IsInResources { get; set; }
/// <summary>
/// Is scene in scene list.
/// </summary>
public bool IsInSceneList { get; set; }
/// <summary>
/// Is a sub asset. For example an asset in an addressable folder.
/// </summary>
public bool IsSubAsset { get; set; }
bool m_CheckedIsScene;
bool m_IsScene;
/// <summary>
/// Is this entry for a scene.
/// </summary>
public bool IsScene
{
get
{
if (!m_CheckedIsScene)
{
m_CheckedIsScene = true;
m_IsScene = AssetPath.EndsWith(".unity");
}
return m_IsScene;
}
}
/// <summary>
/// The set of labels for this entry. There is no inherent limit to the number of labels.
/// </summary>
public HashSet<string> labels { get { return m_Labels; } }
Type m_mainAssetType = null;
internal Type MainAssetType
{
get
{
if (m_mainAssetType == null)
{
m_mainAssetType = AssetDatabase.GetMainAssetTypeAtPath(AssetPath);
if (m_mainAssetType == null)
return typeof(object); // do not cache a bad type lookup.
}
return m_mainAssetType;
}
}
/// <summary>
/// Set or unset a label on this entry.
/// </summary>
/// <param name="label">The label name.</param>
/// <param name="enable">Setting to true will add the label, false will remove it.</param>
/// <param name="force">When enable is true, setting force to true will force the label to exist on the parent AddressableAssetSettings object if it does not already.</param>
/// <param name="postEvent">Post modification event.</param>
/// <returns></returns>
public bool SetLabel(string label, bool enable, bool force=false, bool postEvent = true)
{
if (enable)
{
if(force)
parentGroup.Settings.AddLabel(label, postEvent);
if (m_Labels.Add(label))
{
SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, this, postEvent);
return true;
}
}
else
{
if (m_Labels.Remove(label))
{
SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, this, postEvent);
return true;
}
}
return false;
}
/// <summary>
/// Creates a list of keys that can be used to load this entry.
/// </summary>
/// <returns>The list of keys. This will contain the address, the guid as a Hash128 if valid, all assigned labels, and the scene index if applicable.</returns>
public List<object> CreateKeyList()
{
var keys = new List<object>();
//the address must be the first key
keys.Add(address);
if (!string.IsNullOrEmpty(guid))
keys.Add(guid);
if (IsScene && IsInSceneList)
{
int index = BuiltinSceneCache.GetSceneIndex(new GUID(guid));
if (index != -1)
keys.Add(index);
}
if (labels != null)
{
foreach (var l in labels)
keys.Add(l);
}
return keys;
}
internal AddressableAssetEntry(string guid, string address, AddressableAssetGroup parent, bool readOnly)
{
m_GUID = guid;
m_Address = address;
m_ReadOnly = readOnly;
parentGroup = parent;
IsInResources = false;
IsInSceneList = false;
}
internal void SerializeForHash(BinaryFormatter formatter, Stream stream)
{
formatter.Serialize(stream, m_GUID);
formatter.Serialize(stream, m_Address);
formatter.Serialize(stream, m_ReadOnly);
formatter.Serialize(stream, m_Labels.Count);
foreach (var t in m_Labels)
formatter.Serialize(stream, t);
formatter.Serialize(stream, IsInResources);
formatter.Serialize(stream, IsInSceneList);
formatter.Serialize(stream, IsSubAsset);
}
internal void SetDirty(AddressableAssetSettings.ModificationEvent e, object o, bool postEvent)
{
if (parentGroup != null)
parentGroup.SetDirty(e, o, postEvent, true);
}
internal string m_cachedAssetPath = null;
/// <summary>
/// The path of the asset.
/// </summary>
public string AssetPath
{
get
{
if (string.IsNullOrEmpty(m_cachedAssetPath))
{
if (string.IsNullOrEmpty(guid))
m_cachedAssetPath = string.Empty;
else if (guid == EditorSceneListName)
m_cachedAssetPath = EditorSceneListPath;
else if (guid == ResourcesName)
m_cachedAssetPath = ResourcesPath;
else
m_cachedAssetPath = AssetDatabase.GUIDToAssetPath(guid);
}
return m_cachedAssetPath;
}
}
/// <summary>
/// The asset load path. This is used to determine the internal id of resource locations.
/// </summary>
/// <param name="isBundled">True if the asset will be contained in an asset bundle.</param>
/// <returns>Return the runtime path that should be used to load this entry.</returns>
public string GetAssetLoadPath(bool isBundled)
{
if (!IsScene)
{
if (IsInResources)
return GetResourcesPath(AssetPath);
else
return AssetPath;
}
else
{
if (isBundled)
return AssetPath;
var path = AssetPath;
int i = path.LastIndexOf(".unity");
if (i > 0)
path = path.Substring(0, i);
i = path.ToLower().IndexOf("assets/");
if (i == 0)
path = path.Substring("assets/".Length);
return path;
}
}
static string GetResourcesPath(string path)
{
path = path.Replace('\\', '/');
int ri = path.ToLower().LastIndexOf("/resources/");
if (ri >= 0)
path = path.Substring(ri + "/resources/".Length);
int i = path.LastIndexOf('.');
if (i > 0)
path = path.Substring(0, i);
return path;
}
public void GatherAllAssets(List<AddressableAssetEntry> assets, bool includeSelf, bool recurseAll, Func<AddressableAssetEntry, bool> entryFilter = null)
{
var settings = parentGroup.Settings;
if (guid == EditorSceneListName)
{
foreach (var s in BuiltinSceneCache.scenes)
{
if (s.enabled)
{
var entry = settings.CreateSubEntryIfUnique(s.guid.ToString(), Path.GetFileNameWithoutExtension(s.path), this);
if (entry != null) //TODO - it's probably really bad if this is ever null. need some error detection
{
entry.IsInSceneList = true;
entry.m_Labels = m_Labels;
if (entryFilter == null || entryFilter(entry))
assets.Add(entry);
}
}
}
}
else if (guid == ResourcesName)
{
foreach (var resourcesDir in Directory.GetDirectories("Assets", "Resources", SearchOption.AllDirectories))
{
foreach (var file in Directory.GetFiles(resourcesDir, "*.*", recurseAll ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
{
if (AddressableAssetUtility.IsPathValidForEntry(file))
{
var g = AssetDatabase.AssetPathToGUID(file);
var addr = GetResourcesPath(file);
var entry = settings.CreateSubEntryIfUnique(g, addr, this);
if (entry != null) //TODO - it's probably really bad if this is ever null. need some error detection
{
entry.IsInResources = true;
entry.m_Labels = m_Labels;
if (entryFilter == null || entryFilter(entry))
assets.Add(entry);
}
}
}
if (!recurseAll)
{
foreach (var folder in Directory.GetDirectories(resourcesDir))
{
if (AssetDatabase.IsValidFolder(folder))
{
var entry = settings.CreateSubEntryIfUnique(AssetDatabase.AssetPathToGUID(folder), GetResourcesPath(folder), this);
if (entry != null) //TODO - it's probably really bad if this is ever null. need some error detection
{
entry.IsInResources = true;
entry.m_Labels = m_Labels;
if (entryFilter == null || entryFilter(entry))
assets.Add(entry);
}
}
}
}
}
}
else
{
var path = AssetPath;
if (string.IsNullOrEmpty(path))
return;
if (AssetDatabase.IsValidFolder(path))
{
foreach (var fi in Directory.GetFiles(path, "*.*", recurseAll ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
{
var file = fi.Replace('\\', '/');
if (AddressableAssetUtility.IsPathValidForEntry(file))
{
var subGuid = AssetDatabase.AssetPathToGUID(file);
if (!BuiltinSceneCache.Contains(new GUID(subGuid)))
{
var entry = settings.CreateSubEntryIfUnique(subGuid, address + GetRelativePath(file, path), this);
if (entry != null)
{
entry.IsInResources = IsInResources; //if this is a sub-folder of Resources, copy it on down
entry.m_Labels = m_Labels;
if (entryFilter == null || entryFilter(entry))
assets.Add(entry);
}
}
}
}
if (!recurseAll)
{
foreach (var fo in Directory.GetDirectories(path, "*.*", SearchOption.TopDirectoryOnly))
{
var folder = fo.Replace('\\', '/');
if (AssetDatabase.IsValidFolder(folder))
{
var entry = settings.CreateSubEntryIfUnique(AssetDatabase.AssetPathToGUID(folder), address + GetRelativePath(folder, path), this);
if (entry != null)
{
entry.IsInResources = IsInResources; //if this is a sub-folder of Resources, copy it on down
entry.m_Labels = m_Labels;
if (entryFilter == null || entryFilter(entry))
assets.Add(entry);
}
}
}
}
}
else
{
if (MainAssetType == typeof(AddressableAssetEntryCollection))
{
var col = AssetDatabase.LoadAssetAtPath<AddressableAssetEntryCollection>(AssetPath);
if (col != null)
{
foreach (var e in col.Entries)
{
var entry = settings.CreateSubEntryIfUnique(e.guid, e.address, this);
if (entry != null)
{
entry.IsInResources = e.IsInResources;
foreach (var l in e.labels)
entry.SetLabel(l, true, false);
foreach (var l in m_Labels)
entry.SetLabel(l, true, false);
if (entryFilter == null || entryFilter(entry))
assets.Add(entry);
}
}
}
}
else
{
if (includeSelf)
if (entryFilter == null || entryFilter(this))
assets.Add(this);
}
}
}
}
string GetRelativePath(string file, string path)
{
return file.Substring(path.Length);
}
/// <summary>
/// Implementation of ISerializationCallbackReceiver. Converts data to serializable form before serialization.
/// </summary>
public void OnBeforeSerialize()
{
m_SerializedLabels = new List<string>();
foreach (var t in m_Labels)
m_SerializedLabels.Add(t);
}
/// <summary>
/// Implementation of ISerializationCallbackReceiver. Converts data from serializable form after deserialization.
/// </summary>
public void OnAfterDeserialize()
{
m_Labels = new HashSet<string>();
foreach (var s in m_SerializedLabels)
m_Labels.Add(s);
m_SerializedLabels = null;
}
public void CreateCatalogEntries(List<ContentCatalogDataEntry> entries, bool isBundled, string providerType, IEnumerable<object> deps, object extraData)
{
var assetPath = GetAssetLoadPath(isBundled);
var keyList = CreateKeyList();
var mainType = MainAssetType;
if (mainType == typeof(SceneAsset))
mainType = typeof(SceneInstance);
if (!CheckForEditorAssembly(ref mainType, assetPath, !IsInResources))
return;
HashSet<Type> typesSeen = new HashSet<Type>();
typesSeen.Add(mainType);
var objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(AssetPath);
for (int i = 0; i < objs.Length; i++)
{
var o = objs[i];
var t = o.GetType();
var internalId = string.Format("{0}[{1}]", assetPath, o.name);
if (!CheckForEditorAssembly(ref t, internalId, false))
continue;
var namedAddress = string.Format("{0}.{1}", address, o.name);
entries.Add(new ContentCatalogDataEntry(t, internalId, providerType, new object[] { namedAddress }, deps, extraData));
if (!typesSeen.Contains(t))
{
entries.Add(new ContentCatalogDataEntry(t, assetPath, providerType, keyList, deps, extraData));
typesSeen.Add(t);
}
}
entries.Add(new ContentCatalogDataEntry(mainType, assetPath, providerType, keyList, deps, extraData));
}
static bool CheckForEditorAssembly(ref Type t, string internalId, bool warnIfStripped)
{
if (t == null)
return false;
if (t.Assembly.IsDefined(typeof(AssemblyIsEditorAssembly), true) || BuildUtility.IsEditorAssembly(t.Assembly))
{
if (t == typeof(UnityEditor.Animations.AnimatorController))
{
t = typeof(RuntimeAnimatorController);
return true;
}
if (t.FullName == "UnityEditor.Audio.AudioMixerController")
{
t = typeof(UnityEngine.Audio.AudioMixer);
return true;
}
if(warnIfStripped)
Debug.LogWarningFormat("Type {0} is in editor assembly {1}. Asset location with internal id {2} will be stripped.", t.Name, t.Assembly.FullName, internalId);
return false;
}
return true;
}
}
}