initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
using BuildCompression = UnityEngine.BuildCompression;
|
||||
|
||||
/// <summary>
|
||||
/// Build settings for addressables.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AddressableAssetBuildSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls whether to compile scripts when running in virtual mode. When disabled, build times are faster but the simulated bundle contents may not be accurate due to including editor code.
|
||||
/// </summary>
|
||||
public bool compileScriptsInVirtualMode
|
||||
{
|
||||
get { return m_CompileScriptsInVirtualMode; }
|
||||
set
|
||||
{
|
||||
m_CompileScriptsInVirtualMode = value;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_compileScriptsInVirtualMode")]
|
||||
[SerializeField]
|
||||
bool m_CompileScriptsInVirtualMode;
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether to remove temporary files after each build. When disabled, build times in packed mode are faster, but may not reflect all changes in assets.
|
||||
/// </summary>
|
||||
public bool cleanupStreamingAssetsAfterBuilds
|
||||
{
|
||||
get { return m_CleanupStreamingAssetsAfterBuilds; }
|
||||
set
|
||||
{
|
||||
m_CleanupStreamingAssetsAfterBuilds = value;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_cleanupStreamingAssetsAfterBuilds")]
|
||||
[SerializeField]
|
||||
bool m_CleanupStreamingAssetsAfterBuilds = true;
|
||||
|
||||
[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
|
||||
{
|
||||
if (m_LogResourceManagerExceptions != value)
|
||||
{
|
||||
m_LogResourceManagerExceptions = value;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// //Specifies where to build asset bundles, this is usually a temporary folder (or a folder in the project). Bundles are copied out of this location to their final destination.
|
||||
/// </summary>
|
||||
public string bundleBuildPath
|
||||
{
|
||||
get { return m_BundleBuildPath; }
|
||||
set
|
||||
{
|
||||
m_BundleBuildPath = value;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_bundleBuildPath")]
|
||||
[SerializeField]
|
||||
string m_BundleBuildPath = "Temp/com.unity.addressables/AssetBundles";
|
||||
|
||||
Hash128 m_CurrentHash;
|
||||
internal Hash128 currentHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_CurrentHash.isValid)
|
||||
HashUtilities.ComputeHash128(ref m_LogResourceManagerExceptions, ref m_CurrentHash);
|
||||
return m_CurrentHash;
|
||||
}
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
AddressableAssetSettings m_Settings;
|
||||
|
||||
void SetDirty()
|
||||
{
|
||||
m_CurrentHash = default;
|
||||
if (m_Settings != null)
|
||||
m_Settings.SetDirty(AddressableAssetSettings.ModificationEvent.BuildSettingsChanged, this, true, false);
|
||||
}
|
||||
|
||||
internal void OnAfterDeserialize(AddressableAssetSettings settings)
|
||||
{
|
||||
m_Settings = settings;
|
||||
}
|
||||
|
||||
internal void Validate(AddressableAssetSettings addressableAssetSettings)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0cbdebf3ba7c84c42a15f1cb8c47c14e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6b4a4e229baaa2544a4d681cc0dc5dea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a list of AddressableAssetEntries that can be included in the settings. The purpose of this class is to provide a way of combining entries from external sources such as packages into your project settings.
|
||||
/// </summary>
|
||||
[Obsolete("Addressable AssetEntryCollection is Obsolete")]
|
||||
public class AddressableAssetEntryCollection : ScriptableObject
|
||||
{
|
||||
[FormerlySerializedAs("m_serializeEntries")]
|
||||
[SerializeField]
|
||||
List<AddressableAssetEntry> m_SerializeEntries = new List<AddressableAssetEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// The collection of entries.
|
||||
/// </summary>
|
||||
public List<AddressableAssetEntry> Entries
|
||||
{
|
||||
get { return m_SerializeEntries; }
|
||||
}
|
||||
|
||||
internal static bool ConvertEntryCollectionToEntries(AddressableAssetEntryCollection collection, AddressableAssetSettings settings)
|
||||
{
|
||||
if (settings == null)
|
||||
settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
if (settings == null || !AssetDatabase.TryGetGUIDAndLocalFileIdentifier(collection, out var collectionGuid, out long localId))
|
||||
return false;
|
||||
|
||||
var collectionEntry = settings.FindAssetEntry(collectionGuid, true);
|
||||
|
||||
var group = collectionEntry == null ? settings.DefaultGroup : collectionEntry.parentGroup;
|
||||
List<AddressableAssetEntry> entries = new List<AddressableAssetEntry>();
|
||||
foreach (AddressableAssetEntry assetEntry in collection.Entries)
|
||||
{
|
||||
if (assetEntry == null)
|
||||
continue;
|
||||
var entry = settings.FindAssetEntry(assetEntry.guid);
|
||||
if (entry != null)
|
||||
continue;
|
||||
entries.Add(assetEntry);
|
||||
}
|
||||
|
||||
HashSet<string> collectionLabels = new HashSet<string>();
|
||||
if (collectionEntry != null)
|
||||
{
|
||||
collectionLabels = collectionEntry.labels;
|
||||
if (!settings.RemoveAssetEntry(collectionEntry))
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (AddressableAssetEntry entry in entries)
|
||||
{
|
||||
var newEntry = settings.CreateOrMoveEntry(entry.guid, group);
|
||||
newEntry.SetAddress(entry.address);
|
||||
foreach (string label in collectionLabels)
|
||||
newEntry.SetLabel(label, true);
|
||||
foreach (string label in entry.labels)
|
||||
newEntry.SetLabel(label, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 948f5bac7ffcefe4e8dbcf04b503e79b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,841 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the collection of asset entries associated with this group.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AddressableAssetGroup : ScriptableObject, IComparer<AddressableAssetEntry>, ISerializationCallbackReceiver
|
||||
{
|
||||
internal static GUIContent RemoveSchemaContent = new GUIContent("Remove Schema", "Remove this schema.");
|
||||
internal static GUIContent MoveSchemaUpContent = new GUIContent("Move Up", "Move schema up one in list.");
|
||||
internal static GUIContent MoveSchemaDownContent = new GUIContent("Move Down", "Move schema down one in list.");
|
||||
internal static GUIContent ExpandSchemaContent = new GUIContent("Expand All", "Expand all settings within schema.");
|
||||
|
||||
|
||||
[FormerlySerializedAs("m_name")]
|
||||
[SerializeField]
|
||||
string m_GroupName;
|
||||
|
||||
[FormerlySerializedAs("m_data")]
|
||||
[SerializeField]
|
||||
KeyDataStore m_Data;
|
||||
|
||||
[FormerlySerializedAs("m_guid")]
|
||||
[SerializeField]
|
||||
string m_GUID;
|
||||
|
||||
[FormerlySerializedAs("m_serializeEntries")]
|
||||
[SerializeField]
|
||||
List<AddressableAssetEntry> m_SerializeEntries = new List<AddressableAssetEntry>();
|
||||
|
||||
[FormerlySerializedAs("m_readOnly")]
|
||||
[SerializeField]
|
||||
internal bool m_ReadOnly;
|
||||
|
||||
[FormerlySerializedAs("m_settings")]
|
||||
[SerializeField]
|
||||
AddressableAssetSettings m_Settings;
|
||||
|
||||
[FormerlySerializedAs("m_schemaSet")]
|
||||
[SerializeField]
|
||||
AddressableAssetGroupSchemaSet m_SchemaSet = new AddressableAssetGroupSchemaSet();
|
||||
|
||||
Dictionary<string, AddressableAssetEntry> m_EntryMap = new Dictionary<string, AddressableAssetEntry>();
|
||||
List<AddressableAssetEntry> m_FolderEntryCache = null;
|
||||
List<AddressableAssetEntry> m_AssetCollectionEntryCache = null;
|
||||
|
||||
/// <summary>
|
||||
/// If true, this Group is likely marked 'Cannot Change Post Release', but has a modified asset since the previous build.
|
||||
/// </summary>
|
||||
public bool FlaggedDuringContentUpdateRestriction { get; internal set; }
|
||||
|
||||
internal void RefreshEntriesCache()
|
||||
{
|
||||
m_FolderEntryCache = new List<AddressableAssetEntry>();
|
||||
m_AssetCollectionEntryCache = new List<AddressableAssetEntry>();
|
||||
FlaggedDuringContentUpdateRestriction = false;
|
||||
foreach (AddressableAssetEntry e in entries)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.AssetPath) && e.MainAssetType == typeof(DefaultAsset) && AssetDatabase.IsValidFolder(e.AssetPath))
|
||||
m_FolderEntryCache.Add(e);
|
||||
#pragma warning disable 0618
|
||||
else if (!string.IsNullOrEmpty(e.AssetPath) && e.AssetPath.EndsWith(".asset", StringComparison.OrdinalIgnoreCase) && e.MainAssetType == typeof(AddressableAssetEntryCollection))
|
||||
m_AssetCollectionEntryCache.Add(e);
|
||||
#pragma warning restore 0618
|
||||
if (e.FlaggedDuringContentUpdateRestriction)
|
||||
FlaggedDuringContentUpdateRestriction = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The group name.
|
||||
/// </summary>
|
||||
public virtual string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_GroupName))
|
||||
m_GroupName = Guid;
|
||||
|
||||
return m_GroupName;
|
||||
}
|
||||
set
|
||||
{
|
||||
string newName = value;
|
||||
newName = newName.Replace('/', '-');
|
||||
newName = newName.Replace('\\', '-');
|
||||
if (newName != value)
|
||||
Debug.Log("Group names cannot include '\\' or '/'. Replacing with '-'. " + m_GroupName);
|
||||
if (m_GroupName != newName)
|
||||
{
|
||||
string previousName = m_GroupName;
|
||||
|
||||
string guid;
|
||||
long localId;
|
||||
if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(this, out guid, out localId))
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
var folder = Path.GetDirectoryName(path);
|
||||
var extension = Path.GetExtension(path);
|
||||
var newPath = $"{folder}/{newName}{extension}".Replace('\\', '/');
|
||||
if (path != newPath)
|
||||
{
|
||||
var setPath = AssetDatabase.MoveAsset(path, newPath);
|
||||
bool success = false;
|
||||
if (string.IsNullOrEmpty(setPath))
|
||||
{
|
||||
name = m_GroupName = newName;
|
||||
success = RenameSchemaAssets();
|
||||
}
|
||||
|
||||
if (success == false)
|
||||
{
|
||||
//unable to rename group due to invalid file name
|
||||
Debug.LogError("Rename of Group failed. " + setPath);
|
||||
name = m_GroupName = previousName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//this isn't a valid asset, which means it wasn't persisted, so just set the object name to the desired display name.
|
||||
name = m_GroupName = newName;
|
||||
}
|
||||
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.GroupRenamed, this, true, true);
|
||||
}
|
||||
else if (name != newName)
|
||||
{
|
||||
name = m_GroupName;
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.GroupRenamed, this, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The group GUID.
|
||||
/// </summary>
|
||||
public virtual string Guid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_GUID))
|
||||
m_GUID = GUID.Generate().ToString();
|
||||
return m_GUID;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of schemas for this group.
|
||||
/// </summary>
|
||||
public List<AddressableAssetGroupSchema> Schemas
|
||||
{
|
||||
get { return m_SchemaSet.Schemas; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the types of added schema for this group.
|
||||
/// </summary>
|
||||
public List<Type> SchemaTypes
|
||||
{
|
||||
get { return m_SchemaSet.Types; }
|
||||
}
|
||||
|
||||
string GetSchemaAssetPath(Type type)
|
||||
{
|
||||
return Settings.IsPersisted ? (Settings.GroupSchemaFolder + "/" + Name + "_" + type.Name + ".asset") : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a copy of the provided schema object.
|
||||
/// </summary>
|
||||
/// <param name="schema">The schema to add. A copy will be made and saved in a folder relative to the main Addressables settings asset. </param>
|
||||
/// <param name="postEvent">Determines if this method call will post an event to the internal addressables event system</param>
|
||||
/// <returns>The created schema object.</returns>
|
||||
public AddressableAssetGroupSchema AddSchema(AddressableAssetGroupSchema schema, bool postEvent = true)
|
||||
{
|
||||
var added = m_SchemaSet.AddSchema(schema, GetSchemaAssetPath);
|
||||
if (added != null)
|
||||
{
|
||||
added.Group = this;
|
||||
if (m_Settings && m_Settings.IsPersisted)
|
||||
EditorUtility.SetDirty(added);
|
||||
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.GroupSchemaAdded, this, postEvent, true);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds a schema of a given type to this group. The schema asset will be created in the GroupSchemas directory relative to the settings asset.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type. This type must not already be added.</param>
|
||||
/// <param name="postEvent">Determines if this method call will post an event to the internal addressables event system</param>
|
||||
/// <returns>The created schema object.</returns>
|
||||
public AddressableAssetGroupSchema AddSchema(Type type, bool postEvent = true)
|
||||
{
|
||||
var added = m_SchemaSet.AddSchema(type, GetSchemaAssetPath);
|
||||
if (added != null)
|
||||
{
|
||||
added.Group = this;
|
||||
if (m_Settings && m_Settings.IsPersisted)
|
||||
EditorUtility.SetDirty(added);
|
||||
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.GroupSchemaAdded, this, postEvent, true);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds a schema of a given type to this group.
|
||||
/// </summary>
|
||||
/// <param name="postEvent">Determines if this method call will post an event to the internal addressables event system</param>
|
||||
/// <typeparam name="TSchema">The schema type. This type must not already be added.</typeparam>
|
||||
/// <returns>The created schema object.</returns>
|
||||
public TSchema AddSchema<TSchema>(bool postEvent = true) where TSchema : AddressableAssetGroupSchema
|
||||
{
|
||||
return AddSchema(typeof(TSchema), postEvent) as TSchema;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a given schema from this group.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <param name="postEvent">Determines if this method call will post an event to the internal addressables event system</param>
|
||||
/// <returns>True if the schema was found and removed, false otherwise.</returns>
|
||||
public bool RemoveSchema(Type type, bool postEvent = true)
|
||||
{
|
||||
if (!m_SchemaSet.RemoveSchema(type))
|
||||
return false;
|
||||
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.GroupSchemaRemoved, this, postEvent, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a given schema from this group.
|
||||
/// </summary>
|
||||
/// <param name="postEvent">Determines if this method call will post an event to the internal addressables event system</param>
|
||||
/// <typeparam name="TSchema">The schema type.</typeparam>
|
||||
/// <returns>True if the schema was found and removed, false otherwise.</returns>
|
||||
public bool RemoveSchema<TSchema>(bool postEvent = true)
|
||||
{
|
||||
return RemoveSchema(typeof(TSchema), postEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an added schema of the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSchema">The schema type.</typeparam>
|
||||
/// <returns>The schema if found, otherwise null.</returns>
|
||||
public TSchema GetSchema<TSchema>() where TSchema : AddressableAssetGroupSchema
|
||||
{
|
||||
return GetSchema(typeof(TSchema)) as TSchema;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an added schema of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <returns>The schema if found, otherwise null.</returns>
|
||||
public AddressableAssetGroupSchema GetSchema(Type type)
|
||||
{
|
||||
return m_SchemaSet.GetSchema(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the group contains a schema of a given type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSchema">The schema type.</typeparam>
|
||||
/// <returns>True if the schema type or subclass has been added to this group.</returns>
|
||||
public bool HasSchema<TSchema>()
|
||||
{
|
||||
return HasSchema(typeof(TSchema));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all schemas and optionally deletes the assets associated with them.
|
||||
/// </summary>
|
||||
/// <param name="deleteAssets">If true, the schema assets will also be deleted.</param>
|
||||
/// <param name="postEvent">Determines if this method call will post an event to the internal addressables event system</param>
|
||||
public void ClearSchemas(bool deleteAssets, bool postEvent = true)
|
||||
{
|
||||
m_SchemaSet.ClearSchemas(deleteAssets);
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.GroupRemoved, this, postEvent, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the group contains a schema of a given type.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <returns>True if the schema type or subclass has been added to this group.</returns>
|
||||
public bool HasSchema(Type type)
|
||||
{
|
||||
return GetSchema(type) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is this group read only. This is normally false. Built in resources (resource folders and the scene list) are put into a special read only group.
|
||||
/// </summary>
|
||||
public virtual bool ReadOnly
|
||||
{
|
||||
get { return m_ReadOnly; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AddressableAssetSettings that this group belongs to.
|
||||
/// </summary>
|
||||
public AddressableAssetSettings Settings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Settings == null)
|
||||
m_Settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
|
||||
return m_Settings;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection of asset entries.
|
||||
/// </summary>
|
||||
public virtual ICollection<AddressableAssetEntry> entries
|
||||
{
|
||||
get { return m_EntryMap.Values; }
|
||||
}
|
||||
|
||||
internal Dictionary<string, AddressableAssetEntry> EntryMap => m_EntryMap;
|
||||
|
||||
internal ICollection<AddressableAssetEntry> FolderEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_FolderEntryCache == null)
|
||||
RefreshEntriesCache();
|
||||
return m_FolderEntryCache;
|
||||
}
|
||||
}
|
||||
|
||||
internal ICollection<AddressableAssetEntry> AssetCollectionEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_AssetCollectionEntryCache == null)
|
||||
RefreshEntriesCache();
|
||||
return m_AssetCollectionEntryCache;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the default group.
|
||||
/// </summary>
|
||||
public virtual bool Default
|
||||
{
|
||||
get { return Guid == Settings.DefaultGroup.Guid; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two asset entries based on their guids.
|
||||
/// </summary>
|
||||
/// <param name="x">The first entry to compare.</param>
|
||||
/// <param name="y">The second entry to compare.</param>
|
||||
/// <returns>Returns 0 if both entries are null or equivalent.
|
||||
/// Returns -1 if the first entry is null or the first entry precedes the second entry in the sort order.
|
||||
/// Returns 1 if the second entry is null or the first entry follows the second entry in the sort order.</returns>
|
||||
public virtual int Compare(AddressableAssetEntry x, AddressableAssetEntry y)
|
||||
{
|
||||
if (x == null && y == null)
|
||||
return 0;
|
||||
if (x == null)
|
||||
return -1;
|
||||
if (y == null)
|
||||
return 1;
|
||||
return x.guid.CompareTo(y.guid);
|
||||
}
|
||||
|
||||
Hash128 m_CurrentHash;
|
||||
internal Hash128 currentHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_CurrentHash.isValid)
|
||||
{
|
||||
m_CurrentHash.Append(m_GroupName);
|
||||
m_CurrentHash.Append(m_GUID);
|
||||
m_CurrentHash.Append(entries.Count);
|
||||
m_CurrentHash.Append(ref m_ReadOnly);
|
||||
foreach (var e in entries)
|
||||
{
|
||||
var eh = e.currentHash;
|
||||
m_CurrentHash.Append(ref eh);
|
||||
}
|
||||
}
|
||||
return m_CurrentHash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts data to serializable format.
|
||||
/// </summary>
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
if (m_SerializeEntries == null)
|
||||
{
|
||||
m_SerializeEntries = new List<AddressableAssetEntry>(entries.Count);
|
||||
foreach (var e in entries)
|
||||
m_SerializeEntries.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts data from serializable format.
|
||||
/// </summary>
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
ResetEntryMap();
|
||||
}
|
||||
|
||||
internal void ResetEntryMap()
|
||||
{
|
||||
m_EntryMap.Clear();
|
||||
m_FolderEntryCache = null;
|
||||
m_AssetCollectionEntryCache = null;
|
||||
foreach (var e in m_SerializeEntries)
|
||||
{
|
||||
try
|
||||
{
|
||||
e.parentGroup = this;
|
||||
e.IsSubAsset = false;
|
||||
m_EntryMap.Add(e.guid, e);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Addressables.InternalSafeSerializationLog(e.address);
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
Validate();
|
||||
}
|
||||
|
||||
internal void Validate()
|
||||
{
|
||||
bool allValid = false;
|
||||
while (!allValid)
|
||||
{
|
||||
allValid = true;
|
||||
for (int i = 0; i < m_SchemaSet.Schemas.Count; i++)
|
||||
{
|
||||
if (m_SchemaSet.Schemas[i] == null)
|
||||
{
|
||||
m_SchemaSet.Schemas.RemoveAt(i);
|
||||
allValid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_SchemaSet.Schemas[i].Group == null)
|
||||
m_SchemaSet.Schemas[i].Group = this;
|
||||
|
||||
m_SchemaSet.Schemas[i].Validate();
|
||||
}
|
||||
}
|
||||
|
||||
var editorList = GetAssetEntry(AddressableAssetEntry.EditorSceneListName);
|
||||
if (editorList != null)
|
||||
{
|
||||
if (m_GroupName == null)
|
||||
m_GroupName = AddressableAssetSettings.PlayerDataGroupName;
|
||||
if (m_Data != null)
|
||||
{
|
||||
if (!HasSchema<PlayerDataGroupSchema>())
|
||||
AddSchema<PlayerDataGroupSchema>();
|
||||
m_Data = null;
|
||||
}
|
||||
}
|
||||
else if (m_Settings != null)
|
||||
{
|
||||
if (m_GroupName == null)
|
||||
m_GroupName = Settings.FindUniqueGroupName("Packed Content Group");
|
||||
m_Data = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void DedupeEnteries()
|
||||
{
|
||||
if (m_Settings == null)
|
||||
return;
|
||||
|
||||
List<AddressableAssetEntry> removeEntries = new List<AddressableAssetEntry>();
|
||||
foreach (AddressableAssetEntry e in m_EntryMap.Values)
|
||||
{
|
||||
AddressableAssetEntry lookedUpEntry = m_Settings.FindAssetEntry(e.guid);
|
||||
if (lookedUpEntry.parentGroup != this)
|
||||
{
|
||||
Debug.LogWarning(e.address
|
||||
+ " is already a member of group "
|
||||
+ lookedUpEntry.parentGroup
|
||||
+ " but group "
|
||||
+ m_GroupName
|
||||
+ " contained a reference to it. Removing referece.");
|
||||
removeEntries.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (removeEntries.Count > 0)
|
||||
RemoveAssetEntries(removeEntries);
|
||||
}
|
||||
|
||||
internal void Initialize(AddressableAssetSettings settings, string groupName, string guid, bool readOnly)
|
||||
{
|
||||
m_Settings = settings;
|
||||
m_GroupName = groupName;
|
||||
if (m_GroupName == null)
|
||||
m_GroupName = settings.FindUniqueGroupName("Packed Content Group");
|
||||
m_ReadOnly = readOnly;
|
||||
m_GUID = guid;
|
||||
m_Data = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gathers all asset entries. Each explicit entry may contain multiple sub entries. For example, addressable folders create entries for each asset contained within.
|
||||
/// </summary>
|
||||
/// <param name="results">The generated list of entries. For simple entries, this will contain just the entry itself if specified.</param>
|
||||
/// <param name="includeSelf">Determines if the entry should be contained in the result list or just sub entries.</param>
|
||||
/// <param name="recurseAll">Determines if full recursion should be done when gathering entries.</param>
|
||||
/// <param name="includeSubObjects">Determines if sub objects such as sprites should be included.</param>
|
||||
/// <param name="entryFilter">Optional predicate to run against each entry, only returning those that pass. A null filter will return all entries</param>
|
||||
public virtual void GatherAllAssets(List<AddressableAssetEntry> results, bool includeSelf, bool recurseAll, bool includeSubObjects, Func<AddressableAssetEntry, bool> entryFilter = null)
|
||||
{
|
||||
foreach (var e in entries)
|
||||
if (entryFilter == null || entryFilter(e))
|
||||
e.GatherAllAssets(results, includeSelf, recurseAll, includeSubObjects, entryFilter);
|
||||
}
|
||||
|
||||
internal void GatherAllDirectAssetReferenceEntryData(List<IReferenceEntryData> results, HashSet<string> processed)
|
||||
{
|
||||
if (processed == null)
|
||||
processed = new HashSet<string>();
|
||||
|
||||
// go through all entries that are not in folder/collections
|
||||
foreach (AddressableAssetEntry entry in entries)
|
||||
{
|
||||
var path = entry.AssetPath;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
|
||||
if (processed.Contains(path))
|
||||
continue;
|
||||
if (FolderEntries.Contains(entry))
|
||||
continue;
|
||||
if (entry.guid == AddressableAssetEntry.EditorSceneListName || entry.guid == AddressableAssetEntry.ResourcesName)
|
||||
continue;
|
||||
|
||||
processed.Add(path);
|
||||
var reference = new ImplicitAssetEntry()
|
||||
{
|
||||
address = entry.address,
|
||||
AssetPath = entry.AssetPath,
|
||||
IsInResources = entry.IsInResources,
|
||||
labels = new HashSet<string>(entry.labels)
|
||||
};
|
||||
results.Add(reference);
|
||||
}
|
||||
}
|
||||
|
||||
internal void GatherAllFolderSubAssetReferenceEntryData(List<IReferenceEntryData> results, HashSet<string> processed)
|
||||
{
|
||||
if (processed == null)
|
||||
processed = new HashSet<string>();
|
||||
|
||||
// go through all entries that are in folder
|
||||
foreach (AddressableAssetEntry folderEntry in FolderEntries)
|
||||
{
|
||||
var path = folderEntry.AssetPath;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
if (processed.Contains(path))
|
||||
continue;
|
||||
if (folderEntry.guid == AddressableAssetEntry.EditorSceneListName || folderEntry.guid == AddressableAssetEntry.ResourcesName)
|
||||
continue;
|
||||
|
||||
processed.Add(folderEntry.AssetPath);
|
||||
|
||||
string[] guids = AssetDatabase.FindAssets("", new[] {folderEntry.AssetPath});
|
||||
foreach (var guid in guids)
|
||||
{
|
||||
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
return;
|
||||
if (processed.Contains(assetPath))
|
||||
continue;
|
||||
processed.Add(assetPath);
|
||||
|
||||
if (BuiltinSceneCache.Contains(assetPath))
|
||||
continue;
|
||||
if (AssetDatabase.IsValidFolder(assetPath))
|
||||
continue;
|
||||
if (!AddressableAssetUtility.IsPathValidForEntry(assetPath))
|
||||
continue;
|
||||
|
||||
string relativePath = assetPath.Remove(0, folderEntry.AssetPath.Length);
|
||||
string relativeAddress = folderEntry.address + relativePath;
|
||||
var reference = new ImplicitAssetEntry()
|
||||
{
|
||||
address = relativeAddress,
|
||||
AssetPath = assetPath,
|
||||
IsInResources = folderEntry.IsInResources,
|
||||
labels = new HashSet<string>(folderEntry.labels)
|
||||
};
|
||||
results.Add(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 0618
|
||||
internal void GatherAllAssetCollectionAssetReferenceEntryData(List<IReferenceEntryData> results, HashSet<string> processed)
|
||||
{
|
||||
if (processed == null)
|
||||
processed = new HashSet<string>();
|
||||
|
||||
if (AssetCollectionEntries.Count != 0)
|
||||
{
|
||||
foreach (var e in m_AssetCollectionEntryCache)
|
||||
{
|
||||
var entries = new List<AddressableAssetEntry>();
|
||||
e.GatherAssetEntryCollectionEntries(entries, null);
|
||||
|
||||
foreach (AddressableAssetEntry entry in entries)
|
||||
{
|
||||
// do entries
|
||||
string assetPath = entry.AssetPath;
|
||||
if (string.IsNullOrEmpty(assetPath))
|
||||
return;
|
||||
if (processed.Contains(assetPath))
|
||||
continue;
|
||||
processed.Add(assetPath);
|
||||
|
||||
if (AssetDatabase.IsValidFolder(assetPath))
|
||||
continue;
|
||||
if (!AddressableAssetUtility.IsPathValidForEntry(assetPath))
|
||||
continue;
|
||||
|
||||
var reference = new ImplicitAssetEntry()
|
||||
{
|
||||
address = entry.address,
|
||||
AssetPath = entry.AssetPath,
|
||||
IsInResources = entry.IsInResources,
|
||||
labels = new HashSet<string>(entry.labels)
|
||||
};
|
||||
results.Add(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore 0618
|
||||
|
||||
internal void AddAssetEntry(AddressableAssetEntry e, bool postEvent = true)
|
||||
{
|
||||
e.IsSubAsset = false;
|
||||
e.parentGroup = this;
|
||||
m_EntryMap[e.guid] = e;
|
||||
if (m_FolderEntryCache != null && !string.IsNullOrEmpty(e.AssetPath) && e.MainAssetType == typeof(DefaultAsset) && AssetDatabase.IsValidFolder(e.AssetPath))
|
||||
m_FolderEntryCache.Add(e);
|
||||
#pragma warning disable 0618
|
||||
else if (m_AssetCollectionEntryCache != null && !string.IsNullOrEmpty(e.AssetPath) && e.AssetPath.EndsWith(".asset", StringComparison.OrdinalIgnoreCase) && e.MainAssetType == typeof(AddressableAssetEntryCollection))
|
||||
m_AssetCollectionEntryCache.Add(e);
|
||||
#pragma warning restore 0618
|
||||
if (HasSchema<ContentUpdateGroupSchema>() && !GetSchema<ContentUpdateGroupSchema>().StaticContent)
|
||||
e.FlaggedDuringContentUpdateRestriction = false;
|
||||
m_SerializeEntries = null;
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.EntryAdded, e, postEvent, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an entry via the asset guid.
|
||||
/// </summary>
|
||||
/// <param name="guid">The asset guid.</param>
|
||||
/// <returns></returns>
|
||||
public virtual AddressableAssetEntry GetAssetEntry(string guid)
|
||||
{
|
||||
return GetAssetEntry(guid, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an entry via the asset guid.
|
||||
/// </summary>
|
||||
/// <param name="guid">The asset guid.</param>
|
||||
/// <param name="includeImplicit">Whether or not to include implicit asset entries in the search.</param>
|
||||
/// <returns></returns>
|
||||
public virtual AddressableAssetEntry GetAssetEntry(string guid, bool includeImplicit)
|
||||
{
|
||||
if (m_EntryMap.TryGetValue(guid, out var entry))
|
||||
return entry;
|
||||
return includeImplicit ? GetImplicitAssetEntry(guid, null) : null;
|
||||
}
|
||||
|
||||
internal AddressableAssetEntry GetImplicitAssetEntry(string assetGuid, string assetPath)
|
||||
{
|
||||
if (AssetCollectionEntries.Count != 0)
|
||||
{
|
||||
AddressableAssetEntry entry;
|
||||
foreach (var e in m_AssetCollectionEntryCache)
|
||||
{
|
||||
entry = e.GetAssetCollectionSubEntry(assetGuid);
|
||||
if (entry != null)
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (FolderEntries.Count != 0)
|
||||
{
|
||||
if (assetPath == null)
|
||||
assetPath = AssetDatabase.GUIDToAssetPath(assetGuid);
|
||||
|
||||
AddressableAssetEntry entry;
|
||||
foreach (var e in m_FolderEntryCache)
|
||||
{
|
||||
entry = e.GetFolderSubEntry(assetGuid, assetPath);
|
||||
if (entry != null)
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the object as modified.
|
||||
/// </summary>
|
||||
/// <param name="modificationEvent">The event type that is changed.</param>
|
||||
/// <param name="eventData">The object data that corresponds to the event.</param>
|
||||
/// <param name="postEvent">If true, the event is propagated to callbacks.</param>
|
||||
/// <param name="groupModified">If true, the group asset will be marked as dirty.</param>
|
||||
public void SetDirty(AddressableAssetSettings.ModificationEvent modificationEvent, object eventData, bool postEvent, bool groupModified = false)
|
||||
{
|
||||
m_CurrentHash = default;
|
||||
if (Settings != null)
|
||||
{
|
||||
if (groupModified && Settings.IsPersisted && this != null)
|
||||
EditorUtility.SetDirty(this);
|
||||
Settings.SetDirty(modificationEvent, eventData, postEvent, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove an entry.
|
||||
/// </summary>
|
||||
/// <param name="entry">The entry to remove.</param>
|
||||
/// <param name="postEvent">If true, post the event to callbacks.</param>
|
||||
public void RemoveAssetEntry(AddressableAssetEntry entry, bool postEvent = true)
|
||||
{
|
||||
m_EntryMap.Remove(entry.guid);
|
||||
m_FolderEntryCache?.Remove(entry);
|
||||
m_AssetCollectionEntryCache?.Remove(entry);
|
||||
entry.parentGroup = null;
|
||||
m_SerializeEntries = null;
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.EntryRemoved, entry, postEvent, true);
|
||||
}
|
||||
|
||||
internal void RemoveAssetEntries(IEnumerable<AddressableAssetEntry> removeEntries, bool postEvent = true)
|
||||
{
|
||||
foreach (AddressableAssetEntry entry in removeEntries)
|
||||
{
|
||||
m_EntryMap.Remove(entry.guid);
|
||||
m_FolderEntryCache?.Remove(entry);
|
||||
m_AssetCollectionEntryCache?.Remove(entry);
|
||||
entry.parentGroup = null;
|
||||
}
|
||||
|
||||
if (removeEntries.Count() > 0)
|
||||
{
|
||||
m_SerializeEntries = null;
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.EntryRemoved, removeEntries.ToArray(), postEvent, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if a group is the Default Group.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsDefaultGroup()
|
||||
{
|
||||
return Guid == m_Settings.DefaultGroup.Guid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a group has the appropriate schemas and attributes that the Default Group requires.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool CanBeSetAsDefault()
|
||||
{
|
||||
return !m_ReadOnly;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of a schema based on its specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <returns>Valid index if found, otherwise returns -1.</returns>
|
||||
public int FindSchema(Type type)
|
||||
{
|
||||
var schemas = m_SchemaSet.Schemas;
|
||||
for (int i = 0; i < schemas.Count; i++)
|
||||
{
|
||||
if (schemas[i].GetType() == type)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private bool RenameSchemaAssets()
|
||||
{
|
||||
return m_SchemaSet.RenameSchemaAssets(GetSchemaAssetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bbb281ee3bf0b054c82ac2347e9e782c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEditor.AddressableAssets.GUI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data for AddressableAssetGroups.
|
||||
/// </summary>
|
||||
public class AddressableAssetGroupSchema : ScriptableObject
|
||||
{
|
||||
[FormerlySerializedAs("m_group")]
|
||||
[AddressableReadOnly]
|
||||
[SerializeField]
|
||||
AddressableAssetGroup m_Group;
|
||||
|
||||
SerializedObject m_SchemaSerializedObject = null;
|
||||
|
||||
internal SerializedObject SchemaSerializedObject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_SchemaSerializedObject == null)
|
||||
m_SchemaSerializedObject = new SerializedObject(this);
|
||||
return m_SchemaSerializedObject;
|
||||
}
|
||||
set { m_SchemaSerializedObject = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the group that the schema belongs to.
|
||||
/// </summary>
|
||||
public AddressableAssetGroup Group
|
||||
{
|
||||
get { return m_Group; }
|
||||
internal set
|
||||
{
|
||||
m_Group = value;
|
||||
if (m_Group != null)
|
||||
{
|
||||
OnSetGroup(m_Group);
|
||||
Validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to perform post creation initialization.
|
||||
/// </summary>
|
||||
/// <param name="group">The group that the schema is added to.</param>
|
||||
protected virtual void OnSetGroup(AddressableAssetGroup group)
|
||||
{
|
||||
}
|
||||
|
||||
internal virtual void Validate()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to display the GUI of the schema.
|
||||
/// </summary>
|
||||
public virtual void OnGUI()
|
||||
{
|
||||
var type = GetType();
|
||||
var p = SchemaSerializedObject.GetIterator();
|
||||
p.Next(true);
|
||||
while (p.Next(false))
|
||||
{
|
||||
var prop = type.GetField(p.name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
if (prop != null)
|
||||
EditorGUILayout.PropertyField(p, true);
|
||||
}
|
||||
|
||||
SchemaSerializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to display the GUI of multiple selected groups.
|
||||
/// </summary>
|
||||
/// <param name="otherSchemas">Schema instances in the other selected groups</param>
|
||||
public virtual void OnGUIMultiple(List<AddressableAssetGroupSchema> otherSchemas)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to notify the addressables settings that data has been modified. This must be called by subclasses to ensure proper cache invalidation.
|
||||
/// </summary>
|
||||
/// <param name="postEvent">Determines if this method call will post an event to the internal addressables event system</param>
|
||||
protected void SetDirty(bool postEvent)
|
||||
{
|
||||
m_SchemaSerializedObject = null;
|
||||
if (m_Group != null)
|
||||
{
|
||||
if (m_Group.Settings != null && m_Group.Settings.IsPersisted)
|
||||
{
|
||||
EditorUtility.SetDirty(this);
|
||||
AddressableAssetUtility.OpenAssetIfUsingVCIntegration(this);
|
||||
}
|
||||
|
||||
if (m_Group != null)
|
||||
m_Group.SetDirty(AddressableAssetSettings.ModificationEvent.GroupSchemaModified, this, postEvent, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for drawing properties in the inspector.
|
||||
/// </summary>
|
||||
public virtual void ShowAllProperties()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display mixed values for the specified property found in a list of schemas.
|
||||
/// </summary>
|
||||
/// <param name="property">The property.</param>
|
||||
/// <param name="otherSchemas">The list of schemas that may contain the property.</param>
|
||||
/// <param name="type">The property type.</param>
|
||||
/// <param name="propertyName">The property name.</param>
|
||||
protected void ShowMixedValue(SerializedProperty property, List<AddressableAssetGroupSchema> otherSchemas, Type type, string propertyName)
|
||||
{
|
||||
foreach (var schema in otherSchemas)
|
||||
{
|
||||
var s_prop = schema.SchemaSerializedObject.FindProperty(propertyName);
|
||||
if ((property.propertyType == SerializedPropertyType.Enum && (property.enumValueIndex != s_prop.enumValueIndex)) ||
|
||||
(property.propertyType == SerializedPropertyType.String && (property.stringValue != s_prop.stringValue)) ||
|
||||
(property.propertyType == SerializedPropertyType.Integer && (property.intValue != s_prop.intValue)) ||
|
||||
(property.propertyType == SerializedPropertyType.Boolean && (property.boolValue != s_prop.boolValue)))
|
||||
{
|
||||
EditorGUI.showMixedValue = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == typeof(ProfileValueReference))
|
||||
{
|
||||
var field = property.serializedObject.targetObject.GetType().GetField(property.name,
|
||||
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance |
|
||||
BindingFlags.DeclaredOnly);
|
||||
|
||||
string lhsId = (field?.GetValue(property.serializedObject.targetObject) as ProfileValueReference)?.Id;
|
||||
string rhsId = (field?.GetValue(s_prop.serializedObject.targetObject) as ProfileValueReference)?.Id;
|
||||
|
||||
if (lhsId != null && rhsId != null && lhsId != rhsId)
|
||||
{
|
||||
EditorGUI.showMixedValue = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == typeof(SerializedType))
|
||||
{
|
||||
var field = property.serializedObject.targetObject.GetType().GetField(property.name,
|
||||
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance |
|
||||
BindingFlags.DeclaredOnly);
|
||||
|
||||
Type lhs = ((SerializedType)field?.GetValue(property.serializedObject.targetObject)).Value;
|
||||
Type rhs = ((SerializedType)field?.GetValue(s_prop.serializedObject.targetObject)).Value;
|
||||
|
||||
if (lhs != null && rhs != null && lhs != rhs)
|
||||
{
|
||||
EditorGUI.showMixedValue = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef7207c080a54574b9bfc095d91595aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of AddressableAssetGroupSchema objects
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AddressableAssetGroupSchemaSet
|
||||
{
|
||||
[FormerlySerializedAs("m_schemas")]
|
||||
[SerializeField]
|
||||
List<AddressableAssetGroupSchema> m_Schemas = new List<AddressableAssetGroupSchema>();
|
||||
|
||||
/// <summary>
|
||||
/// List of schemas for this group.
|
||||
/// </summary>
|
||||
public List<AddressableAssetGroupSchema> Schemas
|
||||
{
|
||||
get { return m_Schemas; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the list of schema types.
|
||||
/// </summary>
|
||||
public List<Type> Types
|
||||
{
|
||||
get
|
||||
{
|
||||
var types = new List<Type>(m_Schemas.Count);
|
||||
foreach (var s in m_Schemas)
|
||||
types.Add(s.GetType());
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a copy of the provided schema object.
|
||||
/// </summary>
|
||||
/// <param name="schema">The schema to copy.</param>
|
||||
/// <param name="pathFunc">A function that returns the path where this method can save the schema asset. Set to null to not create an in-project asset.</param>
|
||||
/// <returns>The created schema object.</returns>
|
||||
public AddressableAssetGroupSchema AddSchema(AddressableAssetGroupSchema schema, Func<Type, string> pathFunc)
|
||||
{
|
||||
if (schema == null)
|
||||
{
|
||||
Debug.LogWarning("Cannot add null Schema object.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = schema.GetType();
|
||||
|
||||
if (GetSchema(type) != null)
|
||||
{
|
||||
Debug.LogWarningFormat("Cannot add multiple schemas of the same type: {0}.", type.FullName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pathFunc == null)
|
||||
{
|
||||
m_Schemas.Add(schema);
|
||||
return schema;
|
||||
}
|
||||
|
||||
var assetName = pathFunc(type);
|
||||
if (File.Exists(assetName))
|
||||
{
|
||||
Debug.LogWarningFormat("Schema asset already exists at path {0}, relinking.", assetName);
|
||||
var existingSchema = AssetDatabase.LoadAssetAtPath(assetName, type) as AddressableAssetGroupSchema;
|
||||
m_Schemas.Add(existingSchema);
|
||||
return existingSchema;
|
||||
}
|
||||
|
||||
var newSchema = Object.Instantiate(schema);
|
||||
if (!string.IsNullOrEmpty(assetName))
|
||||
{
|
||||
var dir = Path.GetDirectoryName(assetName);
|
||||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
AssetDatabase.CreateAsset(newSchema, assetName);
|
||||
}
|
||||
|
||||
m_Schemas.Add(newSchema);
|
||||
return newSchema;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and adds a schema of a given type to this group. The schema asset will be created in the GroupSchemas directory relative to the settings asset.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type. This type must not already be added.</param>
|
||||
/// <param name="pathFunc">A function that returns the path where this method can save the schema asset. Set to null to not create an in-project asset.</param>
|
||||
/// <returns>The created schema object.</returns>
|
||||
public AddressableAssetGroupSchema AddSchema(Type type, Func<Type, string> pathFunc)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
Debug.LogWarning("Cannot add null Schema type.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!typeof(AddressableAssetGroupSchema).IsAssignableFrom(type))
|
||||
{
|
||||
Debug.LogWarningFormat("Invalid Schema type {0}. Schemas must inherit from AddressableAssetGroupSchema.", type.FullName);
|
||||
return null;
|
||||
}
|
||||
|
||||
var existing = GetSchema(type);
|
||||
if (existing != null)
|
||||
{
|
||||
Debug.LogWarningFormat("Cannot add multiple schemas of the same type: {0}.", existing.GetType().FullName);
|
||||
return existing;
|
||||
}
|
||||
|
||||
var assetName = pathFunc(type);
|
||||
if (File.Exists(assetName))
|
||||
{
|
||||
Debug.LogWarningFormat("Schema asset already exists at path {0}, relinking.", assetName);
|
||||
var existingSchema = AssetDatabase.LoadAssetAtPath(assetName, type) as AddressableAssetGroupSchema;
|
||||
m_Schemas.Add(existingSchema);
|
||||
return existingSchema;
|
||||
}
|
||||
|
||||
var schema = ScriptableObject.CreateInstance(type) as AddressableAssetGroupSchema;
|
||||
if (!string.IsNullOrEmpty(assetName))
|
||||
{
|
||||
var dir = Path.GetDirectoryName(assetName);
|
||||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
AssetDatabase.CreateAsset(schema, assetName);
|
||||
}
|
||||
|
||||
m_Schemas.Add(schema);
|
||||
return schema;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a given schema from this group.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <returns>True if the schema was found and removed, false otherwise.</returns>
|
||||
public bool RemoveSchema(Type type)
|
||||
{
|
||||
for (int i = 0; i < m_Schemas.Count; i++)
|
||||
{
|
||||
var s = m_Schemas[i];
|
||||
if (s.GetType() == type)
|
||||
{
|
||||
m_Schemas.RemoveAt(i);
|
||||
string guid;
|
||||
long lfid;
|
||||
if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(s, out guid, out lfid))
|
||||
AssetDatabase.DeleteAsset(AssetDatabase.GUIDToAssetPath(guid));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an added schema of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <returns>The schema if found, otherwise null.</returns>
|
||||
public AddressableAssetGroupSchema GetSchema(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
Debug.LogWarning("Cannot get schema with null type.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!typeof(AddressableAssetGroupSchema).IsAssignableFrom(type))
|
||||
{
|
||||
Debug.LogWarningFormat("Invalid Schema type {0}. Schemas must inherit from AddressableAssetGroupSchema.", type.FullName);
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var s in m_Schemas)
|
||||
if (type == s.GetType())
|
||||
return s;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all schemas and optionally deletes the assets associated with them.
|
||||
/// </summary>
|
||||
/// <param name="deleteAssets">If true, the schema assets will also be deleted.</param>
|
||||
public void ClearSchemas(bool deleteAssets)
|
||||
{
|
||||
if (deleteAssets)
|
||||
{
|
||||
for (int i = 0; i < m_Schemas.Count; i++)
|
||||
{
|
||||
var s = m_Schemas[i];
|
||||
string guid;
|
||||
long lfid;
|
||||
if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(s, out guid, out lfid))
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (File.Exists(path))
|
||||
AssetDatabase.DeleteAsset(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Schemas.Clear();
|
||||
}
|
||||
|
||||
internal bool RenameSchemaAssets(Func<Type, string> pathFunc)
|
||||
{
|
||||
bool result = true;
|
||||
foreach (var schema in m_Schemas)
|
||||
{
|
||||
string guid;
|
||||
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(schema, out guid, out long lfid))
|
||||
continue;
|
||||
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (string.IsNullOrEmpty(path))
|
||||
continue;
|
||||
|
||||
string newPath = pathFunc(schema.GetType());
|
||||
if (path == newPath)
|
||||
continue;
|
||||
|
||||
string setPath = AssetDatabase.MoveAsset(path, newPath);
|
||||
if (!string.IsNullOrEmpty(setPath))
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2fa9998af31eaba4d86afdc900c345ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
// TODO: OBSELETE: This is replaced with AddressableAssetGroupTemplate, this is needed to update existing setups to new Preset method
|
||||
/// <summary>
|
||||
/// Contains a set of schemas used by the GUI to create predefined asset groups.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AddressableAssetGroupSchemaTemplate
|
||||
{
|
||||
[FormerlySerializedAs("m_displayName")]
|
||||
[SerializeField]
|
||||
string m_DisplayName;
|
||||
|
||||
[FormerlySerializedAs("m_description")]
|
||||
[SerializeField]
|
||||
string m_Description;
|
||||
|
||||
[FormerlySerializedAs("m_schemaTypes")]
|
||||
[SerializeField]
|
||||
List<SerializedType> m_SchemaTypes;
|
||||
|
||||
/// <summary>
|
||||
/// The display name of the template.
|
||||
/// </summary>
|
||||
public string DisplayName
|
||||
{
|
||||
get { return m_DisplayName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the description of the template.
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get { return m_Description; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The types of schemas in this template.
|
||||
/// </summary>
|
||||
/// <returns>The array of schema types.</returns>
|
||||
public Type[] GetTypes()
|
||||
{
|
||||
var types = new Type[m_SchemaTypes.Count];
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
types[i] = m_SchemaTypes[i].Value;
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a template with the specified name, description and schema types.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the template.</param>
|
||||
/// <param name="descr">The template description.</param>
|
||||
/// <param name="types">The schema types for the template.</param>
|
||||
/// <returns>The newly created schema template.</returns>
|
||||
public static AddressableAssetGroupSchemaTemplate Create(string name, string descr, params Type[] types)
|
||||
{
|
||||
var st = new AddressableAssetGroupSchemaTemplate {m_DisplayName = name, m_Description = descr};
|
||||
st.m_SchemaTypes = new List<SerializedType>(types.Length);
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
st.m_SchemaTypes.Add(new SerializedType {Value = types[i]});
|
||||
return st;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b5aaf4e4b41e6a447935d02bf76b2aa3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Presets;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to create template groups to make it easier for the user to create new groups.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "AddressableAssetGroupTemplate.asset", menuName = "Addressables/Group Templates/Blank Group Template")]
|
||||
public class AddressableAssetGroupTemplate : ScriptableObject, IGroupTemplate
|
||||
{
|
||||
[SerializeField]
|
||||
private List<AddressableAssetGroupSchema> m_SchemaObjects = new List<AddressableAssetGroupSchema>();
|
||||
|
||||
[SerializeField]
|
||||
private string m_Description;
|
||||
|
||||
[SerializeField]
|
||||
private AddressableAssetSettings m_Settings;
|
||||
|
||||
internal AddressableAssetSettings Settings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Settings == null)
|
||||
m_Settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
|
||||
return m_Settings;
|
||||
}
|
||||
set { m_Settings = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of Preset objects for AddressableAssetGroupSchema associated with this template
|
||||
/// </summary>
|
||||
internal List<Preset> SchemaPresetObjects
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Preset> m_SchemaPresetObjects = new List<Preset>(m_SchemaObjects.Count);
|
||||
foreach (AddressableAssetGroupSchema schemaObject in m_SchemaObjects)
|
||||
m_SchemaPresetObjects.Add(new Preset(schemaObject));
|
||||
return m_SchemaPresetObjects;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of Preset objects of AddressableAssetGroupSchema associated with this template
|
||||
/// </summary>
|
||||
public List<AddressableAssetGroupSchema> SchemaObjects
|
||||
{
|
||||
get { return m_SchemaObjects; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the AddressableAssetGroupTemplate
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The description of the AddressableAssetGroupTemplate
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get { return m_Description; }
|
||||
set { m_Description = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the types of the AddressableAssetGroupSchema associated with this template
|
||||
/// </summary>
|
||||
/// <returns>AddressableAssetGroupSchema types for schema on this template</returns>
|
||||
public Type[] GetTypes()
|
||||
{
|
||||
var types = new Type[m_SchemaObjects.Count];
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
types[i] = m_SchemaObjects[i].GetType();
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies schema values for the group to the schema values found in the template
|
||||
/// </summary>
|
||||
/// <param name="group">The AddressableAssetGroup to apply the schema settings to</param>
|
||||
public void ApplyToAddressableAssetGroup(AddressableAssetGroup group)
|
||||
{
|
||||
foreach (AddressableAssetGroupSchema schema in group.Schemas)
|
||||
{
|
||||
List<Preset> presets = SchemaPresetObjects;
|
||||
foreach (Preset p in presets)
|
||||
{
|
||||
Assert.IsNotNull(p);
|
||||
if (p.CanBeAppliedTo(schema))
|
||||
{
|
||||
p.ApplyTo(schema);
|
||||
schema.Group = group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the AddressableAssetGroupSchema of type to the template.
|
||||
/// </summary>
|
||||
/// <param name="type">The Type for the AddressableAssetGroupSchema to add to this template.</param>
|
||||
/// <param name="postEvent">If true, the event is propagated to callbacks.</param>
|
||||
/// <returns>If true, the type was added successfully.</returns>
|
||||
public bool AddSchema(Type type, bool postEvent = true)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
Debug.LogWarning("Cannot remove schema with null type.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!typeof(AddressableAssetGroupSchema).IsAssignableFrom(type))
|
||||
{
|
||||
Debug.LogWarningFormat("Invalid Schema type {0}. Schemas must inherit from AddressableAssetGroupSchema.", type.FullName);
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (AddressableAssetGroupSchema schemaObject in m_SchemaObjects)
|
||||
{
|
||||
if (schemaObject.GetType() == type)
|
||||
{
|
||||
Debug.LogError("Scheme of type " + type + " already exists");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AddressableAssetGroupSchema schemaInstance = (AddressableAssetGroupSchema)CreateInstance(type);
|
||||
if (schemaInstance != null)
|
||||
{
|
||||
schemaInstance.name = type.Name;
|
||||
try
|
||||
{
|
||||
schemaInstance.hideFlags |= HideFlags.HideInHierarchy;
|
||||
AssetDatabase.AddObjectToAsset(schemaInstance, this);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
|
||||
m_SchemaObjects.Add(schemaInstance);
|
||||
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.GroupTemplateSchemaAdded, this, postEvent);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
return schemaInstance != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the AddressableAssetGroupSchema of the type from the template.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of AddressableAssetGroupSchema to be removed.</param>
|
||||
/// <param name="postEvent">If true, the event is propagated to callbacks.</param>
|
||||
/// <returns>If true, the type was removed successfully.</returns>
|
||||
public bool RemoveSchema(Type type, bool postEvent = true)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
Debug.LogWarning("Cannot remove schema with null type.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!typeof(AddressableAssetGroupSchema).IsAssignableFrom(type))
|
||||
{
|
||||
Debug.LogWarningFormat("Invalid Schema type {0}. Schemas must inherit from AddressableAssetGroupSchema.", type.FullName);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_SchemaObjects.Count; ++i)
|
||||
{
|
||||
if (m_SchemaObjects[i].GetType() == type)
|
||||
return RemoveSchema(i, postEvent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the Schema at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the object to be removed.</param>
|
||||
/// <param name="postEvent">If true, the event is propagated to callbacks.</param>
|
||||
/// <returns>If true, the type was removed successfully.</returns>
|
||||
internal bool RemoveSchema(int index, bool postEvent = true)
|
||||
{
|
||||
if (index == -1)
|
||||
return false;
|
||||
|
||||
AssetDatabase.RemoveObjectFromAsset(m_SchemaObjects[index]);
|
||||
DestroyImmediate(m_SchemaObjects[index]);
|
||||
m_SchemaObjects.RemoveAt(index);
|
||||
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.GroupTemplateSchemaRemoved, this, postEvent);
|
||||
AssetDatabase.SaveAssets();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the object as modified.
|
||||
/// </summary>
|
||||
/// <param name="modificationEvent">The event type that is changed.</param>
|
||||
/// <param name="eventData">The object data that corresponds to the event.</param>
|
||||
/// <param name="postEvent">If true, the event is propagated to callbacks.</param>
|
||||
public void SetDirty(AddressableAssetSettings.ModificationEvent modificationEvent, object eventData, bool postEvent)
|
||||
{
|
||||
if (Settings != null)
|
||||
{
|
||||
if (Settings.IsPersisted && this != null)
|
||||
{
|
||||
EditorUtility.SetDirty(this);
|
||||
AddressableAssetUtility.OpenAssetIfUsingVCIntegration(this);
|
||||
}
|
||||
|
||||
Settings.SetDirty(modificationEvent, eventData, postEvent, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the group contains a schema of a given type.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <returns>True if the schema type or subclass has been added to this group.</returns>
|
||||
public bool HasSchema(Type type)
|
||||
{
|
||||
return GetSchemaByType(type) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an added schema of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <returns>The schema if found, otherwise null.</returns>
|
||||
public AddressableAssetGroupSchema GetSchemaByType(Type type)
|
||||
{
|
||||
foreach (var schema in m_SchemaObjects)
|
||||
{
|
||||
if (schema.GetType() == type)
|
||||
{
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of a schema based on its specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The schema type.</param>
|
||||
/// <returns>Valid index if found, otherwise returns -1.</returns>
|
||||
public int FindSchema(Type type)
|
||||
{
|
||||
for (int i = 0; i < m_SchemaObjects.Count; i++)
|
||||
{
|
||||
if (m_SchemaObjects[i].GetType() == type)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1a3c5d64ac83548c09dd1678b9f6f1cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores information about a group template.
|
||||
/// </summary>
|
||||
public interface IGroupTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the group, used for the menu and newly created group name.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Description of the Template, to be used as a tooltip
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5ad5fa919de6248649b4c095f3809802
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
internal class AddressablesAssetPostProcessor : AssetPostprocessor
|
||||
{
|
||||
private static AddressableAssetUtility.SortedDelegate<string[], string[], string[], string[]> s_OnPostProcessHandler =
|
||||
new AddressableAssetUtility.SortedDelegate<string[], string[], string[], string[]>();
|
||||
|
||||
public static AddressableAssetUtility.SortedDelegate<string[], string[], string[], string[]> OnPostProcess => s_OnPostProcessHandler;
|
||||
|
||||
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
{
|
||||
if (s_OnPostProcessHandler != null)
|
||||
{
|
||||
s_OnPostProcessHandler.Invoke(importedAssets, deletedAssets, movedAssets, movedFromAssetPaths);
|
||||
}
|
||||
else if (AddressableAssetSettingsDefaultObject.SettingsExists)
|
||||
{
|
||||
s_OnPostProcessHandler.TryInvokeOrDelayToReady(importedAssets, deletedAssets, movedAssets, movedFromAssetPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f88726da597753f45b7ea82733006421
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,895 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.AddressableAssets.GUI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.AddressableAssets.Initialization;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
// ReSharper disable DelegateSubtraction
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains user defined variables to control build parameters.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class AddressableAssetProfileSettings
|
||||
{
|
||||
internal delegate string ProfileStringEvaluationDelegate(string key);
|
||||
|
||||
[NonSerialized]
|
||||
internal ProfileStringEvaluationDelegate onProfileStringEvaluation;
|
||||
|
||||
internal void RegisterProfileStringEvaluationFunc(ProfileStringEvaluationDelegate f)
|
||||
{
|
||||
onProfileStringEvaluation -= f;
|
||||
onProfileStringEvaluation += f;
|
||||
}
|
||||
|
||||
internal void UnregisterProfileStringEvaluationFunc(ProfileStringEvaluationDelegate f)
|
||||
{
|
||||
onProfileStringEvaluation -= f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class BuildProfile
|
||||
{
|
||||
[Serializable]
|
||||
internal class ProfileEntry
|
||||
{
|
||||
[FormerlySerializedAs("m_id")]
|
||||
[SerializeField]
|
||||
string m_Id;
|
||||
|
||||
public string id
|
||||
{
|
||||
get { return m_Id; }
|
||||
set { m_Id = value; m_CurrentHash = default; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_value")]
|
||||
[SerializeField]
|
||||
string m_Value;
|
||||
|
||||
Hash128 m_CurrentHash;
|
||||
internal Hash128 currentHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_CurrentHash.isValid)
|
||||
{
|
||||
m_CurrentHash.Append(m_Id);
|
||||
m_CurrentHash.Append(m_Value);
|
||||
}
|
||||
return m_CurrentHash;
|
||||
}
|
||||
}
|
||||
|
||||
public string value
|
||||
{
|
||||
get { return m_Value; }
|
||||
set { m_Value = value; m_CurrentHash = default; }
|
||||
}
|
||||
|
||||
internal ProfileEntry()
|
||||
{
|
||||
}
|
||||
|
||||
internal ProfileEntry(string id, string v)
|
||||
{
|
||||
m_Id = id;
|
||||
m_Value = v;
|
||||
}
|
||||
|
||||
internal ProfileEntry(ProfileEntry copy)
|
||||
{
|
||||
m_Id = copy.m_Id;
|
||||
m_Value = copy.m_Value;
|
||||
}
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
internal AddressableAssetProfileSettings m_ProfileParent;
|
||||
|
||||
[FormerlySerializedAs("m_inheritedParent")]
|
||||
[SerializeField]
|
||||
string m_InheritedParent;
|
||||
|
||||
public string inheritedParent
|
||||
{
|
||||
get { return m_InheritedParent; }
|
||||
set { m_InheritedParent = value; m_CurrentHash = default; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_id")]
|
||||
[SerializeField]
|
||||
string m_Id;
|
||||
|
||||
internal string id
|
||||
{
|
||||
get { return m_Id; }
|
||||
set { m_Id = value; m_CurrentHash = default; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_profileName")]
|
||||
[SerializeField]
|
||||
string m_ProfileName;
|
||||
|
||||
internal string profileName
|
||||
{
|
||||
get { return m_ProfileName; }
|
||||
set { m_ProfileName = value; m_CurrentHash = default; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_values")]
|
||||
[SerializeField]
|
||||
List<ProfileEntry> m_Values = new List<ProfileEntry>();
|
||||
|
||||
|
||||
Hash128 m_CurrentHash;
|
||||
internal Hash128 currentHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_CurrentHash.isValid)
|
||||
{
|
||||
m_CurrentHash.Append(m_Id);
|
||||
m_CurrentHash.Append(m_ProfileName);
|
||||
m_CurrentHash.Append(m_InheritedParent);
|
||||
foreach (var e in m_Values)
|
||||
{
|
||||
var peh = e.currentHash;
|
||||
m_CurrentHash.Append(ref peh);
|
||||
}
|
||||
}
|
||||
return m_CurrentHash;
|
||||
}
|
||||
}
|
||||
|
||||
internal List<ProfileEntry> values
|
||||
{
|
||||
get { return m_Values; }
|
||||
set { m_Values = value; m_CurrentHash = default; }
|
||||
}
|
||||
|
||||
internal BuildProfile(string name, BuildProfile copyFrom, AddressableAssetProfileSettings ps)
|
||||
{
|
||||
m_InheritedParent = null;
|
||||
id = GUID.Generate().ToString();
|
||||
profileName = name;
|
||||
values.Clear();
|
||||
m_ProfileParent = ps;
|
||||
|
||||
if (copyFrom != null)
|
||||
{
|
||||
foreach (var v in copyFrom.values)
|
||||
values.Add(new ProfileEntry(v));
|
||||
m_InheritedParent = copyFrom.m_InheritedParent;
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnAfterDeserialize(AddressableAssetProfileSettings ps)
|
||||
{
|
||||
m_ProfileParent = ps;
|
||||
}
|
||||
|
||||
internal string GetValueById(string variableId)
|
||||
{
|
||||
var i = values.FindIndex(v => v.id == variableId);
|
||||
if (i >= 0)
|
||||
return values[i].value;
|
||||
|
||||
|
||||
if (m_ProfileParent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_ProfileParent.GetValueById(m_InheritedParent, variableId);
|
||||
}
|
||||
|
||||
internal void SetValueById(string variableId, string val)
|
||||
{
|
||||
var i = values.FindIndex(v => v.id == variableId);
|
||||
if (i >= 0)
|
||||
values[i].value = val;
|
||||
m_CurrentHash = default;
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnAfterDeserialize(AddressableAssetSettings settings)
|
||||
{
|
||||
m_Settings = settings;
|
||||
foreach (var prof in m_Profiles)
|
||||
{
|
||||
prof.OnAfterDeserialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
[NonSerialized]
|
||||
AddressableAssetSettings m_Settings;
|
||||
|
||||
[FormerlySerializedAs("m_profiles")]
|
||||
[SerializeField]
|
||||
List<BuildProfile> m_Profiles = new List<BuildProfile>();
|
||||
|
||||
internal List<BuildProfile> profiles
|
||||
{
|
||||
get { return m_Profiles; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A container for profile specific data, such as the name and ID of a profile.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ProfileIdData
|
||||
{
|
||||
[FormerlySerializedAs("m_id")]
|
||||
[SerializeField]
|
||||
string m_Id;
|
||||
|
||||
/// <summary>
|
||||
/// The unique ID set to identify a specific profile.
|
||||
/// </summary>
|
||||
public string Id
|
||||
{
|
||||
get { return m_Id; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_name")]
|
||||
[SerializeField]
|
||||
string m_Name;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the specific profile.
|
||||
/// </summary>
|
||||
public string ProfileName
|
||||
{
|
||||
get { return m_Name; }
|
||||
}
|
||||
|
||||
Hash128 m_CurrentHash;
|
||||
internal Hash128 currentHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_CurrentHash.isValid)
|
||||
{
|
||||
m_CurrentHash.Append(m_Id);
|
||||
m_CurrentHash.Append(m_Name);
|
||||
m_CurrentHash.Append(ref m_InlineUsage);
|
||||
}
|
||||
return m_CurrentHash;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Changes the name of a given profile and updates the values in the profile settings.
|
||||
/// </summary>
|
||||
/// <param name="newName">The new name you want to set the profile to.</param>
|
||||
/// <param name="profileSettings">The profile settings object that contains this profile.</param>
|
||||
public void SetName(string newName, AddressableAssetProfileSettings profileSettings)
|
||||
{
|
||||
if (!profileSettings.ValidateNewVariableName(newName))
|
||||
return;
|
||||
|
||||
var currRefStr = "[" + m_Name + "]";
|
||||
var newRefStr = "[" + newName + "]";
|
||||
|
||||
m_Name = newName;
|
||||
|
||||
foreach (var p in profileSettings.profiles)
|
||||
{
|
||||
foreach (var v in p.values)
|
||||
v.value = v.value.Replace(currRefStr, newRefStr);
|
||||
}
|
||||
m_CurrentHash = default;
|
||||
profileSettings.SetDirty(AddressableAssetSettings.ModificationEvent.ProfileModified, null, false);
|
||||
ProfileWindow.MarkForReload();
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_inlineUsage")]
|
||||
[SerializeField]
|
||||
bool m_InlineUsage;
|
||||
|
||||
internal bool InlineUsage
|
||||
{
|
||||
get { return m_InlineUsage; }
|
||||
}
|
||||
|
||||
internal ProfileIdData()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new ProfileIdData.
|
||||
/// </summary>
|
||||
/// <param name="entryId">The unique ID for this ProfileIdData</param>
|
||||
/// <param name="entryName">The name of the ProfileIdData. ProfileIdData names should be unique in a given AddressableAssetProfileSettings.</param>
|
||||
/// <param name="inline">False by default, this informs the BuildProifleSettingsEditor on if it should evaluate the ProfileIdData directly (true)
|
||||
/// or get the value by Id first before evaluation (false).</param>
|
||||
public ProfileIdData(string entryId, string entryName, bool inline)
|
||||
{
|
||||
m_Id = entryId;
|
||||
m_Name = entryName;
|
||||
m_InlineUsage = inline;
|
||||
}
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_profileEntryNames")]
|
||||
[SerializeField]
|
||||
List<ProfileIdData> m_ProfileEntryNames = new List<ProfileIdData>();
|
||||
|
||||
[FormerlySerializedAs("m_profileVersion")]
|
||||
[SerializeField]
|
||||
internal int m_ProfileVersion;
|
||||
|
||||
const int k_CurrentProfileVersion = 1;
|
||||
|
||||
|
||||
Hash128 m_CurrentHash;
|
||||
internal Hash128 currentHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_CurrentHash.isValid)
|
||||
{
|
||||
m_CurrentHash.Append(m_ProfileVersion);
|
||||
foreach (var p in m_ProfileEntryNames)
|
||||
{
|
||||
var peh = p.currentHash;
|
||||
m_CurrentHash.Append(ref peh);
|
||||
}
|
||||
foreach (var p in m_Profiles)
|
||||
{
|
||||
var peh = p.currentHash;
|
||||
m_CurrentHash.Append(ref peh);
|
||||
}
|
||||
}
|
||||
return m_CurrentHash;
|
||||
}
|
||||
}
|
||||
|
||||
internal List<ProfileIdData> profileEntryNames
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ProfileVersion < k_CurrentProfileVersion)
|
||||
{
|
||||
m_ProfileVersion = k_CurrentProfileVersion;
|
||||
//migration cleanup from old way of doing "custom" values...
|
||||
var removeId = string.Empty;
|
||||
foreach (var idPair in m_ProfileEntryNames)
|
||||
{
|
||||
if (idPair.ProfileName == customEntryString)
|
||||
{
|
||||
removeId = idPair.Id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(removeId))
|
||||
RemoveValue(removeId);
|
||||
}
|
||||
|
||||
|
||||
return m_ProfileEntryNames;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Text that represents a custom profile entry.
|
||||
/// </summary>
|
||||
public const string customEntryString = "<custom>";
|
||||
|
||||
/// <summary>
|
||||
/// Text that represents when the default settings path is being used.
|
||||
/// </summary>
|
||||
public const string defaultSettingsPathString = "<default settings path>";
|
||||
|
||||
/// <summary>
|
||||
/// Text that represents an undefined profile entry.
|
||||
/// </summary>
|
||||
public const string undefinedEntryValue = "<undefined>";
|
||||
|
||||
const string k_RootProfileName = "Default";
|
||||
|
||||
/// <summary>
|
||||
/// Get the profile specific data for a given profile id.
|
||||
/// </summary>
|
||||
/// <param name="id">The profile id you're requesting data for.</param>
|
||||
/// <returns>A ProfileIdData with information about a specific profile.</returns>
|
||||
public ProfileIdData GetProfileDataById(string id)
|
||||
{
|
||||
return profileEntryNames.Find(p => p.Id == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the profile specific data for a given profile name.
|
||||
/// </summary>
|
||||
/// <param name="name">The profile name you're requesting data for.</param>
|
||||
/// <returns>A ProfileIdData with information about a specific profile.</returns>
|
||||
public ProfileIdData GetProfileDataByName(string name)
|
||||
{
|
||||
return profileEntryNames.Find(p => p.ProfileName == name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears out the list of profiles, then creates a new default one.
|
||||
/// </summary>
|
||||
/// <returns>Returns the ID of the newly created default profile.</returns>
|
||||
public string Reset()
|
||||
{
|
||||
m_Profiles = new List<BuildProfile>();
|
||||
m_CurrentHash = default;
|
||||
return CreateDefaultProfile();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate a string given a profile id.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile id to use for evaluation.</param>
|
||||
/// <param name="varString">The string to evaluate. Any tokens surrounded by '[' and ']' will be replaced with matching variables.</param>
|
||||
/// <returns>The evaluated string.</returns>
|
||||
public string EvaluateString(string profileId, string varString)
|
||||
{
|
||||
Func<string, string> getVal = s =>
|
||||
{
|
||||
var v = GetValueByName(profileId, s);
|
||||
if (string.IsNullOrEmpty(v))
|
||||
{
|
||||
if (onProfileStringEvaluation != null)
|
||||
{
|
||||
foreach (var i in onProfileStringEvaluation.GetInvocationList())
|
||||
{
|
||||
var del = (ProfileStringEvaluationDelegate)i;
|
||||
v = del(s);
|
||||
if (!string.IsNullOrEmpty(v))
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
v = AddressablesRuntimeProperties.EvaluateProperty(s);
|
||||
}
|
||||
|
||||
return v;
|
||||
};
|
||||
|
||||
return AddressablesRuntimeProperties.EvaluateString(varString, '[', ']', getVal);
|
||||
}
|
||||
|
||||
internal void Validate(AddressableAssetSettings addressableAssetSettings)
|
||||
{
|
||||
CreateDefaultProfile();
|
||||
}
|
||||
|
||||
internal string CreateDefaultProfile()
|
||||
{
|
||||
if (!ValidateProfiles())
|
||||
{
|
||||
m_ProfileEntryNames.Clear();
|
||||
m_Profiles.Clear();
|
||||
|
||||
AddProfile(k_RootProfileName, null);
|
||||
CreateValue("BuildTarget", "[UnityEditor.EditorUserBuildSettings.activeBuildTarget]");
|
||||
CreateValue(AddressableAssetSettings.kLocalBuildPath, AddressableAssetSettings.kLocalBuildPathValue);
|
||||
CreateValue(AddressableAssetSettings.kLocalLoadPath, AddressableAssetSettings.kLocalLoadPathValue);
|
||||
CreateValue(AddressableAssetSettings.kRemoteBuildPath, AddressableAssetSettings.kRemoteBuildPathValue);
|
||||
CreateValue(AddressableAssetSettings.kRemoteLoadPath, AddressableAssetSettings.RemoteLoadPathValue);
|
||||
}
|
||||
|
||||
return GetDefaultProfileId();
|
||||
}
|
||||
|
||||
string GetDefaultProfileId()
|
||||
{
|
||||
var def = GetDefaultProfile();
|
||||
if (def != null)
|
||||
return def.id;
|
||||
return null;
|
||||
}
|
||||
|
||||
BuildProfile GetDefaultProfile()
|
||||
{
|
||||
BuildProfile profile = null;
|
||||
if (m_Profiles.Count > 0)
|
||||
profile = m_Profiles[0];
|
||||
return profile;
|
||||
}
|
||||
|
||||
bool ValidateProfiles()
|
||||
{
|
||||
if (m_Profiles.Count == 0)
|
||||
return false;
|
||||
|
||||
var root = m_Profiles[0];
|
||||
if (root == null || root.values == null)
|
||||
return false;
|
||||
|
||||
foreach (var i in profileEntryNames)
|
||||
if (string.IsNullOrEmpty(i.Id) || string.IsNullOrEmpty(i.ProfileName))
|
||||
return false;
|
||||
|
||||
var rootValueCount = root.values.Count;
|
||||
for (int index = 1; index < m_Profiles.Count; index++)
|
||||
{
|
||||
var profile = m_Profiles[index];
|
||||
|
||||
if (profile == null || string.IsNullOrEmpty(profile.profileName))
|
||||
return false;
|
||||
|
||||
if (profile.values == null || profile.values.Count != rootValueCount)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < rootValueCount; i++)
|
||||
{
|
||||
if (root.values[i].id != profile.values[i].id)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all available variable names
|
||||
/// </summary>
|
||||
/// <returns>The variable names, sorted alphabetically.</returns>
|
||||
public List<string> GetVariableNames()
|
||||
{
|
||||
HashSet<string> names = new HashSet<string>();
|
||||
foreach (var entry in profileEntryNames)
|
||||
names.Add(entry.ProfileName);
|
||||
var list = names.ToList();
|
||||
list.Sort();
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all profile names.
|
||||
/// </summary>
|
||||
/// <returns>The list of profile names.</returns>
|
||||
public List<string> GetAllProfileNames()
|
||||
{
|
||||
CreateDefaultProfile();
|
||||
List<string> result = new List<string>();
|
||||
foreach (var p in m_Profiles)
|
||||
result.Add(p.profileName);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a profile's display name.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile id.</param>
|
||||
/// <returns>The display name of the profile. Returns empty string if not found.</returns>
|
||||
public string GetProfileName(string profileId)
|
||||
{
|
||||
foreach (var p in m_Profiles)
|
||||
{
|
||||
if (p.id == profileId)
|
||||
return p.profileName;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the id of a given display name.
|
||||
/// </summary>
|
||||
/// <param name="profileName">The profile name.</param>
|
||||
/// <returns>The id of the profile. Returns empty string if not found.</returns>
|
||||
public string GetProfileId(string profileName)
|
||||
{
|
||||
foreach (var p in m_Profiles)
|
||||
{
|
||||
if (p.profileName == profileName)
|
||||
return p.id;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of all profile ids.
|
||||
/// </summary>
|
||||
/// <returns>The set of profile ids.</returns>
|
||||
public HashSet<string> GetAllVariableIds()
|
||||
{
|
||||
HashSet<string> ids = new HashSet<string>();
|
||||
foreach (var v in profileEntryNames)
|
||||
ids.Add(v.Id);
|
||||
return ids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the object as modified.
|
||||
/// </summary>
|
||||
/// <param name="modificationEvent">The event type that is changed.</param>
|
||||
/// <param name="eventData">The object data that corresponds to the event.</param>
|
||||
/// <param name="postEvent">If true, the event is propagated to callbacks.</param>
|
||||
public void SetDirty(AddressableAssetSettings.ModificationEvent modificationEvent, object eventData, bool postEvent)
|
||||
{
|
||||
m_CurrentHash = default;
|
||||
if (m_Settings != null)
|
||||
m_Settings.SetDirty(modificationEvent, eventData, postEvent, true);
|
||||
}
|
||||
|
||||
internal bool ValidateNewVariableName(string name)
|
||||
{
|
||||
foreach (var idPair in profileEntryNames)
|
||||
if (idPair.ProfileName == name)
|
||||
return false;
|
||||
return !string.IsNullOrEmpty(name) && !name.Any(c => { return c == '[' || c == ']' || c == '{' || c == '}'; });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new profile.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the new profile.</param>
|
||||
/// <param name="copyFromId">The id of the profile to copy values from.</param>
|
||||
/// <returns>The id of the created profile.</returns>
|
||||
public string AddProfile(string name, string copyFromId)
|
||||
{
|
||||
var existingProfile = GetProfileByName(name);
|
||||
if (existingProfile != null)
|
||||
return existingProfile.id;
|
||||
var copyRoot = GetProfile(copyFromId);
|
||||
if (copyRoot == null && m_Profiles.Count > 0)
|
||||
copyRoot = GetDefaultProfile();
|
||||
var prof = new BuildProfile(name, copyRoot, this);
|
||||
m_Profiles.Add(prof);
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.ProfileAdded, prof, true);
|
||||
ProfileWindow.MarkForReload();
|
||||
return prof.id;
|
||||
}
|
||||
|
||||
// Allows passing in the profile directly for internal methods, makes the profile window's code a bit cleaner
|
||||
// Can't be public since BuildProfile is an internal class
|
||||
internal bool RenameProfile(BuildProfile profile, string newName)
|
||||
{
|
||||
if (profile == null)
|
||||
{
|
||||
Addressables.LogError("Profile rename failed because profile passed in is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile == GetDefaultProfile())
|
||||
{
|
||||
Addressables.LogError("Profile rename failed because default profile cannot be renamed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile.profileName == newName) return false;
|
||||
|
||||
// new name cannot only contain spaces
|
||||
if (newName.Trim().Length == 0)
|
||||
{
|
||||
Addressables.LogError("Profile rename failed because new profile name must not be only spaces.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool profileExistsInSettingsList = false;
|
||||
|
||||
for (int i = 0; i < m_Profiles.Count; i++)
|
||||
{
|
||||
// return false if there already exists a profile with the new name, no duplicates are allowed
|
||||
if (m_Profiles[i].profileName == newName)
|
||||
{
|
||||
Addressables.LogError("Profile rename failed because new profile name is not unique.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_Profiles[i].id == profile.id)
|
||||
{
|
||||
profileExistsInSettingsList = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Rename the profile
|
||||
profile.profileName = newName;
|
||||
|
||||
if (profileExistsInSettingsList)
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.ProfileModified, profile, true);
|
||||
|
||||
ProfileWindow.MarkForReload();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a profile. profileId must refer to an existing profile. Profile names must be unique and must not be comprised of only whitespace.
|
||||
/// Returns false if profileId or newName is invalid.
|
||||
/// </summary>
|
||||
/// <param name="profileId"> The id of the profile to be renamed. </param>
|
||||
/// <param name="newName"> The new name to be given to the profile. </param>
|
||||
/// <returns> True if the rename is successful, false otherwise. </returns>
|
||||
public bool RenameProfile(string profileId, string newName)
|
||||
{
|
||||
var profileToRename = GetProfile(profileId);
|
||||
|
||||
if (profileToRename == null)
|
||||
{
|
||||
Addressables.LogError("Profile rename failed because profile with sought id does not exist.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return RenameProfile(profileToRename, newName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a profile.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The id of the profile to remove.</param>
|
||||
public void RemoveProfile(string profileId)
|
||||
{
|
||||
m_Profiles.RemoveAll(p => p.id == profileId);
|
||||
m_Profiles.ForEach(p =>
|
||||
{
|
||||
if (p.inheritedParent == profileId) p.inheritedParent = null;
|
||||
});
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.ProfileRemoved, profileId, true);
|
||||
ProfileWindow.MarkForReload();
|
||||
}
|
||||
|
||||
BuildProfile GetProfileByName(string profileName)
|
||||
{
|
||||
return m_Profiles.Find(p => p.profileName == profileName);
|
||||
}
|
||||
|
||||
internal string GetUniqueProfileName(string name)
|
||||
{
|
||||
return GenerateUniqueName(name, m_Profiles.Select(p => p.profileName));
|
||||
}
|
||||
|
||||
internal BuildProfile GetProfile(string profileId)
|
||||
{
|
||||
return m_Profiles.Find(p => p.id == profileId);
|
||||
}
|
||||
|
||||
internal string GetVariableId(string variableName)
|
||||
{
|
||||
foreach (var idPair in profileEntryNames)
|
||||
{
|
||||
if (idPair.ProfileName == variableName)
|
||||
return idPair.Id;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of a variable for a specified profile.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile id.</param>
|
||||
/// <param name="variableName">The property name.</param>
|
||||
/// <param name="val">The value to set the property.</param>
|
||||
public void SetValue(string profileId, string variableName, string val)
|
||||
{
|
||||
var profile = GetProfile(profileId);
|
||||
if (profile == null)
|
||||
{
|
||||
Addressables.LogError("setting variable " + variableName + " failed because profile " + profileId + " does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
var id = GetVariableId(variableName);
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
Addressables.LogError("setting variable " + variableName + " failed because variable does not yet exist. Call CreateValue() first.");
|
||||
return;
|
||||
}
|
||||
|
||||
profile.SetValueById(id, val);
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.ProfileModified, profile, true);
|
||||
ProfileWindow.MarkForReload();
|
||||
}
|
||||
|
||||
internal string GetUniqueProfileEntryName(string name)
|
||||
{
|
||||
return GenerateUniqueName(name, profileEntryNames.Select(p => p.ProfileName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new profile property.
|
||||
/// </summary>
|
||||
/// <param name="variableName">The name of the property.</param>
|
||||
/// <param name="defaultValue">The default value.</param>
|
||||
/// <returns>The id of the created variable.</returns>
|
||||
public string CreateValue(string variableName, string defaultValue)
|
||||
{
|
||||
return CreateValue(variableName, defaultValue, false);
|
||||
}
|
||||
|
||||
internal string CreateValue(string variableName, string defaultValue, bool inline)
|
||||
{
|
||||
if (m_Profiles.Count == 0)
|
||||
{
|
||||
Addressables.LogError("Attempting to add a profile variable in Addressables, but there are no profiles yet.");
|
||||
}
|
||||
|
||||
var id = GetVariableId(variableName);
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = GUID.Generate().ToString();
|
||||
profileEntryNames.Add(new ProfileIdData(id, variableName, inline));
|
||||
|
||||
foreach (var pro in m_Profiles)
|
||||
{
|
||||
pro.values.Add(new BuildProfile.ProfileEntry(id, defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.ProfileModified, null, true);
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a profile property.
|
||||
/// </summary>
|
||||
/// <param name="variableId">The id of the property.</param>
|
||||
public void RemoveValue(string variableId)
|
||||
{
|
||||
foreach (var pro in m_Profiles)
|
||||
{
|
||||
pro.values.RemoveAll(x => x.id == variableId);
|
||||
}
|
||||
|
||||
m_ProfileEntryNames.RemoveAll(x => x.Id == variableId);
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.ProfileModified, null, false);
|
||||
ProfileWindow.MarkForReload();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a property.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile id.</param>
|
||||
/// <param name="varId">The property id.</param>
|
||||
/// <returns></returns>
|
||||
public string GetValueById(string profileId, string varId)
|
||||
{
|
||||
BuildProfile profile = GetProfile(profileId);
|
||||
return profile == null ? varId : profile.GetValueById(varId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a property by name.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile id.</param>
|
||||
/// <param name="varName">The variable name.</param>
|
||||
/// <returns></returns>
|
||||
public string GetValueByName(string profileId, string varName)
|
||||
{
|
||||
return GetValueById(profileId, GetVariableId(varName));
|
||||
}
|
||||
|
||||
internal static string GenerateUniqueName(string baseName, IEnumerable<string> enumerable)
|
||||
{
|
||||
var set = new HashSet<string>(enumerable);
|
||||
int counter = 1;
|
||||
var newName = baseName;
|
||||
while (set.Contains(newName))
|
||||
{
|
||||
newName = baseName + counter;
|
||||
counter++;
|
||||
if (counter == int.MaxValue)
|
||||
throw new OverflowException();
|
||||
}
|
||||
|
||||
return newName;
|
||||
}
|
||||
|
||||
internal void CreateDuplicateVariableWithNewName(AddressableAssetSettings addressableAssetSettings, string newVariableName, string variableNameToCopyFrom)
|
||||
{
|
||||
var activeProfileId = addressableAssetSettings.activeProfileId;
|
||||
string newVarId = CreateValue(newVariableName, GetValueByName(activeProfileId, variableNameToCopyFrom));
|
||||
string oldVarId = GetVariableId(variableNameToCopyFrom);
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
profile.SetValueById(newVarId, profile.GetValueById(oldVarId));
|
||||
SetDirty(AddressableAssetSettings.ModificationEvent.ProfileModified, profile, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c49e24ebadbf2c64cb350d3f62c0ad14
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 468a46d0ae32c3544b7d98094e6448a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,760 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
|
||||
using UnityEditor.Build.Utilities;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEditor.PackageManager.Requests;
|
||||
using UnityEditor.VersionControl;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
internal static class AddressableAssetUtility
|
||||
{
|
||||
#if !UNITY_2020_3_OR_NEWER
|
||||
//these extention methods are needed prior to 2020.3 since they are not available
|
||||
public static void Append(this ref Hash128 thisHash, string val)
|
||||
{
|
||||
Hash128 valHash = Hash128.Compute(val);
|
||||
HashUtilities.AppendHash(ref valHash, ref thisHash);
|
||||
}
|
||||
|
||||
public static void Append(this ref Hash128 thisHash, int val)
|
||||
{
|
||||
Hash128 valHash = default;
|
||||
HashUtilities.ComputeHash128(ref val, ref valHash);
|
||||
HashUtilities.AppendHash(ref valHash, ref thisHash);
|
||||
}
|
||||
|
||||
public static void Append(this ref Hash128 thisHash, Hash128[] vals)
|
||||
{
|
||||
Hash128 valHash = default;
|
||||
for (int i = 0; i < vals.Length; i++)
|
||||
{
|
||||
HashUtilities.ComputeHash128(ref vals[i], ref valHash);
|
||||
HashUtilities.AppendHash(ref valHash, ref thisHash);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Append<T>(this ref Hash128 thisHash, ref T val) where T : unmanaged
|
||||
{
|
||||
Hash128 valHash = default;
|
||||
HashUtilities.ComputeHash128(ref val, ref valHash);
|
||||
HashUtilities.AppendHash(ref valHash, ref thisHash);
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static bool IsInResources(string path)
|
||||
{
|
||||
#if NET_UNITY_4_8
|
||||
return path.Replace('\\', '/').Contains("/Resources/", StringComparison.OrdinalIgnoreCase);
|
||||
#else
|
||||
return path.Replace('\\', '/').ToLower().Contains("/resources/");
|
||||
#endif
|
||||
}
|
||||
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool StringContains(string input, string value, StringComparison comp)
|
||||
{
|
||||
#if NET_UNITY_4_8
|
||||
return input.Contains(value, comp);
|
||||
#else
|
||||
return input.Contains(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static bool TryGetPathAndGUIDFromTarget(Object target, out string path, out string guid)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
guid = string.Empty;
|
||||
path = string.Empty;
|
||||
return false;
|
||||
}
|
||||
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(target, out guid, out long id))
|
||||
{
|
||||
guid = string.Empty;
|
||||
path = string.Empty;
|
||||
return false;
|
||||
}
|
||||
path = AssetDatabase.GetAssetOrScenePath(target);
|
||||
if (!IsPathValidForEntry(path))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string isEditorFolder = $"{Path.DirectorySeparatorChar}Editor";
|
||||
private static string insideEditorFolder = $"{Path.DirectorySeparatorChar}Editor{Path.DirectorySeparatorChar}";
|
||||
static HashSet<string> excludedExtensions = new HashSet<string>(new string[] { ".cs", ".js", ".boo", ".exe", ".dll", ".meta", ".preset", ".asmdef" });
|
||||
internal static bool IsPathValidForEntry(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return false;
|
||||
|
||||
if (path.Contains('\\'))
|
||||
path = path.Replace('\\', Path.DirectorySeparatorChar);
|
||||
|
||||
if (Path.DirectorySeparatorChar != '/' && path.Contains('/'))
|
||||
path = path.Replace('/', Path.DirectorySeparatorChar);
|
||||
|
||||
if (!path.StartsWith("Assets", StringComparison.OrdinalIgnoreCase) && !IsPathValidPackageAsset(path))
|
||||
return false;
|
||||
|
||||
string ext = Path.GetExtension(path);
|
||||
if (string.IsNullOrEmpty(ext))
|
||||
{
|
||||
// is folder
|
||||
if (path == "Assets")
|
||||
return false;
|
||||
int editorIndex = path.IndexOf(isEditorFolder, StringComparison.OrdinalIgnoreCase);
|
||||
if (editorIndex != -1)
|
||||
{
|
||||
int length = path.Length;
|
||||
if (editorIndex == length - 7)
|
||||
return false;
|
||||
if (path[editorIndex + 7] == '/')
|
||||
return false;
|
||||
// Could still have something like Assets/editorthings/Editor/things, but less likely
|
||||
if (StringContains(path, insideEditorFolder, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
}
|
||||
if (String.Equals(path, CommonStrings.UnityEditorResourcePath, StringComparison.Ordinal) ||
|
||||
String.Equals(path, CommonStrings.UnityDefaultResourcePath, StringComparison.Ordinal) ||
|
||||
String.Equals(path, CommonStrings.UnityBuiltInExtraPath, StringComparison.Ordinal))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// asset type
|
||||
if (StringContains(path, insideEditorFolder, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
if (excludedExtensions.Contains(ext))
|
||||
return false;
|
||||
}
|
||||
|
||||
var settings = AddressableAssetSettingsDefaultObject.SettingsExists ? AddressableAssetSettingsDefaultObject.Settings : null;
|
||||
if (settings != null && path.StartsWith(settings.ConfigFolder, StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool IsPathValidPackageAsset(string pathLowerCase)
|
||||
{
|
||||
string[] splitPath = pathLowerCase.Split(Path.DirectorySeparatorChar);
|
||||
|
||||
if (splitPath.Length < 3)
|
||||
return false;
|
||||
if (!String.Equals(splitPath[0], "packages", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
if (String.Equals(splitPath[2], "package.json", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static HashSet<Type> validTypes = new HashSet<Type>();
|
||||
|
||||
internal static Type MapEditorTypeToRuntimeType(Type t, bool allowFolders)
|
||||
{
|
||||
//type is valid and already seen (most common)
|
||||
if (validTypes.Contains(t))
|
||||
return t;
|
||||
|
||||
//removes the need to check this outside of this call
|
||||
if (t == null)
|
||||
return t;
|
||||
|
||||
//check for editor type, this will get hit once for each new type encountered
|
||||
if (!t.Assembly.IsDefined(typeof(AssemblyIsEditorAssembly), true) && !Build.BuildUtility.IsEditorAssembly(t.Assembly))
|
||||
{
|
||||
validTypes.Add(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
if (t == typeof(DefaultAsset))
|
||||
return typeof(DefaultAsset);
|
||||
|
||||
//try to remap the editor type to a runtime type
|
||||
return MapEditorTypeToRuntimeTypeInternal(t);
|
||||
}
|
||||
|
||||
static Type MapEditorTypeToRuntimeTypeInternal(Type t)
|
||||
{
|
||||
if (t == typeof(UnityEditor.Animations.AnimatorController))
|
||||
return typeof(RuntimeAnimatorController);
|
||||
if (t == typeof(UnityEditor.SceneAsset))
|
||||
return typeof(UnityEngine.ResourceManagement.ResourceProviders.SceneInstance);
|
||||
if (t.FullName == "UnityEditor.Audio.AudioMixerController")
|
||||
return typeof(UnityEngine.Audio.AudioMixer);
|
||||
if (t.FullName == "UnityEditor.Audio.AudioMixerGroupController")
|
||||
return typeof(UnityEngine.Audio.AudioMixerGroup);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static void ConvertAssetBundlesToAddressables()
|
||||
{
|
||||
AssetDatabase.RemoveUnusedAssetBundleNames();
|
||||
var bundleList = AssetDatabase.GetAllAssetBundleNames();
|
||||
|
||||
float fullCount = bundleList.Length;
|
||||
int currCount = 0;
|
||||
|
||||
var settings = AddressableAssetSettingsDefaultObject.GetSettings(true);
|
||||
foreach (var bundle in bundleList)
|
||||
{
|
||||
if (EditorUtility.DisplayCancelableProgressBar("Converting Legacy Asset Bundles", bundle, currCount / fullCount))
|
||||
break;
|
||||
|
||||
currCount++;
|
||||
var group = settings.CreateGroup(bundle, false, false, false, null);
|
||||
var schema = group.AddSchema<BundledAssetGroupSchema>();
|
||||
schema.Validate();
|
||||
schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether;
|
||||
group.AddSchema<ContentUpdateGroupSchema>().StaticContent = true;
|
||||
|
||||
var assetList = AssetDatabase.GetAssetPathsFromAssetBundle(bundle);
|
||||
|
||||
foreach (var asset in assetList)
|
||||
{
|
||||
var guid = AssetDatabase.AssetPathToGUID(asset);
|
||||
settings.CreateOrMoveEntry(guid, group, false, false);
|
||||
var imp = AssetImporter.GetAtPath(asset);
|
||||
if (imp != null)
|
||||
imp.SetAssetBundleNameAndVariant(string.Empty, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
if (fullCount > 0)
|
||||
settings.SetDirty(AddressableAssetSettings.ModificationEvent.BatchModification, null, true, true);
|
||||
EditorUtility.ClearProgressBar();
|
||||
AssetDatabase.RemoveUnusedAssetBundleNames();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all types that can be assigned to type T
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The class type to use as the base class or interface for all found types.</typeparam>
|
||||
/// <returns>A list of types that are assignable to type T. The results are cached.</returns>
|
||||
public static List<Type> GetTypes<T>()
|
||||
{
|
||||
return TypeManager<T>.Types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all types that can be assigned to type rootType.
|
||||
/// </summary>
|
||||
/// <param name="rootType">The class type to use as the base class or interface for all found types.</param>
|
||||
/// <returns>A list of types that are assignable to type T. The results are not cached.</returns>
|
||||
public static List<Type> GetTypes(Type rootType)
|
||||
{
|
||||
return TypeManager.GetManagerTypes(rootType);
|
||||
}
|
||||
|
||||
class TypeManager
|
||||
{
|
||||
public static List<Type> GetManagerTypes(Type rootType)
|
||||
{
|
||||
var types = new List<Type>();
|
||||
try
|
||||
{
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (a.IsDynamic)
|
||||
continue;
|
||||
foreach (var t in a.ExportedTypes)
|
||||
{
|
||||
if (t != rootType && rootType.IsAssignableFrom(t) && !t.IsAbstract)
|
||||
types.Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
}
|
||||
|
||||
class TypeManager<T> : TypeManager
|
||||
{
|
||||
// ReSharper disable once StaticMemberInGenericType
|
||||
static List<Type> s_Types;
|
||||
|
||||
public static List<Type> Types
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Types == null)
|
||||
s_Types = GetManagerTypes(typeof(T));
|
||||
|
||||
return s_Types;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool SafeMoveResourcesToGroup(AddressableAssetSettings settings, AddressableAssetGroup targetGroup, List<string> paths, List<string> guids, bool showDialog = true)
|
||||
{
|
||||
if (targetGroup == null)
|
||||
{
|
||||
Debug.LogWarning("No valid group to move Resources to");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (paths == null || paths.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("No valid Resources found to move");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (guids == null)
|
||||
{
|
||||
guids = new List<string>();
|
||||
foreach (var p in paths)
|
||||
guids.Add(AssetDatabase.AssetPathToGUID(p));
|
||||
}
|
||||
|
||||
Dictionary<string, string> guidToNewPath = new Dictionary<string, string>();
|
||||
|
||||
var message = "Any assets in Resources that you wish to mark as Addressable must be moved within the project. We will move the files to:\n\n";
|
||||
for (int i = 0; i < guids.Count; i++)
|
||||
{
|
||||
var newName = paths[i].Replace("\\", "/");
|
||||
newName = newName.Replace("Resources", "Resources_moved");
|
||||
newName = newName.Replace("resources", "resources_moved");
|
||||
if (newName == paths[i])
|
||||
continue;
|
||||
|
||||
guidToNewPath.Add(guids[i], newName);
|
||||
message += newName + "\n";
|
||||
}
|
||||
|
||||
message += "\nAre you sure you want to proceed?";
|
||||
if (!showDialog || EditorUtility.DisplayDialog("Move From Resources", message, "Yes", "No"))
|
||||
{
|
||||
settings.MoveAssetsFromResources(guidToNewPath, targetGroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static Dictionary<Type, string> s_CachedDisplayNames = new Dictionary<Type, string>();
|
||||
|
||||
internal static string GetCachedTypeDisplayName(Type type)
|
||||
{
|
||||
string result = "<none>";
|
||||
if (type != null)
|
||||
{
|
||||
if (!s_CachedDisplayNames.TryGetValue(type, out result))
|
||||
{
|
||||
var displayNameAtr = type.GetCustomAttribute<DisplayNameAttribute>();
|
||||
if (displayNameAtr != null)
|
||||
{
|
||||
result = (string)displayNameAtr.DisplayName;
|
||||
}
|
||||
else
|
||||
result = type.Name;
|
||||
|
||||
s_CachedDisplayNames.Add(type, result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct PackageData
|
||||
{
|
||||
public string version;
|
||||
}
|
||||
|
||||
private static string m_Version = null;
|
||||
|
||||
internal static string GetVersionFromPackageData()
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_Version))
|
||||
{
|
||||
var jsonFile = AssetDatabase.LoadAssetAtPath<TextAsset>("Packages/com.unity.addressables/package.json");
|
||||
var packageData = JsonUtility.FromJson<PackageData>(jsonFile.text);
|
||||
var split = packageData.version.Split('.');
|
||||
if (split.Length < 2)
|
||||
throw new Exception("Could not get correct version data for Addressables package");
|
||||
m_Version = $"{split[0]}.{split[1]}";
|
||||
}
|
||||
|
||||
return m_Version;
|
||||
}
|
||||
|
||||
public static string GenerateDocsURL(string page)
|
||||
{
|
||||
return $"https://docs.unity3d.com/Packages/com.unity.addressables@{GetVersionFromPackageData()}/manual/{page}";
|
||||
}
|
||||
|
||||
internal static bool IsUsingVCIntegration()
|
||||
{
|
||||
return Provider.isActive && Provider.enabled;
|
||||
}
|
||||
|
||||
internal static bool IsVCAssetOpenForEdit(string path)
|
||||
{
|
||||
AssetList VCAssets = GetVCAssets(path);
|
||||
foreach (Asset vcAsset in VCAssets)
|
||||
{
|
||||
if (vcAsset.path == path)
|
||||
return Provider.IsOpenForEdit(vcAsset);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static AssetList GetVCAssets(string path)
|
||||
{
|
||||
VersionControl.Task op = Provider.Status(path);
|
||||
op.Wait();
|
||||
return op.assetList;
|
||||
}
|
||||
|
||||
private static bool MakeAssetEditable(Asset asset)
|
||||
{
|
||||
if (!AssetDatabase.IsOpenForEdit(asset.path))
|
||||
return AssetDatabase.MakeEditable(asset.path);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool OpenAssetIfUsingVCIntegration(Object target, bool exitGUI = false)
|
||||
{
|
||||
if (!IsUsingVCIntegration() || target == null)
|
||||
return false;
|
||||
return OpenAssetIfUsingVCIntegration(AssetDatabase.GetAssetOrScenePath(target), exitGUI);
|
||||
}
|
||||
|
||||
internal static bool OpenAssetIfUsingVCIntegration(string path, bool exitGUI = false)
|
||||
{
|
||||
if (!IsUsingVCIntegration() || string.IsNullOrEmpty(path))
|
||||
return false;
|
||||
|
||||
AssetList assets = GetVCAssets(path);
|
||||
var uneditableAssets = new List<Asset>();
|
||||
string message = "Check out file(s)?\n\n";
|
||||
foreach (Asset asset in assets)
|
||||
{
|
||||
if (!Provider.IsOpenForEdit(asset))
|
||||
{
|
||||
uneditableAssets.Add(asset);
|
||||
message += $"{asset.path}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (uneditableAssets.Count == 0)
|
||||
return false;
|
||||
|
||||
bool openedAsset = true;
|
||||
if (EditorUtility.DisplayDialog("Attempting to modify files that are uneditable", message, "Yes", "No"))
|
||||
{
|
||||
foreach (Asset asset in uneditableAssets)
|
||||
{
|
||||
if (!MakeAssetEditable(asset))
|
||||
openedAsset = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
openedAsset = false;
|
||||
|
||||
if (exitGUI)
|
||||
GUIUtility.ExitGUI();
|
||||
return openedAsset;
|
||||
}
|
||||
|
||||
internal static bool InstallCCDPackage()
|
||||
{
|
||||
#if !ENABLE_CCD
|
||||
var confirm = EditorUtility.DisplayDialog("Install CCD Management SDK Package",
|
||||
"Are you sure you want to install the CCD Management SDK package and enable CCD features within Addressables?\nTo remove this package and its related features please use the Package manager, or uncheck the Addressable Asset Settings > Cloud Content Delivery > Enable CCD Features toggle.",
|
||||
"Yes", "No");
|
||||
if (confirm)
|
||||
{
|
||||
AddressableAnalytics.ReportUsageEvent(AddressableAnalytics.UsageEventType.InstallCCDManagementPackage);
|
||||
Client.Add("com.unity.services.ccd.management@2.1.0");
|
||||
AddressableAssetSettingsDefaultObject.Settings.CCDEnabled = true;
|
||||
}
|
||||
#endif
|
||||
return AddressableAssetSettingsDefaultObject.Settings.CCDEnabled;
|
||||
}
|
||||
|
||||
internal static bool RemoveCCDPackage()
|
||||
{
|
||||
var confirm = EditorUtility.DisplayDialog("Remove CCD Management SDK Package", "Are you sure you want to remove the CCD Management SDK package?", "Yes", "No");
|
||||
if (confirm)
|
||||
{
|
||||
#if (UNITY_2019_4_OR_NEWER)
|
||||
Client.Remove("com.unity.services.ccd.management");
|
||||
AddressableAssetSettingsDefaultObject.Settings.CCDEnabled = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
return AddressableAssetSettingsDefaultObject.Settings.CCDEnabled;
|
||||
}
|
||||
|
||||
internal static string GetMd5Hash(string path)
|
||||
{
|
||||
string hashString;
|
||||
using (var md5 = MD5.Create())
|
||||
{
|
||||
using (var stream = File.OpenRead(path))
|
||||
{
|
||||
var hash = md5.ComputeHash(stream);
|
||||
hashString = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
return hashString;
|
||||
}
|
||||
|
||||
|
||||
internal static System.Threading.Tasks.Task ParallelForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, System.Threading.Tasks.Task> body)
|
||||
{
|
||||
async System.Threading.Tasks.Task AwaitPartition(IEnumerator<T> partition)
|
||||
{
|
||||
using (partition)
|
||||
{
|
||||
while (partition.MoveNext())
|
||||
{
|
||||
await body(partition.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return System.Threading.Tasks.Task.WhenAll(
|
||||
Partitioner
|
||||
.Create(source)
|
||||
.GetPartitions(dop)
|
||||
.AsParallel()
|
||||
.Select(p => AwaitPartition(p)));
|
||||
}
|
||||
|
||||
internal class SortedDelegate<T1, T2, T3, T4>
|
||||
{
|
||||
struct QueuedValues
|
||||
{
|
||||
public T1 arg1;
|
||||
public T2 arg2;
|
||||
public T3 arg3;
|
||||
public T4 arg4;
|
||||
}
|
||||
|
||||
public delegate void Delegate(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
|
||||
private readonly SortedList<int, Delegate> m_SortedInvocationList = new SortedList<int, Delegate>();
|
||||
|
||||
private readonly List<QueuedValues> m_InvokeQueue = new List<QueuedValues>();
|
||||
private readonly List<(int, Delegate)> m_RegisterQueue = new List<(int, Delegate)> ();
|
||||
private bool m_IsInvoking;
|
||||
|
||||
/// <summary>
|
||||
/// Removes a delegate from the invocation list.
|
||||
/// </summary>
|
||||
/// <param name="toUnregister">Delegate to remove</param>
|
||||
public void Unregister(Delegate toUnregister)
|
||||
{
|
||||
IList<int> keys = m_SortedInvocationList.Keys;
|
||||
for (int i = 0; i < keys.Count; ++i)
|
||||
{
|
||||
m_SortedInvocationList[keys[i]] -= toUnregister;
|
||||
if (m_SortedInvocationList[keys[i]] == null)
|
||||
{
|
||||
m_SortedInvocationList.Remove(keys[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_IsInvoking && m_RegisterQueue.Count > 0)
|
||||
{
|
||||
for (int i = m_RegisterQueue.Count - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_RegisterQueue[i].Item2 == toUnregister)
|
||||
{
|
||||
m_RegisterQueue.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a delegate to the invocation list
|
||||
/// </summary>
|
||||
/// <param name="toRegister">Delegate to add</param>
|
||||
/// <param name="order">Order to call the delegate in the invocation list</param>
|
||||
public void Register(Delegate toRegister, int order)
|
||||
{
|
||||
if (m_IsInvoking)
|
||||
{
|
||||
m_RegisterQueue.Add((order, toRegister));
|
||||
return;
|
||||
}
|
||||
|
||||
FlushRegistrationQueue();
|
||||
RegisterToInvocationList(toRegister, order);
|
||||
FlushInvokeQueue();
|
||||
}
|
||||
|
||||
private void RegisterToInvocationList(Delegate toRegister, int order)
|
||||
{
|
||||
// unregister first, this will remove the delegate from another order if it is added
|
||||
Unregister(toRegister);
|
||||
if (m_SortedInvocationList.ContainsKey(order))
|
||||
m_SortedInvocationList[order] += toRegister;
|
||||
else
|
||||
m_SortedInvocationList.Add(order, toRegister);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoke all delegates in the invocation list for the given parameters
|
||||
/// </summary>
|
||||
/// <param name="arg1"></param>
|
||||
/// <param name="arg2"></param>
|
||||
/// <param name="arg3"></param>
|
||||
/// <param name="arg4"></param>
|
||||
public void Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
{
|
||||
if (m_IsInvoking)
|
||||
return;
|
||||
|
||||
FlushRegistrationQueue();
|
||||
Invoke_Internal(arg1, arg2, arg3, arg4);
|
||||
FlushInvokeQueue();
|
||||
}
|
||||
|
||||
private void Invoke_Internal(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
{
|
||||
m_IsInvoking = true;
|
||||
foreach (var invocationList in m_SortedInvocationList)
|
||||
{
|
||||
invocationList.Value?.Invoke(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
m_IsInvoking = false;
|
||||
}
|
||||
|
||||
private void FlushRegistrationQueue()
|
||||
{
|
||||
if (m_RegisterQueue.Count > 0)
|
||||
{
|
||||
for (int i = m_RegisterQueue.Count - 1; i >= 0; --i)
|
||||
RegisterToInvocationList(m_RegisterQueue[i].Item2, m_RegisterQueue[i].Item1);
|
||||
}
|
||||
}
|
||||
|
||||
private void FlushInvokeQueue()
|
||||
{
|
||||
if (m_InvokeQueue.Count > 0)
|
||||
{
|
||||
// keep looping the invoke buffer in case new invokes get added during invoke
|
||||
while (m_InvokeQueue.Count > 0)
|
||||
{
|
||||
for (int i = m_InvokeQueue.Count - 1; i >= 0; --i)
|
||||
{
|
||||
Invoke_Internal(m_InvokeQueue[i].arg1, m_InvokeQueue[i].arg2, m_InvokeQueue[i].arg3, m_InvokeQueue[i].arg4);
|
||||
m_InvokeQueue.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will invoke with the given parameters if there is any delegates in the invocation list, and not currently invoking
|
||||
/// else, will save the values and invoke when there is a delegate registered.
|
||||
/// </summary>
|
||||
/// <param name="arg1"></param>
|
||||
/// <param name="arg2"></param>
|
||||
/// <param name="arg3"></param>
|
||||
/// <param name="arg4"></param>
|
||||
public void TryInvokeOrDelayToReady(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
|
||||
{
|
||||
if (m_SortedInvocationList.Count == 0 || m_IsInvoking)
|
||||
{
|
||||
m_InvokeQueue.Add(new QueuedValues {arg1 = arg1, arg2 = arg2, arg3 = arg3, arg4 = arg4});
|
||||
}
|
||||
else
|
||||
{
|
||||
Invoke(arg1, arg2, arg3, arg4);
|
||||
}
|
||||
}
|
||||
|
||||
public static SortedDelegate<T1, T2, T3, T4> operator +(SortedDelegate<T1, T2, T3, T4> self, Delegate delegateToAdd)
|
||||
{
|
||||
int lastInOrder = self.m_SortedInvocationList.Keys[self.m_SortedInvocationList.Count - 1];
|
||||
self.Register(delegateToAdd, lastInOrder + 1);
|
||||
return self;
|
||||
}
|
||||
|
||||
public static SortedDelegate<T1, T2, T3, T4> operator -(SortedDelegate<T1, T2, T3, T4> self, Delegate delegateToRemove)
|
||||
{
|
||||
self.Unregister(delegateToRemove);
|
||||
return self;
|
||||
}
|
||||
|
||||
public static bool operator ==(SortedDelegate<T1, T2, T3, T4> obj1, SortedDelegate<T1, T2, T3, T4> obj2)
|
||||
{
|
||||
bool aNull = ReferenceEquals(obj1, null);
|
||||
bool bNull = ReferenceEquals(obj2, null);
|
||||
|
||||
if (aNull && bNull)
|
||||
return true;
|
||||
if (!aNull && bNull)
|
||||
return obj1.m_SortedInvocationList.Count == 0;
|
||||
if (aNull && !bNull)
|
||||
return obj2.m_SortedInvocationList.Count == 0;
|
||||
if (ReferenceEquals(obj1, obj2))
|
||||
return true;
|
||||
return obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
public static bool operator !=(SortedDelegate<T1, T2, T3, T4> lhs, SortedDelegate<T1, T2, T3, T4> rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
protected bool Equals(SortedDelegate<T1, T2, T3, T4> other)
|
||||
{
|
||||
return Equals(m_SortedInvocationList, other.m_SortedInvocationList);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((SortedDelegate<T1, T2, T3, T4>)obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (m_SortedInvocationList != null ? m_SortedInvocationList.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MoveEntriesToGroup(AddressableAssetSettings settings, List<AddressableAssetEntry> entries, AddressableAssetGroup group)
|
||||
{
|
||||
foreach (AddressableAssetEntry entry in entries)
|
||||
{
|
||||
if (entry.parentGroup != group)
|
||||
{
|
||||
settings.MoveEntry(entry, group, entry.ReadOnly, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: caa347671b900804bb0624c39d5404f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
static class AddressableScenesManager
|
||||
{
|
||||
static public void InitializeGlobalState()
|
||||
{
|
||||
BuiltinSceneCache.sceneListChanged += OnScenesChanged;
|
||||
AddressableAssetSettings.OnModificationGlobal += OnSettingsChanged;
|
||||
}
|
||||
|
||||
static public void ShutdownGlobalState()
|
||||
{
|
||||
AddressableAssetSettings.OnModificationGlobal -= OnSettingsChanged;
|
||||
BuiltinSceneCache.sceneListChanged -= OnScenesChanged;
|
||||
}
|
||||
|
||||
internal static void OnSettingsChanged(AddressableAssetSettings settings, AddressableAssetSettings.ModificationEvent evt, object obj)
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case AddressableAssetSettings.ModificationEvent.EntryCreated:
|
||||
case AddressableAssetSettings.ModificationEvent.EntryAdded:
|
||||
case AddressableAssetSettings.ModificationEvent.EntryMoved:
|
||||
case AddressableAssetSettings.ModificationEvent.EntryModified:
|
||||
var entries = obj as List<AddressableAssetEntry>;
|
||||
if (entries == null)
|
||||
{
|
||||
entries = new List<AddressableAssetEntry>();
|
||||
entries.Add(obj as AddressableAssetEntry);
|
||||
}
|
||||
|
||||
CheckForScenesInBuildList(entries);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnScenesChanged()
|
||||
{
|
||||
//ignore the play mode changes...
|
||||
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
return;
|
||||
|
||||
var settings = AddressableAssetSettingsDefaultObject.GetSettings(false);
|
||||
if (settings == null)
|
||||
return;
|
||||
|
||||
foreach (var scene in BuiltinSceneCache.scenes)
|
||||
{
|
||||
if (scene.enabled)
|
||||
{
|
||||
var entry = settings.FindAssetEntry(scene.guid.ToString());
|
||||
if (entry != null)
|
||||
{
|
||||
Debug.LogWarning("An addressable scene was added to the build scenes list and can thus no longer be addressable. " + scene.path);
|
||||
settings.RemoveAssetEntry(scene.guid.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckForScenesInBuildList(IList<AddressableAssetEntry> entries)
|
||||
{
|
||||
if (entries == null)
|
||||
return;
|
||||
|
||||
EditorBuildSettingsScene[] scenes = BuiltinSceneCache.scenes;
|
||||
bool changed = false;
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
if (entry == null)
|
||||
continue;
|
||||
|
||||
for (int index = 0; index < scenes.Length; index++)
|
||||
{
|
||||
var scene = scenes[index];
|
||||
if (scene.enabled && entry.AssetPath == scene.path)
|
||||
{
|
||||
Debug.LogWarning("A scene from the EditorBuildScenes list has been marked as addressable. It has thus been disabled in the build scenes list. " + scene.path);
|
||||
scenes[index].enabled = false;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
BuiltinSceneCache.scenes = scenes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 63e950f5b8d524d43b391e54d52f9ac0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor.Build.Pipeline.Interfaces;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
internal class AddressableAssetTree
|
||||
{
|
||||
internal class TreeNode
|
||||
{
|
||||
internal string Path { get; set; }
|
||||
internal bool IsAddressable { get; set; }
|
||||
public bool IsFolder { get; set; }
|
||||
internal bool HasEnumerated { get; set; }
|
||||
|
||||
internal Dictionary<string, TreeNode> Children { get; set; }
|
||||
|
||||
internal TreeNode(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
|
||||
internal bool GetChild(string p, out TreeNode node)
|
||||
{
|
||||
if (Children != null)
|
||||
return Children.TryGetValue(p, out node);
|
||||
node = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void AddChild(string p, TreeNode node)
|
||||
{
|
||||
if (Children == null)
|
||||
Children = new Dictionary<string, TreeNode>();
|
||||
Children.Add(p, node);
|
||||
}
|
||||
}
|
||||
|
||||
TreeNode m_Root;
|
||||
|
||||
internal AddressableAssetTree()
|
||||
{
|
||||
m_Root = new TreeNode("");
|
||||
m_Root.IsFolder = true;
|
||||
}
|
||||
|
||||
IEnumerable<string> EnumAddressables(TreeNode node, bool recursive, string relativePath)
|
||||
{
|
||||
if (node.Children != null)
|
||||
{
|
||||
List<TreeNode> curDirectory = new List<TreeNode>(node.Children.Values.Where(x => !x.IsAddressable));
|
||||
curDirectory.Sort((x, y) => { return string.CompareOrdinal(x.Path, y.Path); });
|
||||
string pathPrepend = string.IsNullOrEmpty(relativePath) ? "" : $"{relativePath}/";
|
||||
foreach (var v in curDirectory)
|
||||
{
|
||||
if (!v.IsFolder)
|
||||
yield return pathPrepend + v.Path;
|
||||
}
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
foreach (var v in curDirectory)
|
||||
if (v.Children != null)
|
||||
foreach (string v2 in EnumAddressables(v, true, pathPrepend + v.Path))
|
||||
yield return v2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<string> Enumerate(string path, bool recursive)
|
||||
{
|
||||
TreeNode node = FindNode(path, false);
|
||||
if (node == null)
|
||||
throw new Exception($"Path {path} was not in the enumeration tree");
|
||||
if (!node.HasEnumerated)
|
||||
throw new Exception($"Path {path} cannot be enumerated because the file system has not enumerated them yet");
|
||||
|
||||
return EnumAddressables(node, recursive, path);
|
||||
}
|
||||
|
||||
internal TreeNode FindNode(string path, bool shouldAdd)
|
||||
{
|
||||
TreeNode it = m_Root;
|
||||
foreach (string p in path.Split('/'))
|
||||
{
|
||||
if (!it.GetChild(p, out TreeNode it2))
|
||||
{
|
||||
if (!shouldAdd)
|
||||
return null;
|
||||
it2 = new TreeNode(p);
|
||||
it.AddChild(p, it2);
|
||||
it.IsFolder = true;
|
||||
}
|
||||
|
||||
it = it2;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Methods for enumerating Addressable folders.
|
||||
/// </summary>
|
||||
public class AddressablesFileEnumeration
|
||||
{
|
||||
internal static AddressableAssetTree BuildAddressableTree(AddressableAssetSettings settings, IBuildLogger logger = null)
|
||||
{
|
||||
using (logger.ScopedStep(LogLevel.Verbose, "BuildAddressableTree"))
|
||||
{
|
||||
if (!ExtractAddressablePaths(settings, out HashSet<string> paths))
|
||||
return null;
|
||||
|
||||
AddressableAssetTree tree = new AddressableAssetTree();
|
||||
foreach (string path in paths)
|
||||
{
|
||||
AddressableAssetTree.TreeNode node = tree.FindNode(path, true);
|
||||
node.IsAddressable = true;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ExtractAddressablePaths(AddressableAssetSettings settings, out HashSet<string> paths)
|
||||
{
|
||||
paths = new HashSet<string>();
|
||||
bool hasAddrFolder = false;
|
||||
foreach (AddressableAssetGroup group in settings.groups)
|
||||
{
|
||||
if (group == null)
|
||||
continue;
|
||||
foreach (AddressableAssetEntry entry in group.entries)
|
||||
{
|
||||
string convertedPath = entry.AssetPath;
|
||||
if (!hasAddrFolder && AssetDatabase.IsValidFolder(convertedPath))
|
||||
hasAddrFolder = true;
|
||||
paths.Add(convertedPath);
|
||||
}
|
||||
}
|
||||
|
||||
return hasAddrFolder;
|
||||
}
|
||||
|
||||
static void AddLocalFilesToTreeIfNotEnumerated(AddressableAssetTree tree, string path, IBuildLogger logger)
|
||||
{
|
||||
AddressableAssetTree.TreeNode pathNode = tree.FindNode(path, true);
|
||||
|
||||
if (pathNode == null || pathNode.HasEnumerated) // Already enumerated
|
||||
return;
|
||||
|
||||
pathNode.HasEnumerated = true;
|
||||
using (logger.ScopedStep(LogLevel.Info, $"Enumerating {path}"))
|
||||
{
|
||||
foreach (string filename in Directory.EnumerateFileSystemEntries(path, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (!AddressableAssetUtility.IsPathValidForEntry(filename) || string.IsNullOrEmpty(AssetDatabase.AssetPathToGUID(filename)))
|
||||
continue;
|
||||
string convertedPath = filename.Replace('\\', '/');
|
||||
var node = tree.FindNode(convertedPath, true);
|
||||
node.IsFolder = AssetDatabase.IsValidFolder(filename);
|
||||
node.HasEnumerated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class AddressablesFileEnumerationScope : IDisposable
|
||||
{
|
||||
AddressableAssetTree m_PrevTree;
|
||||
|
||||
internal AddressablesFileEnumerationScope(AddressableAssetTree tree)
|
||||
{
|
||||
m_PrevTree = m_PrecomputedTree;
|
||||
m_PrecomputedTree = tree;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_PrecomputedTree = m_PrevTree;
|
||||
}
|
||||
}
|
||||
|
||||
internal class AddressablesFileEnumerationCache : IDisposable
|
||||
{
|
||||
internal AddressablesFileEnumerationCache(AddressableAssetSettings settings, bool prepopulateAssetsFolder, IBuildLogger logger)
|
||||
{
|
||||
BeginPrecomputedEnumerationSession(settings, prepopulateAssetsFolder, logger);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
EndPrecomputedEnumerationSession();
|
||||
}
|
||||
}
|
||||
|
||||
internal static AddressableAssetTree m_PrecomputedTree;
|
||||
|
||||
static void BeginPrecomputedEnumerationSession(AddressableAssetSettings settings, bool prepopulateAssetsFolder, IBuildLogger logger)
|
||||
{
|
||||
using (logger.ScopedStep(LogLevel.Info, "AddressablesFileEnumeration.BeginPrecomputedEnumerationSession"))
|
||||
{
|
||||
m_PrecomputedTree = BuildAddressableTree(settings, logger);
|
||||
if (m_PrecomputedTree != null && prepopulateAssetsFolder)
|
||||
AddLocalFilesToTreeIfNotEnumerated(m_PrecomputedTree, "Assets", logger);
|
||||
}
|
||||
}
|
||||
|
||||
static void EndPrecomputedEnumerationSession()
|
||||
{
|
||||
m_PrecomputedTree = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collects and returns all the asset paths of a given Addressable folder entry
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the folder</param>
|
||||
/// <param name="settings">The AddressableAssetSettings used to gather sub entries.</param>
|
||||
/// <param name="recurseAll">Flag indicating if the folder should be traversed recursively.</param>
|
||||
/// <param name="logger">Used to log messages during a build, if desired.</param>
|
||||
/// <returns>List of asset files in a given folder entry</returns>
|
||||
public static List<string> EnumerateAddressableFolder(string path, AddressableAssetSettings settings, bool recurseAll, IBuildLogger logger = null)
|
||||
{
|
||||
if (!AssetDatabase.IsValidFolder(path))
|
||||
throw new Exception($"Path {path} cannot be enumerated because it does not exist");
|
||||
|
||||
AddressableAssetTree tree = m_PrecomputedTree != null ? m_PrecomputedTree : BuildAddressableTree(settings, logger);
|
||||
if (tree == null)
|
||||
return new List<string>();
|
||||
|
||||
AddLocalFilesToTreeIfNotEnumerated(tree, path, logger);
|
||||
|
||||
List<string> files = new List<string>();
|
||||
using (logger.ScopedStep(LogLevel.Info, $"Enumerating Addressables Tree {path}"))
|
||||
{
|
||||
foreach (string file in tree.Enumerate(path, recurseAll))
|
||||
{
|
||||
if (BuiltinSceneCache.Contains(file))
|
||||
continue;
|
||||
files.Add(file);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6fea6aa8d800ff94baa9d7edb9754298
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,472 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEditor.AddressableAssets.GUI;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.U2D;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
/// <summary>
|
||||
/// Contains editor data for the AssetReference.
|
||||
/// </summary>
|
||||
internal static class AssetReferenceDrawerUtilities
|
||||
{
|
||||
internal const string noAssetString = "None (Addressable Asset)";
|
||||
internal const string noAssetTypeStringformat = "None (Addressable {0})";
|
||||
|
||||
static internal bool ValidateAsset(AssetReference assetRefObject, List<AssetReferenceUIRestrictionSurrogate> restrictions, Object obj)
|
||||
{
|
||||
return assetRefObject != null
|
||||
&& assetRefObject.ValidateAsset(obj)
|
||||
&& restrictions != null
|
||||
&& restrictions.All(r => r.ValidateAsset(obj));
|
||||
}
|
||||
|
||||
static internal bool ValidateAsset(AssetReference assetRefObject, List<AssetReferenceUIRestrictionSurrogate> restrictions, IReferenceEntryData entryData)
|
||||
{
|
||||
return assetRefObject != null
|
||||
&& assetRefObject.ValidateAsset(entryData?.AssetPath)
|
||||
&& restrictions != null
|
||||
&& restrictions.All(r => r.ValidateAsset(entryData));
|
||||
}
|
||||
|
||||
static internal bool ValidateAsset(AssetReference assetRefObject, List<AssetReferenceUIRestrictionSurrogate> restrictions, string path)
|
||||
{
|
||||
if (assetRefObject != null && assetRefObject.ValidateAsset(path))
|
||||
{
|
||||
foreach (var restriction in restrictions)
|
||||
{
|
||||
if (!restriction.ValidateAsset(path))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static internal bool SetObject(ref AssetReference assetRefObject, ref bool referencesSame, SerializedProperty property, Object target, FieldInfo fieldInfo, string label, out string guid)
|
||||
{
|
||||
guid = null;
|
||||
try
|
||||
{
|
||||
if (assetRefObject == null)
|
||||
return false;
|
||||
Undo.RecordObject(property.serializedObject.targetObject, "Assign Asset Reference");
|
||||
if (target == null)
|
||||
{
|
||||
guid = SetSingleAsset(ref assetRefObject, property, null, null);
|
||||
if (property.serializedObject.targetObjects.Length > 1)
|
||||
return SetMainAssets(ref referencesSame, property, null, null, fieldInfo, label);
|
||||
return true;
|
||||
}
|
||||
|
||||
Object subObject = null;
|
||||
if (target.GetType() == typeof(Sprite))
|
||||
{
|
||||
var atlasEntries = new List<AddressableAssetEntry>();
|
||||
AddressableAssetSettingsDefaultObject.Settings.GetAllAssets(atlasEntries, false, null,
|
||||
e => AssetDatabase.GetMainAssetTypeAtPath(e.AssetPath) == typeof(SpriteAtlas));
|
||||
var spriteName = FormatName(target.name);
|
||||
foreach (var a in atlasEntries)
|
||||
{
|
||||
var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(a.AssetPath);
|
||||
if (atlas == null)
|
||||
continue;
|
||||
var s = atlas.GetSprite(spriteName);
|
||||
if (s == null)
|
||||
continue;
|
||||
subObject = target;
|
||||
target = atlas;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (subObject == null && AssetDatabase.IsSubAsset(target))
|
||||
{
|
||||
subObject = target;
|
||||
target = AssetDatabase.LoadAssetAtPath<Object>(AssetDatabase.GetAssetPath(target));
|
||||
}
|
||||
|
||||
guid = SetSingleAsset(ref assetRefObject, property, target, subObject);
|
||||
|
||||
var success = true;
|
||||
if (property.serializedObject.targetObjects.Length > 1)
|
||||
{
|
||||
success = SetMainAssets(ref referencesSame, property, target, subObject, fieldInfo, label);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static internal string SetSingleAsset(ref AssetReference assetReferenceObject, SerializedProperty property, Object asset, Object subObject)
|
||||
{
|
||||
string guid = null;
|
||||
bool success = false;
|
||||
if (asset == null)
|
||||
{
|
||||
assetReferenceObject.SetEditorAsset(null);
|
||||
SetDirty(property.serializedObject.targetObject);
|
||||
return guid;
|
||||
}
|
||||
|
||||
success = assetReferenceObject.SetEditorAsset(asset);
|
||||
if (success)
|
||||
{
|
||||
if (subObject != null)
|
||||
assetReferenceObject.SetEditorSubObject(subObject);
|
||||
else
|
||||
assetReferenceObject.SubObjectName = null;
|
||||
guid = assetReferenceObject.AssetGUID;
|
||||
SetDirty(property.serializedObject.targetObject);
|
||||
}
|
||||
|
||||
return guid;
|
||||
}
|
||||
|
||||
static internal bool SetMainAssets(ref bool referencesSame, SerializedProperty property, Object asset, Object subObject, FieldInfo propertyField, string labelText)
|
||||
{
|
||||
var allsuccess = true;
|
||||
foreach (var targetObj in property.serializedObject.targetObjects)
|
||||
{
|
||||
var serializeObjectMulti = new SerializedObject(targetObj);
|
||||
SerializedProperty sp = serializeObjectMulti.FindProperty(property.propertyPath);
|
||||
var assetRefObject =
|
||||
sp.GetActualObjectForSerializedProperty<AssetReference>(propertyField, ref labelText);
|
||||
if (assetRefObject != null)
|
||||
{
|
||||
Undo.RecordObject(targetObj, "Assign Asset Reference");
|
||||
var success = assetRefObject.SetEditorAsset(asset);
|
||||
if (success)
|
||||
{
|
||||
if (subObject != null)
|
||||
assetRefObject.SetEditorSubObject(subObject);
|
||||
else
|
||||
assetRefObject.SubObjectName = null;
|
||||
SetDirty(targetObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
allsuccess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
referencesSame = allsuccess;
|
||||
return allsuccess;
|
||||
}
|
||||
|
||||
static internal bool SetSubAssets(SerializedProperty property, Object subAsset, FieldInfo propertyField, string labelText)
|
||||
{
|
||||
bool valueChanged = false;
|
||||
string spriteName = null;
|
||||
if (subAsset != null && subAsset.GetType() == typeof(Sprite))
|
||||
{
|
||||
spriteName = FormatName(subAsset.name);
|
||||
}
|
||||
|
||||
foreach (var t in property.serializedObject.targetObjects)
|
||||
{
|
||||
var serializeObjectMulti = new SerializedObject(t);
|
||||
var sp = serializeObjectMulti.FindProperty(property.propertyPath);
|
||||
var assetRefObject =
|
||||
sp.GetActualObjectForSerializedProperty<AssetReference>(propertyField, ref labelText);
|
||||
if (assetRefObject != null && (assetRefObject.SubObjectName == null || assetRefObject.SubObjectName != spriteName))
|
||||
{
|
||||
Undo.RecordObject(t, "Assign Asset Reference Sub Object");
|
||||
var success = assetRefObject.SetEditorSubObject(subAsset);
|
||||
if (success)
|
||||
{
|
||||
valueChanged = true;
|
||||
SetDirty(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
valueChanged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return valueChanged;
|
||||
}
|
||||
|
||||
static internal bool CheckTargetObjectsSubassetsAreDifferent(SerializedProperty property, string objName, FieldInfo propertyField, string labelText)
|
||||
{
|
||||
foreach (var targetObject in property.serializedObject.targetObjects)
|
||||
{
|
||||
var serializeObjectMulti = new SerializedObject(targetObject);
|
||||
var sp = serializeObjectMulti.FindProperty(property.propertyPath);
|
||||
var assetRefObject = sp.GetActualObjectForSerializedProperty<AssetReference>(propertyField, ref labelText);
|
||||
if (assetRefObject != null && assetRefObject.SubObjectName != null)
|
||||
{
|
||||
if (assetRefObject.SubObjectName != objName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void SetDirty(Object obj)
|
||||
{
|
||||
UnityEngine.GUI.changed = true; // To support EditorGUI.BeginChangeCheck() / EditorGUI.EndChangeCheck()
|
||||
|
||||
EditorUtility.SetDirty(obj);
|
||||
AddressableAssetUtility.OpenAssetIfUsingVCIntegration(obj);
|
||||
var comp = obj as Component;
|
||||
if (comp != null && comp.gameObject != null && comp.gameObject.activeInHierarchy)
|
||||
EditorSceneManager.MarkSceneDirty(comp.gameObject.scene);
|
||||
}
|
||||
|
||||
static internal List<AssetReferenceUIRestrictionSurrogate> GatherFilters(SerializedProperty property)
|
||||
{
|
||||
List<AssetReferenceUIRestrictionSurrogate> restrictions = new List<AssetReferenceUIRestrictionSurrogate>();
|
||||
var o = property.serializedObject.targetObject;
|
||||
if (o != null)
|
||||
{
|
||||
var t = o.GetType();
|
||||
FieldInfo info = null;
|
||||
|
||||
// We need to look into sub types, if any.
|
||||
string[] pathParts = property.propertyPath.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
for (int i = 0; i < pathParts.Length; i++)
|
||||
{
|
||||
FieldInfo f = t.GetField(pathParts[i],
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (f != null)
|
||||
{
|
||||
t = f.FieldType;
|
||||
info = f;
|
||||
}
|
||||
}
|
||||
|
||||
if (info != null)
|
||||
{
|
||||
var a = info.GetCustomAttributes(false);
|
||||
foreach (var attr in a)
|
||||
{
|
||||
var uiRestriction = attr as AssetReferenceUIRestriction;
|
||||
if (uiRestriction != null)
|
||||
{
|
||||
var surrogate = AssetReferenceUtility.GetSurrogate(uiRestriction.GetType());
|
||||
|
||||
if (surrogate != null)
|
||||
{
|
||||
var surrogateInstance =
|
||||
Activator.CreateInstance(surrogate) as AssetReferenceUIRestrictionSurrogate;
|
||||
if (surrogateInstance != null)
|
||||
{
|
||||
surrogateInstance.Init(uiRestriction);
|
||||
restrictions.Add(surrogateInstance);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetReferenceUIRestrictionSurrogate restriction =
|
||||
new AssetReferenceUIRestrictionSurrogate();
|
||||
restriction.Init(uiRestriction);
|
||||
restrictions.Add(restriction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return restrictions;
|
||||
}
|
||||
|
||||
static internal List<Object> GetSubAssetsList(AssetReference assetReferenceObject)
|
||||
{
|
||||
var subAssets = new List<Object>();
|
||||
subAssets.Add(null);
|
||||
var assetPath = AssetDatabase.GUIDToAssetPath(assetReferenceObject.AssetGUID);
|
||||
|
||||
var repr = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath);
|
||||
if (repr.Any())
|
||||
{
|
||||
var subtype = assetReferenceObject.SubOjbectType ?? GetGenericTypeFromAssetReference(assetReferenceObject);
|
||||
if (subtype != null)
|
||||
repr = repr.Where(o => subtype.IsInstanceOfType(o)).OrderBy(s => s.name).ToArray();
|
||||
}
|
||||
|
||||
subAssets.AddRange(repr);
|
||||
|
||||
var mainType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
|
||||
if (mainType == typeof(SpriteAtlas))
|
||||
{
|
||||
var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(assetPath);
|
||||
var sprites = new Sprite[atlas.spriteCount];
|
||||
atlas.GetSprites(sprites);
|
||||
subAssets.AddRange(sprites.OrderBy(s => s.name));
|
||||
}
|
||||
|
||||
return subAssets;
|
||||
}
|
||||
|
||||
static Type GetGenericTypeFromAssetReference(AssetReference assetReferenceObject)
|
||||
{
|
||||
var type = assetReferenceObject?.GetType();
|
||||
while (type != null)
|
||||
{
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(AssetReferenceT<>))
|
||||
return type.GenericTypeArguments[0];
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static internal string GetNameForAsset(ref bool referencesSame, SerializedProperty property, bool isNotAddressable, FieldInfo propertyField, string labelText)
|
||||
{
|
||||
var currentRef =
|
||||
property.GetActualObjectForSerializedProperty<AssetReference>(propertyField, ref labelText);
|
||||
|
||||
string nameToUse = currentRef.editorAsset != null ? currentRef.editorAsset.name : ConstructNoAssetLabel(propertyField.FieldType);
|
||||
|
||||
if (property.serializedObject.targetObjects.Length > 1)
|
||||
{
|
||||
foreach (var t in property.serializedObject.targetObjects)
|
||||
{
|
||||
var serializeObjectMulti = new SerializedObject(t);
|
||||
var sp = serializeObjectMulti.FindProperty(property.propertyPath);
|
||||
var assetRefObject =
|
||||
sp.GetActualObjectForSerializedProperty<AssetReference>(propertyField, ref labelText);
|
||||
if (assetRefObject.AssetGUID != currentRef.AssetGUID)
|
||||
{
|
||||
referencesSame = false;
|
||||
return "--";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isNotAddressable)
|
||||
{
|
||||
nameToUse = "Not Addressable - " + nameToUse;
|
||||
}
|
||||
|
||||
return nameToUse;
|
||||
}
|
||||
|
||||
internal static string ConstructNoAssetLabel(Type t)
|
||||
{
|
||||
if (t == null || t == typeof(AssetReference))
|
||||
return FormatNoAssetString(string.Empty);
|
||||
t = GetGenericType(t);
|
||||
if (t == null || t == typeof(AssetReference))
|
||||
return FormatNoAssetString(string.Empty);
|
||||
return FormatNoAssetString(t.Name);
|
||||
}
|
||||
|
||||
static string FormatNoAssetString(string n) => string.IsNullOrEmpty(n) ? noAssetString : string.Format(noAssetTypeStringformat, n);
|
||||
|
||||
private static Type GetGenericType(Type t)
|
||||
{
|
||||
if (t == null)
|
||||
return null;
|
||||
while (t.GenericTypeArguments.Length > 0)
|
||||
t = t.GenericTypeArguments[0];
|
||||
if (t.BaseType != null && t.BaseType.GenericTypeArguments.Length == 1)
|
||||
t = GetGenericType(t.BaseType);
|
||||
if (t.HasElementType)
|
||||
t = GetGenericType(t.GetElementType());
|
||||
return t;
|
||||
}
|
||||
|
||||
static internal string FormatName(string name)
|
||||
{
|
||||
var formatted = string.IsNullOrEmpty(name) ? "<none>" : name;
|
||||
if (formatted.EndsWith("(Clone)", StringComparison.Ordinal))
|
||||
formatted = formatted.Replace("(Clone)", "");
|
||||
return formatted;
|
||||
}
|
||||
|
||||
static internal bool ValidateDrag(AssetReference assetReferenceObject, List<AssetReferenceUIRestrictionSurrogate> restrictions, List<AssetEntryTreeViewItem> aaEntries,
|
||||
Object[] dropObjReferences, string[] dropPaths)
|
||||
{
|
||||
if (aaEntries != null)
|
||||
{
|
||||
foreach (AssetEntryTreeViewItem item in aaEntries)
|
||||
{
|
||||
if (item == null || item.entry == null || item.entry.IsInResources || !ValidateAsset(assetReferenceObject, restrictions, item.entry.AssetPath))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (dropObjReferences != null)
|
||||
{
|
||||
foreach (Object obj in dropObjReferences)
|
||||
{
|
||||
if (AssetDatabase.IsSubAsset(obj) && !ValidateAsset(assetReferenceObject, restrictions, obj))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var path in DragAndDrop.paths)
|
||||
{
|
||||
if (!ValidateAsset(assetReferenceObject, restrictions, path))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static internal bool CheckForNewEntry(ref string assetName, AddressableAssetSettings aaSettings, string guid, string checkToForceAddressable)
|
||||
{
|
||||
var entry = aaSettings.FindAssetEntry(guid);
|
||||
if (entry != null)
|
||||
{
|
||||
assetName = entry.address;
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
if (!aaSettings.IsAssetPathInAddressableDirectory(path, out assetName))
|
||||
{
|
||||
assetName = path;
|
||||
if (!string.IsNullOrEmpty(checkToForceAddressable))
|
||||
{
|
||||
var newEntry = aaSettings.CreateOrMoveEntry(guid, aaSettings.DefaultGroup);
|
||||
Addressables.LogFormat("Created AddressableAsset {0} in group {1}.", newEntry.address, aaSettings.DefaultGroup.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
assetName = "Missing File!";
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assetName = "Missing File!";
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a95802677a5e469db2346b7c99cdbe84
|
||||
timeCreated: 1617641523
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
internal static class BuiltinSceneCache
|
||||
{
|
||||
internal static EditorBuildSettingsScene[] m_Scenes;
|
||||
static Dictionary<GUID, int> s_GUIDSceneIndexLookup;
|
||||
static Dictionary<string, int> s_PathSceneIndexLookup;
|
||||
static bool s_IsListening;
|
||||
public static event Action sceneListChanged;
|
||||
|
||||
internal static void ClearState(bool clearCallbacks = false)
|
||||
{
|
||||
InvalidateCache();
|
||||
if (s_IsListening)
|
||||
{
|
||||
EditorBuildSettings.sceneListChanged -= EditorBuildSettings_sceneListChanged;
|
||||
s_IsListening = false;
|
||||
}
|
||||
|
||||
if (clearCallbacks)
|
||||
sceneListChanged = null;
|
||||
}
|
||||
|
||||
public static EditorBuildSettingsScene[] scenes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Scenes == null)
|
||||
{
|
||||
if (!s_IsListening)
|
||||
{
|
||||
s_IsListening = true;
|
||||
EditorBuildSettings.sceneListChanged += EditorBuildSettings_sceneListChanged;
|
||||
}
|
||||
|
||||
InvalidateCache();
|
||||
m_Scenes = EditorBuildSettings.scenes;
|
||||
}
|
||||
|
||||
return m_Scenes;
|
||||
}
|
||||
set { EditorBuildSettings.scenes = value; }
|
||||
}
|
||||
|
||||
public static Dictionary<GUID, int> GUIDSceneIndexLookup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_GUIDSceneIndexLookup == null)
|
||||
{
|
||||
EditorBuildSettingsScene[] localScenes = scenes;
|
||||
s_GUIDSceneIndexLookup = new Dictionary<GUID, int>();
|
||||
int enabledIndex = 0;
|
||||
for (int i = 0; i < scenes.Length; i++)
|
||||
{
|
||||
if (localScenes[i] != null && localScenes[i].enabled)
|
||||
s_GUIDSceneIndexLookup[localScenes[i].guid] = enabledIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return s_GUIDSceneIndexLookup;
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, int> PathSceneIndexLookup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_PathSceneIndexLookup == null)
|
||||
{
|
||||
EditorBuildSettingsScene[] localScenes = scenes;
|
||||
s_PathSceneIndexLookup = new Dictionary<string, int>();
|
||||
int enabledIndex = 0;
|
||||
for (int i = 0; i < scenes.Length; i++)
|
||||
{
|
||||
if (localScenes[i] != null && localScenes[i].enabled)
|
||||
s_PathSceneIndexLookup[localScenes[i].path] = enabledIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return s_PathSceneIndexLookup;
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvalidateCache()
|
||||
{
|
||||
m_Scenes = null;
|
||||
s_GUIDSceneIndexLookup = null;
|
||||
s_PathSceneIndexLookup = null;
|
||||
}
|
||||
|
||||
public static int GetSceneIndex(GUID guid)
|
||||
{
|
||||
int index = -1;
|
||||
return GUIDSceneIndexLookup.TryGetValue(guid, out index) ? index : -1;
|
||||
}
|
||||
|
||||
public static bool Contains(GUID guid)
|
||||
{
|
||||
return GUIDSceneIndexLookup.ContainsKey(guid);
|
||||
}
|
||||
|
||||
public static bool Contains(string path)
|
||||
{
|
||||
return PathSceneIndexLookup.ContainsKey(path);
|
||||
}
|
||||
|
||||
public static bool GetSceneFromGUID(GUID guid, out EditorBuildSettingsScene outScene)
|
||||
{
|
||||
int index = GetSceneIndex(guid);
|
||||
if (index == -1)
|
||||
{
|
||||
outScene = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
outScene = scenes[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void EditorBuildSettings_sceneListChanged()
|
||||
{
|
||||
InvalidateCache();
|
||||
if (sceneListChanged != null)
|
||||
sceneListChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a8e45712f43ff0645b809f8a5c1ba37e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets.Initialization;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Asset container for CacheInitializationData.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "CacheInitializationSettings.asset", menuName = "Addressables/Initialization/Cache Initialization Settings")]
|
||||
public class CacheInitializationSettings : ScriptableObject, IObjectInitializationDataProvider
|
||||
{
|
||||
[FormerlySerializedAs("m_data")]
|
||||
[SerializeField]
|
||||
CacheInitializationData m_Data = new CacheInitializationData();
|
||||
|
||||
/// <summary>
|
||||
/// Display name used in GUI for this object.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get { return "Asset Bundle Cache Settings"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The cache initialization data that will be serialized and applied during Addressables initialization.
|
||||
/// </summary>
|
||||
public CacheInitializationData Data
|
||||
{
|
||||
get { return m_Data; }
|
||||
set { m_Data = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create initialization data to be serialized into the Addressables runtime data.
|
||||
/// </summary>
|
||||
/// <returns>The serialized data for the initialization class and the data.</returns>
|
||||
public ObjectInitializationData CreateObjectInitializationData()
|
||||
{
|
||||
return ObjectInitializationData.CreateSerializedInitializationData<CacheInitialization>(typeof(CacheInitialization).Name, m_Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b44c2db511ef19949950f4b3b93a6e53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
internal interface ICcdFolder<T> where T : class
|
||||
{
|
||||
void GetChildren(DirectoryInfo startDirectory);
|
||||
}
|
||||
|
||||
internal class CcdBuildDataFolder : ICcdFolder<CcdEnvironmentFolder>
|
||||
{
|
||||
public string Name;
|
||||
public string Location;
|
||||
public List<CcdEnvironmentFolder> Environments = new List<CcdEnvironmentFolder>();
|
||||
|
||||
public void GetChildren(DirectoryInfo startDirectory)
|
||||
{
|
||||
var envDirs = startDirectory.GetDirectories().Where(d => !d.Attributes.HasFlag(FileAttributes.Hidden));
|
||||
foreach (var dir in envDirs)
|
||||
{
|
||||
var envFolder = new CcdEnvironmentFolder();
|
||||
envFolder.Name = dir.Name;
|
||||
envFolder.Location = dir.FullName;
|
||||
envFolder.GetChildren(dir);
|
||||
Environments.Add(envFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CcdEnvironmentFolder : ICcdFolder<CcdBucketFolder>
|
||||
{
|
||||
public string Name;
|
||||
public string Location;
|
||||
public List<CcdBucketFolder> Buckets = new List<CcdBucketFolder>();
|
||||
|
||||
public void GetChildren(DirectoryInfo startDirectory)
|
||||
{
|
||||
var bucketDirs = startDirectory.GetDirectories().Where(d => !d.Attributes.HasFlag(FileAttributes.Hidden));
|
||||
foreach (var dir in bucketDirs)
|
||||
{
|
||||
var bucketFolder = new CcdBucketFolder();
|
||||
bucketFolder.Name = dir.Name;
|
||||
bucketFolder.Location = dir.FullName;
|
||||
bucketFolder.GetChildren(dir);
|
||||
Buckets.Add(bucketFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CcdBucketFolder : ICcdFolder<CcdBadgeFolder>
|
||||
{
|
||||
public string Name;
|
||||
public string Location;
|
||||
public List<CcdBadgeFolder> Badges = new List<CcdBadgeFolder>();
|
||||
|
||||
public void GetChildren(DirectoryInfo startDirectory)
|
||||
{
|
||||
var badgeDirs = startDirectory.GetDirectories().Where(d => !d.Attributes.HasFlag(FileAttributes.Hidden));
|
||||
foreach (var dir in badgeDirs)
|
||||
{
|
||||
var badgeFolder = new CcdBadgeFolder();
|
||||
badgeFolder.Name = dir.Name;
|
||||
badgeFolder.Location = dir.FullName;
|
||||
badgeFolder.GetChildren(dir);
|
||||
Badges.Add(badgeFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CcdBadgeFolder : ICcdFolder<FileInfo>
|
||||
{
|
||||
public string Name;
|
||||
public string Location;
|
||||
public List<FileInfo> Files = new List<FileInfo>();
|
||||
|
||||
public void GetChildren(DirectoryInfo startDirectory)
|
||||
{
|
||||
Files = startDirectory.GetFiles().Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 87f118cb270dc490e84ebea5a5283615
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
static class GlobalInitialization
|
||||
{
|
||||
static bool m_IsInitialized = false;
|
||||
|
||||
static GlobalInitialization()
|
||||
{
|
||||
InitializeGlobalState();
|
||||
}
|
||||
|
||||
public static void InitializeGlobalState()
|
||||
{
|
||||
if (!m_IsInitialized)
|
||||
{
|
||||
AddressableScenesManager.InitializeGlobalState();
|
||||
m_IsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This only gets called by testing code that wants to do isolated testing without active Addressables global hooks
|
||||
public static void ShutdownGlobalState()
|
||||
{
|
||||
if (m_IsInitialized)
|
||||
{
|
||||
AddressableScenesManager.ShutdownGlobalState();
|
||||
m_IsInitialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3f9489010bc9cdc458c23db402d6b3fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b842ab94dae39f3459a2159fb7ef9b73
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e5d17a21594effb4e9591490b009e7aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings.GroupSchemas
|
||||
{
|
||||
/// <summary>
|
||||
/// Schema for content updates.
|
||||
/// </summary>
|
||||
// [CreateAssetMenu(fileName = "ContentUpdateGroupSchema.asset", menuName = "Addressables/Group Schemas/Content Update")]
|
||||
[DisplayName("Content Update Restriction")]
|
||||
public class ContentUpdateGroupSchema : AddressableAssetGroupSchema
|
||||
{
|
||||
[FormerlySerializedAs("m_staticContent")]
|
||||
[SerializeField]
|
||||
bool m_StaticContent;
|
||||
|
||||
/// <summary>
|
||||
/// Is the group static. This property is used in determining which assets need to be moved to a new remote group during the content update process.
|
||||
/// </summary>
|
||||
public bool StaticContent
|
||||
{
|
||||
get => m_StaticContent;
|
||||
set
|
||||
{
|
||||
if (m_StaticContent != value)
|
||||
{
|
||||
m_StaticContent = value;
|
||||
SetDirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GUIContent m_UpdateRestrictionGUIContent = new GUIContent("Prevent Updates", "Assets in Prevent Update groups will be moved to a new remote group during the content update process");
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnGUI()
|
||||
{
|
||||
var staticContent = EditorGUILayout.Toggle(m_UpdateRestrictionGUIContent, m_StaticContent);
|
||||
if (staticContent != m_StaticContent)
|
||||
{
|
||||
var prop = SchemaSerializedObject.FindProperty("m_StaticContent");
|
||||
prop.boolValue = staticContent;
|
||||
SchemaSerializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnGUIMultiple(List<AddressableAssetGroupSchema> otherSchemas)
|
||||
{
|
||||
string propertyName = "m_StaticContent";
|
||||
var prop = SchemaSerializedObject.FindProperty(propertyName);
|
||||
|
||||
// Type/Static Content
|
||||
ShowMixedValue(prop, otherSchemas, typeof(bool), propertyName);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
var staticContent = EditorGUILayout.Toggle(m_UpdateRestrictionGUIContent, m_StaticContent);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
prop.boolValue = staticContent;
|
||||
SchemaSerializedObject.ApplyModifiedProperties();
|
||||
foreach (var s in otherSchemas)
|
||||
{
|
||||
var otherProp = s.SchemaSerializedObject.FindProperty(propertyName);
|
||||
otherProp.boolValue = staticContent;
|
||||
s.SchemaSerializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5834b5087d578d24c926ce20cd31e6d6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings.GroupSchemas
|
||||
{
|
||||
/// <summary>
|
||||
/// Schema for the player data asset group
|
||||
/// </summary>
|
||||
//[CreateAssetMenu(fileName = "PlayerDataGroupSchema.asset", menuName = "Addressables/Group Schemas/Player Data")]
|
||||
[DisplayName("Resources and Built In Scenes")]
|
||||
public class PlayerDataGroupSchema : AddressableAssetGroupSchema
|
||||
{
|
||||
[Tooltip("Assets in resources folders will have addresses generated during the build")]
|
||||
[FormerlySerializedAs("m_includeResourcesFolders")]
|
||||
[SerializeField]
|
||||
bool m_IncludeResourcesFolders = true;
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, all assets in resources folders will have addresses generated during the build.
|
||||
/// </summary>
|
||||
public bool IncludeResourcesFolders
|
||||
{
|
||||
get => m_IncludeResourcesFolders;
|
||||
set
|
||||
{
|
||||
if (m_IncludeResourcesFolders != value)
|
||||
{
|
||||
m_IncludeResourcesFolders = value;
|
||||
SetDirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Tooltip("All scenes in the editor build settings will have addresses generated during the build")]
|
||||
[FormerlySerializedAs("m_includeBuildSettingsScenes")]
|
||||
[SerializeField]
|
||||
bool m_IncludeBuildSettingsScenes = true;
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, all scenes in the editor build settings will have addresses generated during the build.
|
||||
/// </summary>
|
||||
public bool IncludeBuildSettingsScenes
|
||||
{
|
||||
get => m_IncludeBuildSettingsScenes;
|
||||
set
|
||||
{
|
||||
if (m_IncludeBuildSettingsScenes != value)
|
||||
{
|
||||
m_IncludeBuildSettingsScenes = value;
|
||||
SetDirty(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnGUIMultiple(List<AddressableAssetGroupSchema> otherSchemas)
|
||||
{
|
||||
SerializedProperty prop;
|
||||
string propertyName = "m_IncludeResourcesFolders";
|
||||
HashSet<SerializedObject> applyModifications = new HashSet<SerializedObject>();
|
||||
|
||||
// IncludeResourcesFolders
|
||||
prop = SchemaSerializedObject.FindProperty(propertyName);
|
||||
ShowMixedValue(prop, otherSchemas, typeof(bool), propertyName);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool newIncludeResourcesFolders = (bool)EditorGUILayout.Toggle(new GUIContent(prop.displayName, "Assets in resources folders will have addresses generated during the build"),
|
||||
IncludeResourcesFolders);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
prop.boolValue = newIncludeResourcesFolders;
|
||||
applyModifications.Add(SchemaSerializedObject);
|
||||
foreach (var s in otherSchemas)
|
||||
{
|
||||
var otherProp = s.SchemaSerializedObject.FindProperty(propertyName);
|
||||
otherProp.boolValue = newIncludeResourcesFolders;
|
||||
applyModifications.Add(s.SchemaSerializedObject);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = false;
|
||||
|
||||
// IncludeBuildSettingsScenes
|
||||
propertyName = "m_IncludeBuildSettingsScenes";
|
||||
prop = SchemaSerializedObject.FindProperty(propertyName);
|
||||
ShowMixedValue(prop, otherSchemas, typeof(bool), propertyName);
|
||||
EditorGUI.BeginChangeCheck();
|
||||
bool newIncludeBuildSettingsScenes =
|
||||
(bool)EditorGUILayout.Toggle(new GUIContent(prop.displayName, "All scenes in the editor build settings will have addresses generated during the build"), IncludeBuildSettingsScenes);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
prop.boolValue = newIncludeBuildSettingsScenes;
|
||||
applyModifications.Add(SchemaSerializedObject);
|
||||
foreach (var s in otherSchemas)
|
||||
{
|
||||
var otherProp = s.SchemaSerializedObject.FindProperty(propertyName);
|
||||
otherProp.boolValue = newIncludeBuildSettingsScenes;
|
||||
applyModifications.Add(s.SchemaSerializedObject);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.showMixedValue = false;
|
||||
|
||||
foreach (SerializedObject serializedObject in applyModifications)
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b1487f5d688e4f94f828f879d599dbdc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEditor.AddressableAssets.GUI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains serialized data in a generic serializable container.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class KeyDataStore : ISerializationCallbackReceiver
|
||||
{
|
||||
[Serializable]
|
||||
internal struct Entry
|
||||
{
|
||||
[FormerlySerializedAs("m_assemblyName")]
|
||||
[SerializeField]
|
||||
string m_AssemblyName;
|
||||
|
||||
internal string AssemblyName
|
||||
{
|
||||
get { return m_AssemblyName; }
|
||||
set { m_AssemblyName = value; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_className")]
|
||||
[SerializeField]
|
||||
string m_ClassName;
|
||||
|
||||
internal string ClassName
|
||||
{
|
||||
get { return m_ClassName; }
|
||||
set { m_ClassName = value; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_data")]
|
||||
[SerializeField]
|
||||
string m_Data;
|
||||
|
||||
internal string Data
|
||||
{
|
||||
get { return m_Data; }
|
||||
set { m_Data = value; }
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_key")]
|
||||
[SerializeField]
|
||||
string m_Key;
|
||||
|
||||
internal string Key
|
||||
{
|
||||
get { return m_Key; }
|
||||
set { m_Key = value; }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_SerializedData = null;
|
||||
m_EntryMap = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
[FormerlySerializedAs("m_serializedData")]
|
||||
[SerializeField]
|
||||
List<Entry> m_SerializedData;
|
||||
|
||||
Dictionary<string, object> m_EntryMap = new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Delegate that is invoked when data is modified.
|
||||
/// </summary>
|
||||
public Action<string, object, bool> OnSetData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of ISerializationCallbackReceiver interface, used to convert data to a serializable form.
|
||||
/// </summary>
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
m_SerializedData = new List<Entry>(m_EntryMap.Count);
|
||||
foreach (var k in m_EntryMap)
|
||||
m_SerializedData.Add(CreateEntry(k.Key, k.Value));
|
||||
}
|
||||
|
||||
Entry CreateEntry(string key, object value)
|
||||
{
|
||||
var entry = new Entry();
|
||||
entry.Key = key;
|
||||
var objType = value.GetType();
|
||||
entry.AssemblyName = objType.Assembly.FullName;
|
||||
entry.ClassName = objType.FullName;
|
||||
try
|
||||
{
|
||||
if (objType == typeof(string))
|
||||
{
|
||||
entry.Data = value as string;
|
||||
}
|
||||
else if (objType.IsEnum)
|
||||
{
|
||||
entry.Data = value.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
var parseMethod = objType.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any, new[] {typeof(string)}, null);
|
||||
if (parseMethod == null || parseMethod.ReturnType != objType)
|
||||
entry.Data = JsonUtility.ToJson(value);
|
||||
else
|
||||
entry.Data = value.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Addressables.LogWarningFormat("KeyDataStore unable to serizalize entry {0} with value {1}, exception: {2}", key, value, ex);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
object CreateObject(Entry e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.Load(e.AssemblyName);
|
||||
var objType = assembly.GetType(e.ClassName);
|
||||
if (objType == typeof(string))
|
||||
return e.Data;
|
||||
if (objType.IsEnum)
|
||||
return Enum.Parse(objType, e.Data);
|
||||
var parseMethod = objType.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Any, new[] {typeof(string)}, null);
|
||||
if (parseMethod == null || parseMethod.ReturnType != objType)
|
||||
return JsonUtility.FromJson(e.Data, objType);
|
||||
return parseMethod.Invoke(null, new object[] {e.Data});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Addressables.LogWarningFormat("KeyDataStore unable to deserizalize entry {0} from assembly {1} of type {2}, exception: {3}", e.Key, e.AssemblyName, e.ClassName, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of ISerializationCallbackReceiver interface, used to convert data from its serializable form.
|
||||
/// </summary>
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
m_EntryMap = new Dictionary<string, object>(m_SerializedData.Count);
|
||||
foreach (var e in m_SerializedData)
|
||||
m_EntryMap.Add(e.Key, CreateObject(e));
|
||||
m_SerializedData = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The collection of keys stored.
|
||||
/// </summary>
|
||||
public IEnumerable<string> Keys
|
||||
{
|
||||
get { return m_EntryMap.Keys; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of a specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="data">The data to store. Supported types are strings, POD types, objects that have a static method named 'Parse' that convert a string to an object, and object that are serializable via JSONUtilty.</param>
|
||||
public void SetData(string key, object data)
|
||||
{
|
||||
var isNew = m_EntryMap.ContainsKey(key);
|
||||
m_EntryMap[key] = data;
|
||||
if (OnSetData != null)
|
||||
OnSetData(key, data, isNew);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set data for a specified key from a string.
|
||||
/// </summary>
|
||||
/// <param name="key">The data key.</param>
|
||||
/// <param name="data">The data string value.</param>
|
||||
public void SetDataFromString(string key, string data)
|
||||
{
|
||||
var existingType = GetDataType(key);
|
||||
if (existingType == null)
|
||||
SetData(key, data);
|
||||
else
|
||||
SetData(key, CreateObject(new Entry {AssemblyName = existingType.Assembly.FullName, ClassName = existingType.FullName, Data = data, Key = key}));
|
||||
}
|
||||
|
||||
internal Type GetDataType(string key)
|
||||
{
|
||||
object val;
|
||||
if (m_EntryMap.TryGetValue(key, out val))
|
||||
return val.GetType();
|
||||
return null;
|
||||
}
|
||||
|
||||
internal string GetDataString(string key, string defaultValue)
|
||||
{
|
||||
object val;
|
||||
if (m_EntryMap.TryGetValue(key, out val))
|
||||
return CreateEntry(key, val).ToString();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get data via a specified key.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data type.</typeparam>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="defaultValue">The default value to return if the data is not found.</param>
|
||||
/// <param name="addDefault">Optional parameter to control whether to add the default value if the data is not found.</param>
|
||||
/// <returns></returns>
|
||||
public T GetData<T>(string key, T defaultValue, bool addDefault = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
object val;
|
||||
if (m_EntryMap.TryGetValue(key, out val))
|
||||
return (T)val;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (addDefault)
|
||||
SetData(key, defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a3b28cc6d2f5b444af3331b13911334
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEditor.AddressableAssets.GUI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
[Serializable]
|
||||
class LabelTable
|
||||
{
|
||||
[FormerlySerializedAs("m_labelNames")]
|
||||
[SerializeField]
|
||||
List<string> m_LabelNames = new List<string>(new[] {"default"});
|
||||
|
||||
internal List<string> labelNames
|
||||
{
|
||||
get { return m_LabelNames; }
|
||||
}
|
||||
|
||||
internal bool AddLabelName(string name)
|
||||
{
|
||||
if (m_LabelNames.Contains(name))
|
||||
return false;
|
||||
#if NET_UNITY_4_8
|
||||
if (name.Contains('[', StringComparison.Ordinal) && name.Contains(']', StringComparison.Ordinal))
|
||||
#else
|
||||
if (name.Contains("[") && name.Contains("]"))
|
||||
#endif
|
||||
{
|
||||
Debug.LogErrorFormat("Label name '{0}' cannot contain '[ ]'.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_CurrentHash = default;
|
||||
m_LabelNames.Add(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool AddLabelName(string name, int index)
|
||||
{
|
||||
if (m_LabelNames.Contains(name))
|
||||
return false;
|
||||
#if NET_UNITY_4_8
|
||||
if (name.Contains('[', StringComparison.Ordinal) && name.Contains(']', StringComparison.Ordinal))
|
||||
#else
|
||||
if (name.Contains("[") && name.Contains("]"))
|
||||
#endif
|
||||
{
|
||||
Debug.LogErrorFormat("Label name '{0}' cannot contain '[ ]'.", name);
|
||||
return false;
|
||||
}
|
||||
m_CurrentHash = default;
|
||||
m_LabelNames.Insert(index, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal string GetUniqueLabelName(string name)
|
||||
{
|
||||
var newName = name;
|
||||
int counter = 1;
|
||||
while (counter < 100)
|
||||
{
|
||||
if (!m_LabelNames.Contains(newName))
|
||||
return newName;
|
||||
newName = name + counter;
|
||||
counter++;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
internal bool RemoveLabelName(string name)
|
||||
{
|
||||
m_CurrentHash = default;
|
||||
return m_LabelNames.Remove(name);
|
||||
}
|
||||
|
||||
internal string GetString(HashSet<string> val, float width)
|
||||
{
|
||||
if (val == null || val.Count == 0)
|
||||
return "";
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
var content = new GUIContent("");
|
||||
int remaining = val.Count;
|
||||
foreach (string s in val)
|
||||
{
|
||||
remaining--;
|
||||
content.text = s;
|
||||
var sx = UnityEngine.GUI.skin.label.CalcSize(content);
|
||||
width -= sx.x;
|
||||
|
||||
string labelName = m_LabelNames.Contains(s) ? s :
|
||||
AddressablesGUIUtility.ConvertTextToStrikethrough(s);
|
||||
|
||||
if (remaining > 0)
|
||||
sb.Append($"{labelName}, ");
|
||||
else
|
||||
sb.Append(labelName);
|
||||
|
||||
if (width < 20)
|
||||
break;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal int GetIndexOfLabel(string label)
|
||||
{
|
||||
return m_LabelNames.IndexOf(label);
|
||||
}
|
||||
|
||||
internal long GetMask(HashSet<string> maskSet)
|
||||
{
|
||||
if (maskSet.Count == 0)
|
||||
return 0;
|
||||
long one = 1;
|
||||
long val = 0;
|
||||
for (int i = 0; i < m_LabelNames.Count; i++)
|
||||
if (maskSet.Contains(m_LabelNames[i]))
|
||||
val |= one << i;
|
||||
return val;
|
||||
}
|
||||
|
||||
Hash128 m_CurrentHash;
|
||||
internal Hash128 currentHash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_CurrentHash.isValid)
|
||||
{
|
||||
foreach (var label in m_LabelNames)
|
||||
m_CurrentHash.Append(label);
|
||||
}
|
||||
return m_CurrentHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b9df82cea39d69a4a9e27b9eb6103717
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
[Serializable]
|
||||
internal class OrgData
|
||||
{
|
||||
[SerializeField]
|
||||
internal string id;
|
||||
|
||||
[SerializeField]
|
||||
internal string name;
|
||||
|
||||
[SerializeField]
|
||||
internal string foreign_key;
|
||||
|
||||
[SerializeField]
|
||||
internal string billable_user_fk;
|
||||
|
||||
[SerializeField]
|
||||
internal string org_identifier;
|
||||
|
||||
[SerializeField]
|
||||
internal string orgIdentifier;
|
||||
|
||||
internal static OrgData ParseOrgData(string data)
|
||||
{
|
||||
var orgData = JsonUtility.FromJson<OrgData>(data);
|
||||
if (orgData.id == null)
|
||||
{
|
||||
throw new ArgumentException("Unable to parse org data.");
|
||||
}
|
||||
|
||||
return orgData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a521f5672f7fa4a80a23fb2124b2773a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
using UnityEditor.AddressableAssets.Build.BuildPipelineTasks;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.AddressableAssets
|
||||
{
|
||||
internal static class AddressablesPreferences
|
||||
{
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
internal const string kBuildAddressablesWithPlayerBuildKey = "Addressables.BuildAddressablesWithPlayerBuild";
|
||||
#endif
|
||||
private class GUIScope : UnityEngine.GUI.Scope
|
||||
{
|
||||
float m_LabelWidth;
|
||||
|
||||
public GUIScope(float layoutMaxWidth)
|
||||
{
|
||||
m_LabelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth = 250;
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(10);
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Space(15);
|
||||
}
|
||||
|
||||
public GUIScope() : this(500)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void CloseScope()
|
||||
{
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUIUtility.labelWidth = m_LabelWidth;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Properties
|
||||
{
|
||||
public static readonly GUIContent buildSettings = EditorGUIUtility.TrTextContent("Build Settings");
|
||||
|
||||
public static readonly GUIContent buildLayoutReport = EditorGUIUtility.TrTextContent("Debug Build Layout",
|
||||
$"A debug build layout file will be generated as part of the build process. The file will put written to {BuildLayoutGenerationTask.m_LayoutFilePath}");
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
internal static readonly GUIContent autoOpenAddressablesReport = EditorGUIUtility.TrTextContent("Open Addressables Report after build");
|
||||
#endif
|
||||
public static readonly GUIContent buildLayoutReportFileFormat = EditorGUIUtility.TrTextContent("File Format", $"The file format of the debug build layout file.");
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
public static readonly GUIContent playerBuildSettings = EditorGUIUtility.TrTextContent("Player Build Settings");
|
||||
|
||||
public static readonly GUIContent enableAddressableBuildPreprocessPlayer = EditorGUIUtility.TrTextContent("Build Addressables on build Player",
|
||||
$"If enabled, will perform a new Addressables build before building a Player. Addressable Asset Settings value can override the user global preferences.");
|
||||
#endif
|
||||
|
||||
#if !ENABLE_ADDRESSABLE_PROFILER && UNITY_2021_2_OR_NEWER
|
||||
public static readonly GUIContent installProfilingCoreHelp = EditorGUIUtility.TrTextContent("In order to run Addressables profiler using the debug layout. Profiling.Core package is required for etc. Install now?",
|
||||
$"Profiling.Core is needed to transmit and display data from the runtime.");
|
||||
public static readonly GUIContent installProfilingCoreButton = EditorGUIUtility.TrTextContent("Install now",
|
||||
$"Enables Profiling.Core package in this project");
|
||||
#endif
|
||||
}
|
||||
|
||||
static AddressablesPreferences()
|
||||
{
|
||||
}
|
||||
|
||||
[SettingsProvider]
|
||||
static SettingsProvider CreateAddressableSettingsProvider()
|
||||
{
|
||||
var provider = new SettingsProvider("Preferences/Addressables", SettingsScope.User, SettingsProvider.GetSearchKeywordsFromGUIContentProperties<Properties>());
|
||||
provider.guiHandler = sarchContext => OnGUI();
|
||||
return provider;
|
||||
}
|
||||
|
||||
static void OnGUI()
|
||||
{
|
||||
using (new GUIScope())
|
||||
{
|
||||
DrawProperties();
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawProperties()
|
||||
{
|
||||
GUILayout.Label(Properties.buildSettings, EditorStyles.boldLabel);
|
||||
|
||||
ProjectConfigData.GenerateBuildLayout = EditorGUILayout.Toggle(Properties.buildLayoutReport, ProjectConfigData.GenerateBuildLayout);
|
||||
if (ProjectConfigData.GenerateBuildLayout)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
ProjectConfigData.ReportFileFormat buildLayoutReportFileFormat = ProjectConfigData.BuildLayoutReportFileFormat;
|
||||
int formatOldIndex = (int)buildLayoutReportFileFormat;
|
||||
int formatNewIndex = EditorGUILayout.Popup(Properties.buildLayoutReportFileFormat, formatOldIndex, new[] {"TXT and JSON", "JSON"});
|
||||
if (formatNewIndex != formatOldIndex)
|
||||
ProjectConfigData.BuildLayoutReportFileFormat = (ProjectConfigData.ReportFileFormat)formatNewIndex;
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
EditorGUI.indentLevel++;
|
||||
ProjectConfigData.AutoOpenAddressablesReport = EditorGUILayout.Toggle(Properties.autoOpenAddressablesReport, ProjectConfigData.AutoOpenAddressablesReport);
|
||||
EditorGUI.indentLevel--;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !ENABLE_ADDRESSABLE_PROFILER && UNITY_2021_2_OR_NEWER
|
||||
GUILayout.Space(15);
|
||||
EditorGUILayout.HelpBox(Properties.installProfilingCoreHelp);
|
||||
var rect = EditorGUILayout.GetControlRect();
|
||||
if (UnityEngine.GUI.Button(rect, Properties.installProfilingCoreButton))
|
||||
{
|
||||
UnityEditor.PackageManager.Client.Add("com.unity.profiling.core@1.0.2");
|
||||
}
|
||||
#endif
|
||||
|
||||
GUILayout.Space(15);
|
||||
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
bool buildWithPlayerValue = EditorPrefs.GetBool(kBuildAddressablesWithPlayerBuildKey, true);
|
||||
|
||||
GUILayout.Label(Properties.playerBuildSettings, EditorStyles.boldLabel);
|
||||
int index = buildWithPlayerValue ? 0 : 1;
|
||||
int val = EditorGUILayout.Popup(Properties.enableAddressableBuildPreprocessPlayer, index,
|
||||
new[] {"Build Addressables on Player Build", "Do Not Build Addressables on Player Build"});
|
||||
if (val != index)
|
||||
{
|
||||
bool newValue = val == 0 ? true : false;
|
||||
EditorPrefs.SetBool(kBuildAddressablesWithPlayerBuildKey, newValue);
|
||||
buildWithPlayerValue = newValue;
|
||||
}
|
||||
|
||||
var settings = AddressableAssetSettingsDefaultObject.Settings;
|
||||
if (settings != null)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
if (settings.BuildAddressablesWithPlayerBuild == AddressableAssetSettings.PlayerBuildOption.BuildWithPlayer &&
|
||||
buildWithPlayerValue == false)
|
||||
{
|
||||
EditorGUILayout.TextField(" ", "Enabled in AddressableAssetSettings (priority)");
|
||||
}
|
||||
else if (settings.BuildAddressablesWithPlayerBuild == AddressableAssetSettings.PlayerBuildOption.DoNotBuildWithPlayer &&
|
||||
buildWithPlayerValue)
|
||||
{
|
||||
EditorGUILayout.TextField(" ", "Disabled in AddressableAssetSettings (priority)");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7cbf9ceccd804b446bc46ab595d96182
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,516 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using System.Text;
|
||||
using System.Net.Http;
|
||||
|
||||
#if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
|
||||
using Unity.Services.Core;
|
||||
using Unity.Services.Ccd.Management;
|
||||
using Unity.Services.Ccd.Management.Http;
|
||||
using Unity.Services.Ccd.Management.Models;
|
||||
#endif
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Scriptable Object that holds data source setting information for the profile data source dropdown window
|
||||
/// </summary>
|
||||
public class ProfileDataSourceSettings : ScriptableObject, ISerializationCallbackReceiver
|
||||
{
|
||||
const string DEFAULT_PATH = "Assets/AddressableAssetsData";
|
||||
const string DEFAULT_NAME = "ProfileDataSourceSettings";
|
||||
const string CONTENT_RANGE_HEADER = "Content-Range";
|
||||
static string DEFAULT_SETTING_PATH = $"{DEFAULT_PATH}/{DEFAULT_NAME}.asset";
|
||||
|
||||
#if ENABLE_CCD
|
||||
internal const string MANAGED_ENVIRONMENT = "ManagedEnvironment";
|
||||
internal const string MANAGED_BUCKET = "ManagedBucket";
|
||||
internal const string MANAGED_BADGE = "ManagedBadge";
|
||||
#endif
|
||||
|
||||
// paths
|
||||
internal static string m_CloudEnvironment = "production";
|
||||
internal static string m_GenesisBasePath = "https://api-staging.unity.com";
|
||||
internal static string m_CcdClientBasePath = ".client-api-stg.unity3dusercontent.com";
|
||||
internal static string m_DashboardBasePath = "https://staging.dashboard.unity3d.com";
|
||||
internal static string m_ServicesBasePath = "https://staging.services.unity.com";
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
internal static void InitializeCloudEnvironment()
|
||||
{
|
||||
var cloudEnvironment = GetCloudEnvironment();
|
||||
switch (cloudEnvironment)
|
||||
{
|
||||
case "staging":
|
||||
#if ENABLE_CCD
|
||||
CcdManagement.SetBasePath("https://staging.services.unity.com");
|
||||
#endif
|
||||
m_GenesisBasePath = "https://api-staging.unity.com";
|
||||
m_CcdClientBasePath = ".client-api-stg.unity3dusercontent.com";
|
||||
m_DashboardBasePath = "https://staging.dashboard.unity3d.com";
|
||||
m_ServicesBasePath = "https://staging.services.unity.com";
|
||||
break;
|
||||
default:
|
||||
#if ENABLE_CCD
|
||||
CcdManagement.SetBasePath("https://services.unity.com");
|
||||
#endif
|
||||
m_GenesisBasePath = "https://api.unity.com";
|
||||
m_CcdClientBasePath = ".client-api.unity3dusercontent.com";
|
||||
m_DashboardBasePath = "https://dashboard.unity3d.com";
|
||||
m_ServicesBasePath = "https://services.unity.com";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const string EnvironmentArg = "-cloudEnvironment";
|
||||
|
||||
internal static string GetCloudEnvironment()
|
||||
{
|
||||
try
|
||||
{
|
||||
var commandLineArgs = System.Environment.GetCommandLineArgs();
|
||||
var cloudEnvironmentIndex = Array.IndexOf(commandLineArgs, EnvironmentArg);
|
||||
|
||||
if (cloudEnvironmentIndex >= 0 && cloudEnvironmentIndex <= commandLineArgs.Length - 2)
|
||||
{
|
||||
return commandLineArgs[cloudEnvironmentIndex + 1];
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Group types that exist within the settings object
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public List<ProfileGroupType> profileGroupTypes = new List<ProfileGroupType>();
|
||||
|
||||
[SerializeField]
|
||||
internal List<Environment> environments = new List<Environment>();
|
||||
|
||||
[SerializeField]
|
||||
internal Environment currentEnvironment;
|
||||
|
||||
/// <summary>
|
||||
/// Creates, if needed, and returns the profile data source settings for the project
|
||||
/// </summary>
|
||||
/// <param name="path">Desired path to put settings</param>
|
||||
/// <param name="settingName">Desired name for settings</param>
|
||||
/// <returns></returns>
|
||||
public static ProfileDataSourceSettings Create(string path = null, string settingName = null)
|
||||
{
|
||||
ProfileDataSourceSettings aa;
|
||||
var assetPath = DEFAULT_SETTING_PATH;
|
||||
|
||||
if (path != null && settingName != null)
|
||||
{
|
||||
assetPath = $"{path}/{settingName}.asset";
|
||||
}
|
||||
|
||||
aa = AssetDatabase.LoadAssetAtPath<ProfileDataSourceSettings>(assetPath);
|
||||
if (aa == null)
|
||||
{
|
||||
Directory.CreateDirectory(path != null ? path : DEFAULT_PATH);
|
||||
aa = CreateInstance<ProfileDataSourceSettings>();
|
||||
AssetDatabase.CreateAsset(aa, assetPath);
|
||||
aa = AssetDatabase.LoadAssetAtPath<ProfileDataSourceSettings>(assetPath);
|
||||
aa.profileGroupTypes = CreateDefaultGroupTypes();
|
||||
EditorUtility.SetDirty(aa);
|
||||
}
|
||||
|
||||
return aa;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile data source settings for the project
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="settingName"></param>
|
||||
/// <returns></returns>
|
||||
public static ProfileDataSourceSettings GetSettings(string path = null, string settingName = null)
|
||||
{
|
||||
ProfileDataSourceSettings aa;
|
||||
var assetPath = DEFAULT_SETTING_PATH;
|
||||
|
||||
if (path != null && settingName != null)
|
||||
{
|
||||
assetPath = $"{path}/{settingName}.asset";
|
||||
}
|
||||
|
||||
aa = AssetDatabase.LoadAssetAtPath<ProfileDataSourceSettings>(assetPath);
|
||||
if (aa == null)
|
||||
return Create();
|
||||
return aa;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list of default group types that are automatically added on ProfileDataSourceSettings object creation
|
||||
/// </summary>
|
||||
/// <returns>List of ProfileGroupTypes: Built-In and Editor Hosted</returns>
|
||||
public static List<ProfileGroupType> CreateDefaultGroupTypes() => new List<ProfileGroupType>
|
||||
{
|
||||
CreateBuiltInGroupType(),
|
||||
CreateEditorHostedGroupType(),
|
||||
#if ENABLE_CCD
|
||||
CreateCcdManagerGroupType()
|
||||
#endif
|
||||
};
|
||||
|
||||
static ProfileGroupType CreateBuiltInGroupType()
|
||||
{
|
||||
ProfileGroupType defaultBuiltIn = new ProfileGroupType(AddressableAssetSettings.LocalGroupTypePrefix);
|
||||
defaultBuiltIn.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, AddressableAssetSettings.kLocalBuildPathValue));
|
||||
defaultBuiltIn.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, AddressableAssetSettings.kLocalLoadPathValue));
|
||||
return defaultBuiltIn;
|
||||
}
|
||||
|
||||
static ProfileGroupType CreateEditorHostedGroupType()
|
||||
{
|
||||
ProfileGroupType defaultRemote = new ProfileGroupType(AddressableAssetSettings.EditorHostedGroupTypePrefix);
|
||||
defaultRemote.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, AddressableAssetSettings.kRemoteBuildPathValue));
|
||||
defaultRemote.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, AddressableAssetSettings.RemoteLoadPathValue));
|
||||
return defaultRemote;
|
||||
}
|
||||
|
||||
#if ENABLE_CCD
|
||||
static ProfileGroupType CreateCcdManagerGroupType()
|
||||
{
|
||||
string buildPath = $"{AddressableAssetSettings.kCCDBuildDataPath}/{MANAGED_ENVIRONMENT}/{MANAGED_BUCKET}/{MANAGED_BADGE}";
|
||||
string loadPath =
|
||||
$"https://{CloudProjectSettings.projectId}{m_CcdClientBasePath}/client_api/v1/environments/{{CcdManager.EnvironmentName}}/buckets/{{CcdManager.BucketId}}/release_by_badge/{{CcdManager.Badge}}/entry_by_path/content/?path=";
|
||||
ProfileGroupType defaultCcdManager = new ProfileGroupType(AddressableAssetSettings.CcdManagerGroupTypePrefix);
|
||||
defaultCcdManager.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, buildPath));
|
||||
defaultCcdManager.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, loadPath));
|
||||
return defaultCcdManager;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Given a valid profileGroupType, searches the settings and returns, if exists, the profile group type
|
||||
/// </summary>
|
||||
/// <param name="groupType"></param>
|
||||
/// <returns>ProfileGroupType if found, null otherwise</returns>
|
||||
public ProfileGroupType FindGroupType(ProfileGroupType groupType)
|
||||
{
|
||||
ProfileGroupType result = null;
|
||||
if (!groupType.IsValidGroupType())
|
||||
{
|
||||
throw new ArgumentException("Group Type is not valid. Group Type must include a build path and load path variables");
|
||||
}
|
||||
|
||||
var buildPath = groupType.GetVariableBySuffix(AddressableAssetSettings.kBuildPath);
|
||||
var loadPath = groupType.GetVariableBySuffix(AddressableAssetSettings.kLoadPath);
|
||||
foreach (ProfileGroupType settingsGroupType in profileGroupTypes)
|
||||
{
|
||||
var foundBuildPath = settingsGroupType.ContainsVariable(buildPath);
|
||||
var foundLoadPath = settingsGroupType.ContainsVariable(loadPath);
|
||||
if (foundBuildPath && foundLoadPath)
|
||||
{
|
||||
result = settingsGroupType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a list of ProfileGroupType that matches the given prefix
|
||||
/// </summary>
|
||||
/// <param name="prefix">prefix to search by</param>
|
||||
/// <returns>List of ProfileGroupType</returns>
|
||||
public List<ProfileGroupType> GetGroupTypesByPrefix(string prefix)
|
||||
{
|
||||
return profileGroupTypes.Where((groupType) => groupType.GroupTypePrefix.StartsWith(prefix, StringComparison.Ordinal)).ToList();
|
||||
}
|
||||
|
||||
#if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
|
||||
/// <summary>
|
||||
/// Updates the CCD buckets and badges with the data source settings
|
||||
/// </summary>
|
||||
/// <param name="projectId">Project Id connected to Unity Services</param>
|
||||
/// <param name="showInfoLog">Whether or not to show debug logs or not</param>
|
||||
/// <returns>List of ProfileGroupType</returns>
|
||||
public static async Task<List<ProfileGroupType>> UpdateCCDDataSourcesAsync(string projectId, bool showInfoLog)
|
||||
{
|
||||
var settings = GetSettings();
|
||||
|
||||
if (showInfoLog)
|
||||
{
|
||||
Addressables.Log("Syncing CCD Environments, Buckets, and Badges.");
|
||||
}
|
||||
|
||||
var profileGroupTypes = new List<ProfileGroupType>();
|
||||
|
||||
var environments = await GetEnvironments();
|
||||
|
||||
if (showInfoLog)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Syncing Profile Data Sources", "Fetching Environments", 0);
|
||||
Addressables.Log($"Successfully fetched {environments.Count} environments.");
|
||||
}
|
||||
profileGroupTypes.AddRange(CreateDefaultGroupTypes());
|
||||
|
||||
try
|
||||
{
|
||||
var envProgress = 1;
|
||||
foreach (var environment in environments)
|
||||
{
|
||||
var bucketDictionary = await GetAllBucketsAsync(environment.id);
|
||||
var bucketProgress = 1;
|
||||
foreach (var kvp in bucketDictionary)
|
||||
{
|
||||
|
||||
var bucket = kvp.Value;
|
||||
if (showInfoLog)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar($"Syncing Environment: {environment.name} ({envProgress} of {environments.Count})", $"Loading {bucket.Name}", (bucketProgress / (float)bucketDictionary.Count));
|
||||
}
|
||||
|
||||
var badges = await GetAllBadgesAsync(bucket.Id.ToString());
|
||||
if (badges.Count == 0) badges.Add(new CcdBadge(name: "latest"));
|
||||
foreach (var badge in badges)
|
||||
{
|
||||
var groupType =
|
||||
new ProfileGroupType($"CCD{ProfileGroupType.k_PrefixSeparator}{projectId}{ProfileGroupType.k_PrefixSeparator}{environment.id}{ProfileGroupType.k_PrefixSeparator}{bucket.Id}{ProfileGroupType.k_PrefixSeparator}{badge.Name}");
|
||||
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBucket)}{nameof(CcdBucket.Name)}", bucket.Name));
|
||||
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBucket)}{nameof(CcdBucket.Id)}", bucket.Id.ToString()));
|
||||
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(CcdBadge)}{nameof(CcdBadge.Name)}", badge.Name));
|
||||
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(nameof(CcdBucket.Attributes.PromoteOnly), bucket.Attributes.PromoteOnly.ToString()));
|
||||
|
||||
//Adding environment stub here
|
||||
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(Environment)}{nameof(Environment.name)}", environment.name));
|
||||
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable($"{nameof(Environment)}{nameof(Environment.id)}", environment.id));
|
||||
|
||||
string buildPath = $"{AddressableAssetSettings.kCCDBuildDataPath}/{environment.id}/{bucket.Id}/{badge.Name}";
|
||||
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath, buildPath));
|
||||
|
||||
string loadPath =
|
||||
$"https://{projectId}{m_CcdClientBasePath}/client_api/v1/environments/{environment.name}/buckets/{bucket.Id}/release_by_badge/{badge.Name}/entry_by_path/content/?path=";
|
||||
groupType.AddVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath, loadPath));
|
||||
|
||||
profileGroupTypes.Add(groupType);
|
||||
}
|
||||
bucketProgress++;
|
||||
}
|
||||
envProgress++;
|
||||
}
|
||||
|
||||
settings.profileGroupTypes = profileGroupTypes;
|
||||
settings.environments = environments.ToList();
|
||||
if (showInfoLog) Addressables.Log("Successfully synced CCD Buckets and Badges.");
|
||||
EditorUtility.SetDirty(settings);
|
||||
AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings);
|
||||
}
|
||||
catch (CcdManagementException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
return settings.profileGroupTypes;
|
||||
}
|
||||
|
||||
internal static async Task<Dictionary<Guid, CcdBucket>> GetAllBucketsAsync(string environment)
|
||||
{
|
||||
CcdManagement.SetEnvironmentId(environment);
|
||||
int page = 1;
|
||||
bool loop = true;
|
||||
List<CcdBucket> buckets = new List<CcdBucket>();
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
var listBuckets = await CcdManagement.Instance.ListBucketsAsync(new PageOptions()
|
||||
{
|
||||
Page = page
|
||||
});
|
||||
buckets.AddRange(listBuckets);
|
||||
page++;
|
||||
}
|
||||
catch (CcdManagementException e)
|
||||
{
|
||||
if (e.ErrorCode == CcdManagementErrorCodes.OutOfRange)
|
||||
{
|
||||
loop = false;
|
||||
}
|
||||
else if (e.ErrorCode == CommonErrorCodes.Forbidden)
|
||||
{
|
||||
throw new CcdManagementException(e.ErrorCode, "Unactivated Org. Please activate your organization via the Unity Dashboard");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} while (loop);
|
||||
return buckets.ToDictionary(kvp => kvp.Id, kvp => kvp);
|
||||
}
|
||||
|
||||
internal static async Task<List<CcdBadge>> GetAllBadgesAsync(string bucketId)
|
||||
{
|
||||
int page = 1;
|
||||
bool loop = true;
|
||||
List<CcdBadge> badges = new List<CcdBadge>();
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
var listBadges = await CcdManagement.Instance.ListBadgesAsync(Guid.Parse(bucketId), new PageOptions()
|
||||
{
|
||||
Page = page
|
||||
});
|
||||
badges.AddRange(listBadges);
|
||||
page++;
|
||||
}
|
||||
catch (CcdManagementException e)
|
||||
{
|
||||
if (e.ErrorCode == CcdManagementErrorCodes.OutOfRange)
|
||||
{
|
||||
loop = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} while (loop);
|
||||
return badges;
|
||||
}
|
||||
|
||||
internal static async Task<List<Environment>> GetEnvironments()
|
||||
{
|
||||
|
||||
var projectId = CloudProjectSettings.projectId;
|
||||
var authToken = await GetAuthToken();
|
||||
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
|
||||
{
|
||||
client.DefaultRequestHeaders.Add("Authorization", authToken);
|
||||
var response = await client.GetAsync(String.Format("{0}/api/unity/legacy/v1/projects/{1}/environments", m_ServicesBasePath, projectId));
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new Exception(response.ReasonPhrase);
|
||||
}
|
||||
var data = await response.Content.ReadAsStringAsync();
|
||||
var envs = JsonUtility.FromJson<Environments>(data);
|
||||
var envList = envs.results.ToList();
|
||||
return envList;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<string> GetAuthToken()
|
||||
{
|
||||
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient())
|
||||
{
|
||||
var jsonString = JsonUtility.ToJson(new Token() { token = CloudProjectSettings.accessToken });
|
||||
var url = $"{m_ServicesBasePath}/api/auth/v1/genesis-token-exchange/unity/";
|
||||
var clientResponse = await client.PostAsync(url, new StringContent(jsonString, Encoding.UTF8, "application/json"));
|
||||
if (!clientResponse.IsSuccessStatusCode)
|
||||
{
|
||||
throw new Exception(clientResponse.ReasonPhrase);
|
||||
}
|
||||
var token = JsonUtility.FromJson<Token>(await clientResponse.Content.ReadAsStringAsync()).token;
|
||||
return $"Bearer {token}";
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetEnvironmentById(string id)
|
||||
{
|
||||
Environment env = environments.Where(x => x.id == id).FirstOrDefault();
|
||||
if (env != null)
|
||||
{
|
||||
currentEnvironment = env;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Unable to find environment by id");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
||||
{
|
||||
}
|
||||
|
||||
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
||||
{
|
||||
// Ensure static Group types have the correct string
|
||||
// Local
|
||||
var types = GetGroupTypesByPrefix(AddressableAssetSettings.LocalGroupTypePrefix);
|
||||
if (types == null || types.Count == 0)
|
||||
profileGroupTypes.Add(CreateBuiltInGroupType());
|
||||
else
|
||||
{
|
||||
types[0].AddOrUpdateVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath,
|
||||
AddressableAssetSettings.kLocalBuildPathValue));
|
||||
types[0].AddOrUpdateVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath,
|
||||
AddressableAssetSettings.kLocalLoadPathValue));
|
||||
}
|
||||
|
||||
// Editor Hosted
|
||||
types = GetGroupTypesByPrefix(AddressableAssetSettings.EditorHostedGroupTypePrefix);
|
||||
if (types.Count == 0)
|
||||
profileGroupTypes.Add(CreateEditorHostedGroupType());
|
||||
else
|
||||
{
|
||||
types[0].AddOrUpdateVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kBuildPath,
|
||||
AddressableAssetSettings.kRemoteBuildPathValue));
|
||||
types[0].AddOrUpdateVariable(new ProfileGroupType.GroupTypeVariable(AddressableAssetSettings.kLoadPath,
|
||||
AddressableAssetSettings.RemoteLoadPathValue));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Access Token
|
||||
/// </summary>
|
||||
private class Token
|
||||
{
|
||||
[SerializeField]
|
||||
public string token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Environment Wrapper Object
|
||||
/// </summary>
|
||||
internal class Environments
|
||||
{
|
||||
[SerializeField]
|
||||
public List<Environment> results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identity API Environment Object
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class Environment
|
||||
{
|
||||
[SerializeField]
|
||||
public string id;
|
||||
|
||||
[SerializeField]
|
||||
public string projectId;
|
||||
|
||||
[SerializeField]
|
||||
public string projectGenesisId;
|
||||
|
||||
[SerializeField]
|
||||
public string name;
|
||||
|
||||
[SerializeField]
|
||||
public bool isDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7e3976da977cb49238499ea3b4c237ae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to store path pairs and act as an abstraction between path pairs and profile variables.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ProfileGroupType
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to store path values, identified by postfix
|
||||
/// </summary>
|
||||
///
|
||||
[Serializable]
|
||||
internal class GroupTypeVariable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor for variables
|
||||
/// </summary>
|
||||
/// <param name="suffix"></param>
|
||||
/// <param name="value"></param>
|
||||
internal GroupTypeVariable(string suffix, string value)
|
||||
{
|
||||
m_Suffix = suffix;
|
||||
m_Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Postfix of a GroupTypeVariable
|
||||
/// </summary>
|
||||
///
|
||||
[SerializeField]
|
||||
internal string m_Suffix;
|
||||
|
||||
internal string Suffix
|
||||
{
|
||||
get { return m_Suffix; }
|
||||
set { m_Suffix = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specified Value
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
internal string m_Value;
|
||||
|
||||
internal string Value
|
||||
{
|
||||
get { return m_Value; }
|
||||
set { m_Value = value; }
|
||||
}
|
||||
}
|
||||
|
||||
internal const char k_PrefixSeparator = '.';
|
||||
|
||||
|
||||
[SerializeField]
|
||||
string m_GroupTypePrefix;
|
||||
|
||||
/// <summary>
|
||||
/// Common prefix used in determining a path pair
|
||||
/// </summary>
|
||||
internal string GroupTypePrefix
|
||||
{
|
||||
get { return m_GroupTypePrefix; }
|
||||
set { m_GroupTypePrefix = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Group of variables that share a common prefix
|
||||
/// </summary>
|
||||
///
|
||||
[SerializeField]
|
||||
internal List<GroupTypeVariable> m_Variables;
|
||||
|
||||
internal List<GroupTypeVariable> Variables
|
||||
{
|
||||
get { return m_Variables; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ctors for profile group type
|
||||
/// </summary>
|
||||
internal ProfileGroupType()
|
||||
{
|
||||
}
|
||||
|
||||
internal ProfileGroupType(string prefix)
|
||||
{
|
||||
m_GroupTypePrefix = prefix;
|
||||
m_Variables = new List<GroupTypeVariable>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the full variable name
|
||||
/// </summary>
|
||||
/// <param name="variable"></param>
|
||||
/// <returns>the full name of a variable</returns>
|
||||
internal string GetName(GroupTypeVariable variable)
|
||||
{
|
||||
return m_GroupTypePrefix + k_PrefixSeparator + variable.Suffix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a variable in the group
|
||||
/// </summary>
|
||||
/// <param name="variable"></param>
|
||||
/// <returns>True if the variable was added, false if the variable already exists</returns>
|
||||
internal bool AddVariable(GroupTypeVariable variable)
|
||||
{
|
||||
GroupTypeVariable exists = m_Variables.Where(ps => ps.Suffix == variable.Suffix).FirstOrDefault();
|
||||
if (exists != null)
|
||||
{
|
||||
Addressables.LogErrorFormat("{0} already exists.", GetName(variable));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Variables.Add(variable);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Adds a variable to the group, or updates the value if already exists
|
||||
internal void AddOrUpdateVariable(GroupTypeVariable variable)
|
||||
{
|
||||
foreach (GroupTypeVariable typeVariable in m_Variables)
|
||||
{
|
||||
if (typeVariable.Suffix == variable.Suffix)
|
||||
{
|
||||
typeVariable.Value = variable.Value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_Variables.Add(variable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a variable from the group
|
||||
/// </summary>
|
||||
/// <param name="variable"></param>
|
||||
internal void RemoveVariable(GroupTypeVariable variable)
|
||||
{
|
||||
GroupTypeVariable exists = m_Variables.Where(ps => ps.Suffix == variable.Suffix).FirstOrDefault();
|
||||
if (exists == null)
|
||||
{
|
||||
Addressables.LogErrorFormat("{0} does not exist.", GetName(variable));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Variables.Remove(variable);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a variable by its suffix name
|
||||
/// </summary>
|
||||
/// <param name="suffix"></param>
|
||||
/// <returns>the variable if exists, null otherwise</returns>
|
||||
internal GroupTypeVariable GetVariableBySuffix(string suffix)
|
||||
{
|
||||
return m_Variables.Where(var => var.m_Suffix == suffix).FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a group type has a certain variable
|
||||
/// </summary>
|
||||
/// <param name="groupTypeVariable">group type variable</param>
|
||||
/// <returns>true if the group type contains the variable, false otherwise</returns>
|
||||
internal bool ContainsVariable(GroupTypeVariable groupTypeVariable)
|
||||
{
|
||||
return m_Variables.Any(var => var.Suffix == groupTypeVariable.Suffix && var.Value == groupTypeVariable.Value);
|
||||
}
|
||||
|
||||
//UI magic to group the path pairs from profile variables
|
||||
internal static List<ProfileGroupType> CreateGroupTypes(AddressableAssetProfileSettings.BuildProfile buildProfile, AddressableAssetSettings settings)
|
||||
{
|
||||
Dictionary<string, ProfileGroupType> groups = new Dictionary<string, ProfileGroupType>();
|
||||
foreach (var profileEntry in settings.profileSettings.profileEntryNames)
|
||||
{
|
||||
string[] parts = profileEntry.ProfileName.Split(k_PrefixSeparator);
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
string prefix = String.Join(k_PrefixSeparator.ToString(), parts, 0, parts.Length - 1);
|
||||
string suffix = parts[parts.Length - 1];
|
||||
string profileEntryValue = buildProfile.GetValueById(profileEntry.Id);
|
||||
ProfileGroupType group;
|
||||
groups.TryGetValue(prefix, out group);
|
||||
if (group == null)
|
||||
{
|
||||
group = new ProfileGroupType(prefix);
|
||||
}
|
||||
|
||||
GroupTypeVariable variable = new GroupTypeVariable(suffix, profileEntryValue);
|
||||
group.AddVariable(variable);
|
||||
groups[prefix] = group;
|
||||
}
|
||||
}
|
||||
|
||||
List<ProfileGroupType> groupList = new List<ProfileGroupType>();
|
||||
groupList.AddRange(groups.Values.Where(group => group.IsValidGroupType()));
|
||||
return groupList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the group type is a valid
|
||||
/// </summary>
|
||||
/// <returns>True, if the group type has a prefix, a build path, and a load path, false otherwise</returns>
|
||||
internal bool IsValidGroupType()
|
||||
{
|
||||
return m_GroupTypePrefix != null && GetVariableBySuffix("BuildPath") != null && GetVariableBySuffix("LoadPath") != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c965ab7077c7542bd9130d8c2a8c3c95
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to store references to profile variables. This class is intended to be used for fields in user scripts, specifically ones that subclass AddressableAssetGroupSchema.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ProfileValueReference
|
||||
{
|
||||
[FormerlySerializedAs("m_id")]
|
||||
[SerializeField]
|
||||
string m_Id;
|
||||
|
||||
/// <summary>
|
||||
/// This delegate will be invoked when the reference profile id changes. This will NOT be invoked when the actual profile value itself changes.
|
||||
/// </summary>
|
||||
public Action<ProfileValueReference> OnValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Get the profile variable id.
|
||||
/// </summary>
|
||||
public string Id
|
||||
{
|
||||
get { return m_Id; }
|
||||
internal set { m_Id = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate the profile value using the provided settings object.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings object to evaluate with. The activeProfileId will be used.</param>
|
||||
/// <returns>The evaluated string stored in the referenced profile variable.</returns>
|
||||
public string GetValue(AddressableAssetSettings settings)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: AddressableAssetSettings object is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetValue(settings.profileSettings, settings.activeProfileId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate the profile value using the provided profile settings object and a profile id.
|
||||
/// </summary>
|
||||
/// <param name="profileSettings">The profile settings object.</param>
|
||||
/// <param name="profileId">The profile id.</param>
|
||||
/// <returns>The evaluated string stored in the referenced profile variable.</returns>
|
||||
public string GetValue(AddressableAssetProfileSettings profileSettings, string profileId)
|
||||
{
|
||||
if (profileSettings == null)
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: AddressableAssetProfileSettings object is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(profileId))
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: GetValue called with invalid profileId.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return profileSettings.EvaluateString(profileId, profileSettings.GetValueById(profileId, m_Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the profile variable name that is referenced.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings object.</param>
|
||||
/// <returns>The name of the profile variable name.</returns>
|
||||
public string GetName(AddressableAssetSettings settings)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: GetName() - AddressableAssetSettings object is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var pid = settings.profileSettings.GetProfileDataById(m_Id);
|
||||
if (pid == null)
|
||||
Debug.LogWarningFormat("ProfileValueReference: GetName() - Unable to find variable id {0} in settings object.", m_Id);
|
||||
|
||||
return pid == null ? string.Empty : pid.ProfileName;
|
||||
}
|
||||
|
||||
internal bool HasValue(AddressableAssetSettings settings)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: HasValue() - AddressableAssetSettings object is null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(settings.profileSettings.GetValueById(settings.activeProfileId, m_Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the profile variable id using the id of the variable.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings object.</param>
|
||||
/// <param name="id">The id of the profile variable to set.</param>
|
||||
/// <returns>True if the profile data is found and set, false otherwise.</returns>
|
||||
public bool SetVariableById(AddressableAssetSettings settings, string id)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: SetVariableById() - AddressableAssetSettings object is null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: SetVariableById() - invalid parameter id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.profileSettings.GetProfileDataById(id) == null)
|
||||
{
|
||||
Debug.LogWarningFormat("ProfileValueReference: SetVariableById() - Unable to find variable id {0} in settings object.", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Id = id;
|
||||
if (OnValueChanged != null)
|
||||
OnValueChanged(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the profile variable id using the name of the variable.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings object.</param>
|
||||
/// <param name="name">The name of the profile variable to set.</param>
|
||||
/// <returns>True if the profile data is found and set, false otherwise.</returns>
|
||||
public bool SetVariableByName(AddressableAssetSettings settings, string name)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: SetVariableByName() - AddressableAssetSettings object is null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
Debug.LogWarning("ProfileValueReference: SetVariableByName() - invalid parameter name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var idData = settings.profileSettings.GetProfileDataByName(name);
|
||||
if (idData == null)
|
||||
{
|
||||
Debug.LogWarningFormat("ProfileValueReference: SetVariableByName() - Unable to find variable name {0} in settings object.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Id = idData.Id;
|
||||
if (OnValueChanged != null)
|
||||
OnValueChanged(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 46f31ab199825eb488ad8f2a8f6d1a4c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,397 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace UnityEditor.AddressableAssets.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// The project configuration settings for addressables.
|
||||
/// </summary>
|
||||
public class ProjectConfigData
|
||||
{
|
||||
[Serializable]
|
||||
class ConfigSaveData
|
||||
{
|
||||
[FormerlySerializedAs("m_postProfilerEvents")]
|
||||
[SerializeField]
|
||||
internal bool postProfilerEventsInternal;
|
||||
|
||||
[FormerlySerializedAs("m_localLoadSpeed")]
|
||||
[SerializeField]
|
||||
internal long localLoadSpeedInternal = 1024 * 1024 * 10;
|
||||
|
||||
[FormerlySerializedAs("m_remoteLoadSpeed")]
|
||||
[SerializeField]
|
||||
internal long remoteLoadSpeedInternal = 1024 * 1024 * 1;
|
||||
|
||||
[FormerlySerializedAs("m_hierarchicalSearch")]
|
||||
[SerializeField]
|
||||
internal bool hierarchicalSearchInternal = true;
|
||||
|
||||
[SerializeField]
|
||||
internal int activePlayModeIndex = 0;
|
||||
|
||||
[SerializeField]
|
||||
internal bool hideSubObjectsInGroupView = false;
|
||||
|
||||
[SerializeField]
|
||||
internal bool showGroupsAsHierarchy = false;
|
||||
|
||||
[SerializeField]
|
||||
internal bool generateBuildLayout = false;
|
||||
|
||||
[SerializeField]
|
||||
internal ReportFileFormat buildLayoutReportFileFormat = ReportFileFormat.JSON;
|
||||
|
||||
[SerializeField]
|
||||
internal List<string> buildReports = new List<string>();
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
[SerializeField]
|
||||
internal bool autoOpenAddressablesReport = true;
|
||||
[SerializeField]
|
||||
internal bool userHasBeenInformedAboutBuildReportSettingPreBuild = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static ConfigSaveData s_Data;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display sub objects in the Addressables Groups window.
|
||||
/// </summary>
|
||||
public static bool ShowSubObjectsInGroupView
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return !s_Data.hideSubObjectsInGroupView;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.hideSubObjectsInGroupView = !value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to generate the bundle build layout report.
|
||||
/// </summary>
|
||||
public static bool GenerateBuildLayout
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.generateBuildLayout;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
if (s_Data.generateBuildLayout != value)
|
||||
{
|
||||
s_Data.generateBuildLayout = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
internal static bool AutoOpenAddressablesReport
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.autoOpenAddressablesReport;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
if (s_Data.autoOpenAddressablesReport != value)
|
||||
{
|
||||
s_Data.autoOpenAddressablesReport = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool UserHasBeenInformedAboutBuildReportSettingPreBuild
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.userHasBeenInformedAboutBuildReportSettingPreBuild;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
if (s_Data.userHasBeenInformedAboutBuildReportSettingPreBuild != value)
|
||||
{
|
||||
s_Data.userHasBeenInformedAboutBuildReportSettingPreBuild = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// File formats supported for the bundle build layout report.
|
||||
/// </summary>
|
||||
public enum ReportFileFormat
|
||||
{
|
||||
/// <summary>
|
||||
/// When selected, a human readable .txt build layout will be generated alongside the .json file format
|
||||
/// </summary>
|
||||
TXT,
|
||||
|
||||
/// <summary>
|
||||
/// The .json file format.
|
||||
/// </summary>
|
||||
JSON
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// File format of the bundle build layout report.
|
||||
/// </summary>
|
||||
public static ReportFileFormat BuildLayoutReportFileFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.buildLayoutReportFileFormat;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
if (s_Data.buildLayoutReportFileFormat != value)
|
||||
{
|
||||
s_Data.buildLayoutReportFileFormat = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the file paths of build reports used by the Build Reports window.
|
||||
/// </summary>
|
||||
public static List<string> BuildReportFilePaths
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.buildReports;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the filepath of a build report to be used by the Build Reports window
|
||||
/// </summary>
|
||||
/// <param name="reportFilePath">The file path to add</param>
|
||||
public static void AddBuildReportFilePath(string reportFilePath)
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.buildReports.Add(reportFilePath);
|
||||
SaveData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the build report at index from the list of build reports shown in the Build Reports window
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the build report to be removed</param>
|
||||
public static void RemoveBuildReportFilePathAtIndex(int index)
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.buildReports.RemoveAt(index);
|
||||
SaveData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the build report located at reportFilePath from the list of build reports shown in the Build Reports window
|
||||
/// </summary>
|
||||
/// <param name="reportFilePath"></param>
|
||||
public static void RemoveBuildReportFilePath(string reportFilePath)
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.buildReports.Remove(reportFilePath);
|
||||
SaveData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all build reports from the Build Reports window
|
||||
/// </summary>
|
||||
public static void ClearBuildReportFilePaths()
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.buildReports.Clear();
|
||||
SaveData();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The active play mode data builder index.
|
||||
/// </summary>
|
||||
public static int ActivePlayModeIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.activePlayModeIndex;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.activePlayModeIndex = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to post profiler events in the ResourceManager profiler window.
|
||||
/// </summary>
|
||||
public static bool PostProfilerEvents
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.postProfilerEventsInternal;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.postProfilerEventsInternal = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The local bundle loading speed used in the Simulate Groups (advanced) playmode.
|
||||
/// </summary>
|
||||
public static long LocalLoadSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.localLoadSpeedInternal;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.localLoadSpeedInternal = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The remote bundle loading speed used in the Simulate Groups (advanced) playmode.
|
||||
/// </summary>
|
||||
public static long RemoteLoadSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.remoteLoadSpeedInternal;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.remoteLoadSpeedInternal = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to allow searching for assets parsed hierarchally in the Addressables Groups window.
|
||||
/// </summary>
|
||||
public static bool HierarchicalSearch
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.hierarchicalSearchInternal;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.hierarchicalSearchInternal = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to display groups names parsed hierarchally in the Addressables Groups window.
|
||||
/// </summary>
|
||||
public static bool ShowGroupsAsHierarchy
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateData();
|
||||
return s_Data.showGroupsAsHierarchy;
|
||||
}
|
||||
set
|
||||
{
|
||||
ValidateData();
|
||||
s_Data.showGroupsAsHierarchy = value;
|
||||
SaveData();
|
||||
}
|
||||
}
|
||||
|
||||
static void ValidateData()
|
||||
{
|
||||
if (s_Data == null)
|
||||
{
|
||||
var dataPath = Path.GetFullPath(".");
|
||||
dataPath = dataPath.Replace("\\", "/");
|
||||
dataPath += "/Library/AddressablesConfig.dat";
|
||||
|
||||
if (File.Exists(dataPath))
|
||||
{
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
try
|
||||
{
|
||||
using (FileStream file = new FileStream(dataPath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
var data = bf.Deserialize(file) as ConfigSaveData;
|
||||
if (data != null)
|
||||
{
|
||||
s_Data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
//if the current class doesn't match what's in the file, Deserialize will throw. since this data is non-critical, we just wipe it
|
||||
Addressables.LogWarning("Error reading Addressable Asset project config (play mode, etc.). Resetting to default.");
|
||||
File.Delete(dataPath);
|
||||
}
|
||||
}
|
||||
|
||||
//check if some step failed.
|
||||
if (s_Data == null)
|
||||
{
|
||||
s_Data = new ConfigSaveData();
|
||||
}
|
||||
|
||||
if(s_Data.buildReports == null)
|
||||
s_Data.buildReports = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
static void SaveData()
|
||||
{
|
||||
if (s_Data == null)
|
||||
return;
|
||||
|
||||
var dataPath = Path.GetFullPath(".");
|
||||
dataPath = dataPath.Replace("\\", "/");
|
||||
dataPath += "/Library/AddressablesConfig.dat";
|
||||
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
FileStream file = File.Create(dataPath);
|
||||
|
||||
bf.Serialize(file, s_Data);
|
||||
file.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4533d6b9f915a16429e5a3143d2f7a1b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Add table
Add a link
Reference in a new issue