/// <summary>
        /// Remove the asset mapping from the table entry and also cleans up the Addressables if necessary.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="entryReference"></param>
        /// <param name="createUndo"></param>
        public void RemoveAssetFromTable(AssetTable table, TableEntryReference entryReference, bool createUndo = false)
        {
            using (new UndoScope("Remove asset from table", createUndo))
            {
                // Clear the asset but keep the key
                var tableEntry = table.GetEntryFromReference(entryReference);
                if (tableEntry == null)
                {
                    return;
                }

                var removedAssetGuid = tableEntry.Guid;
                tableEntry.Guid = string.Empty;

                var aaSettings = LocalizationEditorSettings.Instance.GetAddressableAssetSettings(false);
                if (aaSettings == null)
                {
                    return;
                }

                EditorUtility.SetDirty(table);
                EditorUtility.SetDirty(table.SharedData);

                RemoveEntryAssetType(tableEntry.KeyId, table.LocaleIdentifier.Code);

                // If the entry has metadata then we will leave an empty entry otherwise we just remove the whole thing.
                if (tableEntry.MetadataEntries.Count == 0)
                {
                    table.RemoveEntry(tableEntry.KeyId);
                }

                // Determine if the asset is being referenced by any entries or tables with the same locale, if not then we can
                // remove the locale label and if no other labels exist also remove the asset from the Addressables system.
                var assetTableCollections = LocalizationEditorSettings.GetAssetTableCollections();
                foreach (var collection in assetTableCollections)
                {
                    if (collection.GetTable(table.LocaleIdentifier) is AssetTable tableWithMatchingLocaleId && tableWithMatchingLocaleId.ContainsValue(removedAssetGuid))
                    {
                        // The asset is referenced elsewhere by a table with the same Locale so we can not remove the locale label or asset.
                        return;
                    }
                }

                // Remove the locale label for this asset
                var assetEntry = aaSettings.FindAssetEntry(removedAssetGuid);
                if (assetEntry != null)
                {
                    if (createUndo)
                    {
                        Undo.RecordObject(assetEntry.parentGroup, "Remove asset from table");
                    }

                    var assetLabel = AddressHelper.FormatAssetLabel(table.LocaleIdentifier);
                    assetEntry.SetLabel(assetLabel, false);
                    UpdateAssetGroup(aaSettings, assetEntry, createUndo);
                }

                LocalizationEditorSettings.EditorEvents.RaiseAssetTableEntryRemoved(this, table, tableEntry, removedAssetGuid);
            }
        }
        /// <summary>
        /// Add a localized asset to the asset table.
        /// This function will ensure the localization system adds the asset to the Addressables system and sets the asset up for use.
        /// </summary>
        /// <param name="table">The table to add the asset to, must be part of the collection.</param>
        /// <param name="entryReference">The table entry Key or Key Id.</param>
        /// <param name="asset">The asset to add.</param>
        /// <param name="createUndo">Should an undo operation be created?</param>
        public virtual void AddAssetToTable(AssetTable table, TableEntryReference entryReference, Object asset, bool createUndo = false)
        {
            if (table == null)
            {
                throw new ArgumentNullException(nameof(table), "Can not add asset to null table");
            }

            if (asset == null)
            {
                throw new ArgumentNullException(nameof(asset), "Can not add null asset to table");
            }

            if (!ContainsTable(table))
            {
                throw new Exception("The table does not belong to this collection.");
            }

            if (!EditorUtility.IsPersistent(table))
            {
                throw new AssetNotPersistentException(table);
            }

            if (!EditorUtility.IsPersistent(asset))
            {
                throw new AssetNotPersistentException(asset);
            }

            // Add the asset to the Addressables system and setup the table with the key to guid mapping.
            var aaSettings = LocalizationEditorSettings.Instance.GetAddressableAssetSettings(true);

            if (aaSettings == null)
            {
                return;
            }

            var undoGroup = Undo.GetCurrentGroup();

            if (createUndo)
            {
                Undo.RecordObject(aaSettings, "Add asset to table");
            }

            // Remove the old asset first
            var assetGuid  = LocalizationEditorSettings.Instance.GetAssetGuid(asset);
            var tableEntry = table.GetEntryFromReference(entryReference);

            if (tableEntry != null)
            {
                if (tableEntry.Guid == assetGuid)
                {
                    return;
                }

                RemoveAssetFromTable(table, entryReference, createUndo);
            }

            // Has the asset already been added? Perhaps it is being used by multiple tables or the user has added it manually.
            var entry      = aaSettings.FindAssetEntry(assetGuid);
            var entryLabel = AddressHelper.FormatAssetLabel(table.LocaleIdentifier.Code);

            aaSettings.AddLabel(entryLabel);

            if (entry == null)
            {
                var group = LocalizationEditorSettings.Instance.GetGroup(aaSettings, FormatAssetTableCollectionName(table.LocaleIdentifier), true, createUndo);

                if (createUndo)
                {
                    Undo.RecordObject(group, "Add asset to table");
                }

                entry = aaSettings.CreateOrMoveEntry(assetGuid, group, true);
                entry.SetLabel(entryLabel, true);
                entry.address = LocalizationEditorSettings.Instance.FindUniqueAssetAddress(asset.name);
            }
            else
            {
                Undo.RecordObject(entry.parentGroup, "Add asset to table");
                entry.SetLabel(entryLabel, true);
                UpdateAssetGroup(aaSettings, entry, createUndo);
            }

            // Update the table
            if (createUndo)
            {
                Undo.RecordObject(table, "Add asset to table");
                Undo.RecordObject(table.SharedData, "Add asset to table");
            }
            //else // Asset changes are not being saved correctly at the moment when using Undo. (LOC-82)
            {
                EditorUtility.SetDirty(table);
                EditorUtility.SetDirty(table.SharedData);
            }

            tableEntry = table.AddEntryFromReference(entryReference, assetGuid);

            // Update type metadata
            AssetTypeMetadata entryMetadata = null;
            AssetTypeMetadata typeMetadata  = null;
            var assetType = asset.GetType();

            // We cant use a foreach here as we are sometimes inside of a loop and exceptions will be thrown (Collection was modified).
            for (int i = 0; i < table.SharedData.Metadata.MetadataEntries.Count; ++i)
            {
                var md = table.SharedData.Metadata.MetadataEntries[i];
                if (md is AssetTypeMetadata at)
                {
                    if (at.Contains(tableEntry.KeyId))
                    {
                        if (!at.Type.IsAssignableFrom(assetType))
                        {
                            tableEntry.RemoveSharedMetadata(at);

                            // Are other tables still using the type for the same id?
                            if (at.Contains(tableEntry.KeyId))
                            {
                                var name = SharedData.GetEntry(tableEntry.KeyId);
                                Debug.LogWarning($"Table entry {name}({tableEntry.KeyId}) contains mixed types. Both {at.Type} and {assetType} are used.");
                            }
                        }
                        else
                        {
                            entryMetadata = at;
                            break;
                        }
                    }

                    if (at.Type == assetType)
                    {
                        typeMetadata = at;
                        break;
                    }
                }
            }
            var foundMetadata = entryMetadata ?? typeMetadata;

            if (foundMetadata == null)
            {
                foundMetadata = new AssetTypeMetadata()
                {
                    Type = assetType
                };
            }
            tableEntry.AddSharedMetadata(foundMetadata);

            if (createUndo)
            {
                Undo.CollapseUndoOperations(undoGroup);
            }

            LocalizationEditorSettings.EditorEvents.RaiseAssetTableEntryAdded(this, table, tableEntry);
        }
        /// <summary>
        /// Remove the asset mapping from the table entry and also cleans up the Addressables if necessary.
        /// </summary>
        /// <param name="table"></param>
        /// <param name="entryReference"></param>
        /// <param name="createUndo"></param>
        public void RemoveAssetFromTable(AssetTable table, TableEntryReference entryReference, bool createUndo = false)
        {
            var undoGroup = Undo.GetCurrentGroup();

            if (createUndo)
            {
                Undo.RecordObject(table, "Remove asset from table");            // We modify the table entry.
                Undo.RecordObject(table.SharedData, "Remove asset from table"); // We modify the shared table metadata.
            }
            //else // Asset changes are not being saved correctly at the moment when using Undo. (LOC-82)
            {
                EditorUtility.SetDirty(table);
                EditorUtility.SetDirty(table.SharedData);
            }

            // Clear the asset but keep the key
            var tableEntry = table.GetEntryFromReference(entryReference);

            if (tableEntry == null)
            {
                return;
            }

            var removedAssetGuid = tableEntry.Guid;

            tableEntry.Guid = string.Empty;

            var aaSettings = LocalizationEditorSettings.Instance.GetAddressableAssetSettings(false);

            if (aaSettings == null)
            {
                return;
            }

            // Update type metadata
            // We cant use a foreach here as we are sometimes inside of a loop and exceptions will be thrown (Collection was modified).
            for (int i = 0; i < table.SharedData.Metadata.MetadataEntries.Count; ++i)
            {
                var md = table.SharedData.Metadata.MetadataEntries[i];
                if (md is AssetTypeMetadata at)
                {
                    if (at.Contains(tableEntry.KeyId))
                    {
                        tableEntry.RemoveSharedMetadata(at);
                    }
                }
            }

            // If the entry has metadata then we will leave an empty entry otherwise we just remove the whole thing.
            if (tableEntry.MetadataEntries.Count == 0)
            {
                table.RemoveEntry(tableEntry.KeyId);
            }

            // Determine if the asset is being referenced by any entries or tables with the same locale, if not then we can
            // remove the locale label and if no other labels exist also remove the asset from the Addressables system.
            var assetTableCollections = LocalizationEditorSettings.GetAssetTableCollections();

            foreach (var collection in assetTableCollections)
            {
                var tableWithMatchingLocaleId = collection.GetTable(table.LocaleIdentifier) as AssetTable;
                if (tableWithMatchingLocaleId == null)
                {
                    continue;
                }

                if (tableWithMatchingLocaleId.ContainsValue(removedAssetGuid))
                {
                    // The asset is referenced elsewhere so we can not remove the label or asset.
                    return;
                }
            }

            // Remove the locale label for this asset
            var assetEntry = aaSettings.FindAssetEntry(removedAssetGuid);

            if (assetEntry != null)
            {
                if (createUndo)
                {
                    Undo.RecordObject(assetEntry.parentGroup, "Remove asset from table");
                }

                var assetLabel = AddressHelper.FormatAssetLabel(table.LocaleIdentifier);
                assetEntry.SetLabel(assetLabel, false);
                UpdateAssetGroup(aaSettings, assetEntry, createUndo);
            }

            if (createUndo)
            {
                Undo.CollapseUndoOperations(undoGroup);
            }

            LocalizationEditorSettings.EditorEvents.RaiseAssetTableEntryRemoved(this, table, tableEntry, removedAssetGuid);
        }
Exemple #4
0
        public void IsLocaleLabel_WorksWithLabelsGeneratedUsing_FormatAssetLabel(Locale locale)
        {
            var label = AddressHelper.FormatAssetLabel(locale.Identifier);

            Assert.IsTrue(AddressHelper.IsLocaleLabel(label), "Expected the Addressables Locale label to be recognized by IsLocaleLabel.");
        }
Exemple #5
0
        public void LocaleLabelToId_WorksWithLabelsGeneratedUsing_FormatAssetLabel(Locale locale)
        {
            var label = AddressHelper.FormatAssetLabel(locale.Identifier);

            LocaleLabelToId_CorrectlyConvertsLabel(label, locale.Identifier.Code);
        }
        protected virtual void Analyze(AddressableAssetSettings settings, GroupResolver resolver)
        {
            try
            {
                EditorUtility.DisplayProgressBar(ruleName, "Finding Tables", 0);
                var tables = AssetDatabase.FindAssets($"t:{typeof(TTable).Name}");

                // Collate the groups so we can check them at the end.
                var groups = new HashSet <AddressableAssetGroup>();

                for (var i = 0; i < tables.Length; ++i)
                {
                    var progress = i / (float)tables.Length;

                    var guid  = tables[i];
                    var entry = settings.FindAssetEntry(guid);
                    var path  = AssetDatabase.GUIDToAssetPath(guid);
                    var table = AssetDatabase.LoadAssetAtPath <TTable>(path);
                    var label = $"{table} - {path}";

                    EditorUtility.DisplayProgressBar(ruleName, $"Checking Table {path}", progress);

                    var collection = LocalizationEditorSettings.GetCollectionForSharedTableData(table.SharedData);
                    if (collection == null)
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{table} - {path}:Loose Table.",
                            severity   = MessageType.Info,
                            // TODO: Create collection for it?
                        });
                        continue;
                    }

                    CheckContents(table, label, settings, collection);

                    if (entry == null)
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{label}:Not Marked as Addressable",
                            severity   = MessageType.Error,
                            FixAction  = () =>
                            {
                                collection.AddTable(table);
                                collection.AddSharedTableDataToAddressables();
                            }
                        });
                        continue;
                    }

                    groups.Add(entry.parentGroup);

                    // Group Name
                    var groupName = resolver.GetExpectedGroupName(new[] { table.LocaleIdentifier }, table, settings);
                    if (entry.parentGroup.Name != groupName)
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{label}:Incorrect Group:Expected `{groupName}` but was `{entry.parentGroup.Name}`",
                            severity   = MessageType.Warning,
                            FixAction  = () => resolver.AddToGroup(table, new[] { table.LocaleIdentifier }, settings, false)
                        });
                    }

                    // Label
                    var expectedLabel = AddressHelper.FormatAssetLabel(table.LocaleIdentifier);
                    if (!entry.labels.Contains(expectedLabel))
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{label}:Missing Locale label.",
                            severity   = MessageType.Warning,
                            FixAction  = () => entry.SetLabel(expectedLabel, true, true)
                        });
                    }

                    // Address
                    var expectedAddress = AddressHelper.GetTableAddress(table.TableCollectionName, table.LocaleIdentifier);
                    if (!entry.labels.Contains(expectedLabel))
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{label}:Incorrect Address:Expected `{expectedAddress}` but was `{entry.address}`",
                            severity   = MessageType.Error,
                            FixAction  = () => entry.address = expectedAddress
                        });
                    }

                    // Shared Table Data
                    var sharedGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(table.SharedData));
                    var g          = new Guid(sharedGuid);
                    if (table.SharedData.TableCollectionNameGuid != g)
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{label}:Incorrect Shared Table Guid:Expected {g} but was {table.SharedData.TableCollectionNameGuid}",
                            severity   = MessageType.Error,
                            FixAction  = () =>
                            {
                                table.SharedData.TableCollectionNameGuid = g;
                                EditorUtility.SetDirty(table.SharedData);
                            }
                        });
                    }

                    var sharedEntry = settings.FindAssetEntry(sharedGuid);
                    if (sharedEntry == null)
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{label}:Shared Table Not Marked as Addressable",
                            severity   = MessageType.Warning,
                            FixAction  = () => resolver.AddToGroup(table.SharedData, null, settings, false)
                        });
                        continue;
                    }

                    groups.Add(sharedEntry.parentGroup);

                    // Shared Group Name
                    var sharedGroupName = resolver.GetExpectedGroupName(null, table.SharedData, settings);
                    if (sharedEntry.parentGroup.Name != sharedGroupName)
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{label}:Incorrect Shared Table Data Group:Expected `{sharedGroupName}` but was `{sharedEntry.parentGroup.Name}`",
                            severity   = MessageType.Warning,
                            FixAction  = () => resolver.AddToGroup(table.SharedData, null, settings, false)
                        });
                    }

                    var expectedSharedGroupName = resolver.GetExpectedGroupName(null, table.SharedData, settings);
                    if (sharedEntry.parentGroup.Name != expectedSharedGroupName)
                    {
                        m_Results.Add(new TableResult
                        {
                            resultName = $"{label}:Incorrect Group:Expected `{expectedSharedGroupName}` but was `{sharedEntry.parentGroup.Name}`",
                            severity   = MessageType.Warning,
                            FixAction  = () => resolver.AddToGroup(table.SharedData, null, settings, false)
                        });
                    }
                }

                if (groups.Count > 0)
                {
                    foreach (var g in groups)
                    {
                        if (g.Schemas.Count == 0 || g.Schemas.All(s => s == null))
                        {
                            m_Results.Add(new TableResult
                            {
                                resultName = $"{g.Name}:Addressables Group Contains No Schemas",
                                severity   = MessageType.Error,
                                FixAction  = () =>
                                {
                                    g.AddSchema <BundledAssetGroupSchema>();
                                    g.AddSchema <ContentUpdateGroupSchema>();
                                }
                            });
                        }
                    }
                }
            }
            finally
            {
                EditorUtility.ClearProgressBar();
            }
        }
        /// <summary>
        /// Add a localized asset to the asset table.
        /// This function will ensure the localization system adds the asset to the Addressables system and sets the asset up for use.
        /// </summary>
        /// <param name="table">The table to add the asset to, must be part of the collection.</param>
        /// <param name="entryReference">The table entry Key or Key Id.</param>
        /// <param name="asset">The asset to add.</param>
        /// <param name="createUndo">Should an undo operation be created?</param>
        public virtual void AddAssetToTable(AssetTable table, TableEntryReference entryReference, Object asset, bool createUndo = false)
        {
            if (table == null)
            {
                throw new ArgumentNullException(nameof(table), "Can not add asset to null table");
            }

            if (asset == null)
            {
                throw new ArgumentNullException(nameof(asset), "Can not add null asset to table");
            }

            if (!ContainsTable(table))
            {
                throw new Exception("The table does not belong to this collection.");
            }

            if (!EditorUtility.IsPersistent(table))
            {
                throw new AssetNotPersistentException(table);
            }

            if (!EditorUtility.IsPersistent(asset))
            {
                throw new AssetNotPersistentException(asset);
            }

            // Add the asset to the Addressables system and setup the table with the key to guid mapping.
            var aaSettings = LocalizationEditorSettings.Instance.GetAddressableAssetSettings(true);

            if (aaSettings == null)
            {
                return;
            }

            using (new UndoScope("Add asset to table", createUndo))
            {
                if (createUndo)
                {
                    Undo.RecordObject(aaSettings, "Add asset to table");
                }

                // Remove the old asset first
                var assetGuid  = LocalizationEditorSettings.Instance.GetAssetGuid(asset);
                var tableEntry = table.GetEntryFromReference(entryReference);
                if (tableEntry != null)
                {
                    if (tableEntry.Guid != assetGuid)
                    {
                        RemoveAssetFromTable(table, entryReference, createUndo);
                    }
                }

                // Has the asset already been added? Perhaps it is being used by multiple tables or the user has added it manually.
                var entry      = aaSettings.FindAssetEntry(assetGuid);
                var entryLabel = AddressHelper.FormatAssetLabel(table.LocaleIdentifier.Code);
                aaSettings.AddLabel(entryLabel);

                if (entry == null)
                {
                    entry = AddressableGroupRules.AddAssetToGroup(asset, new[] { table.LocaleIdentifier }, aaSettings, createUndo);
                    entry.SetLabel(entryLabel, true, true);
                    entry.address = LocalizationEditorSettings.Instance.FindUniqueAssetAddress(asset.name);
                }
                else
                {
                    if (createUndo)
                    {
                        Undo.RecordObject(entry.parentGroup, "Add asset to table");
                    }
                    entry.SetLabel(entryLabel, true, true);
                    UpdateAssetGroup(aaSettings, entry, createUndo);
                }

                if (createUndo)
                {
                    Undo.RecordObjects(new Object[] { table, table.SharedData }, "Add asset to table");
                }

                EditorUtility.SetDirty(table);
                EditorUtility.SetDirty(table.SharedData);

                tableEntry = table.AddEntryFromReference(entryReference, assetGuid);
                SetEntryAssetType(tableEntry.KeyId, asset.GetType(), table.LocaleIdentifier.Code);
                LocalizationEditorSettings.EditorEvents.RaiseAssetTableEntryAdded(this, table, tableEntry);
            }
        }