using System; using System.Collections.Generic; using UnityEngine.Localization.Operations; using UnityEngine.Localization.Tables; using UnityEngine.Pool; using UnityEngine.ResourceManagement.AsyncOperations; namespace UnityEngine.Localization.Settings { /// /// Options for the different fallback behaviours that are available. /// public enum FallbackBehavior { /// /// Uses the value in /// when localizing a string or when localizing an asset. /// UseProjectSettings, /// /// Do not fallback. /// DontUseFallback, /// /// Attempts to use a fallback when a localized value is not found. /// UseFallback } /// /// Options for how to handle a missing translation. /// [Flags] public enum MissingTranslationBehavior { /// /// Includes the missing translation message in the translated string. /// ShowMissingTranslationMessage = 1, /// /// Prints the missing translation message using [Debug.LogWarning](https://docs.unity3d.com/ScriptReference/Debug.LogWarning.html). /// PrintWarning = 2 } /// /// Options for how to handle asynchronous operations. /// public enum AsynchronousBehaviour { /// /// Use the default behavior specified in the . /// Default, /// /// Forces the operation to complete synchronously. /// ForceSynchronous } /// /// Can be assigned to to override the default table loading through Addressables in order to provide a custom table. /// /// /// This example demonstrates how to use the to provide a custom String Table without using the Addressables system. /// This approach is particularly useful when you want to allow users to add third-party content, such as modding. /// The localization data could be loaded from an external file and then converted into a table at runtime. /// /// /// public interface ITableProvider { /// /// Provides a way to return a custom table when when attempting to load from . /// /// /// /// /// A valid table or , which will trigger the default table loading. AsyncOperationHandle ProvideTableAsync(string tableCollectionName, Locale locale) where TTable : LocalizationTable; } /// /// Gets a notification when a or completes loading. /// /// /// This example demonstrates how to use the to apply changes to a table after it has loaded but before it has been used. /// This can be beneficial when you wish to modify or add entries to a table, such as when supporting third-party content, for example modding. /// /// /// public interface ITablePostprocessor { /// /// This could be used to patch a table with updated values. /// /// The loaded or . void PostprocessTable(LocalizationTable table); } /// /// Provides common functionality for both string and asset table fetching. /// /// /// [Serializable] public abstract class LocalizedDatabase : IPreloadRequired, IReset, IDisposable where TTable : DetailedLocalizationTable where TEntry : TableEntry { /// /// Contains the results of a request. The found entry and the table the entry was found in, /// this may be different if a fall back occurred. /// public struct TableEntryResult { /// /// The entry that was resolved or if one could not be found. /// public TEntry Entry { get; } /// /// The table the entry was extracted from. When is , this contains the last table that was tried. /// public TTable Table { get; } internal TableEntryResult(TEntry entry, TTable table) { Entry = entry; Table = table; } } /// /// Preload operation. /// Loads all tables and their contents(when applicable) marked with the preload label for the selected locale. /// public AsyncOperationHandle PreloadOperation { get { #if UNITY_EDITOR // Don't preload in Editor preview if (!LocalizationSettings.Instance.IsPlayingOrWillChangePlaymode) return AddressablesInterface.ResourceManager.CreateCompletedOperation(this, null); #endif if (!m_PreloadOperationHandle.IsValid()) { var operation = PreloadDatabaseOperation.Pool.Get(); operation.Init(this); m_PreloadOperationHandle = AddressablesInterface.ResourceManager.StartOperation(operation, default); } return m_PreloadOperationHandle; } } [SerializeField] TableReference m_DefaultTableReference; [SerializeReference] ITableProvider m_CustomTableProvider; [SerializeReference] ITablePostprocessor m_CustomTablePostprocessor; [SerializeField] AsynchronousBehaviour m_AsynchronousBehaviour; [SerializeField] bool m_UseFallback; internal AsyncOperationHandle m_PreloadOperationHandle; Action m_ReleaseNextFrame; readonly Action> m_PatchTableContentsAction; readonly Action> m_RegisterSharedTableAndGuidOperationAction; readonly Action> m_RegisterCompletedTableOperationAction; internal Action ReleaseNextFrame => m_ReleaseNextFrame; // Used in place of the actual selected locale when it is still being loaded. internal static readonly LocaleIdentifier k_SelectedLocaleId = new LocaleIdentifier("selected locale placeholder"); internal Dictionary<(LocaleIdentifier localeIdentifier, string tableNameOrGuid), AsyncOperationHandle> TableOperations { get; } = new Dictionary<(LocaleIdentifier localeIdentifier, string tableNameOrGuid), AsyncOperationHandle>(); internal Dictionary> SharedTableDataOperations { get; } = new Dictionary>(); /// /// The default table to use when no table collection name is provided. /// public virtual TableReference DefaultTable { get => m_DefaultTableReference; set => m_DefaultTableReference = value; } /// /// Called when attempting to load a table, can be used to override the default table loading through Addressables in order to provide a custom table. /// /// /// This example demonstrates how to use the to provide a custom String Table without using the Addressables system. /// This approach is particularly useful when you want to allow users to add third-party content, such as modding. /// The localization data could be loaded from an external file and then converted into a table at runtime. /// /// /// public ITableProvider TableProvider { get => m_CustomTableProvider; set => m_CustomTableProvider = value; } /// /// Gets a notification when a table completes loading. /// This can be used to apply changes to a table at runtime, such as updating or creating new entries. /// /// /// This example demonstrates how to use the to apply changes to a table after it has loaded but before it has been used. /// This can be beneficial when you wish to modify or add entries to a table, such as when supporting third-party content, for example modding. /// /// /// public ITablePostprocessor TablePostprocessor { get => m_CustomTablePostprocessor; set => m_CustomTablePostprocessor = value; } /// /// Should the fallback Locale be used when a translation could not be found?. /// public bool UseFallback { get => m_UseFallback; set => m_UseFallback = value; } /// /// Options for how to handle asyncronous operations. /// public AsynchronousBehaviour AsynchronousBehaviour { get => m_AsynchronousBehaviour; set => m_AsynchronousBehaviour = value; } /// /// Creates a new instance of the database. /// public LocalizedDatabase() { m_PatchTableContentsAction = PatchTableContents; m_RegisterSharedTableAndGuidOperationAction = RegisterSharedTableAndGuidOperation; m_RegisterCompletedTableOperationAction = RegisterCompletedTableOperation; m_ReleaseNextFrame = LocalizationBehaviour.ReleaseNextFrame; } internal TableReference GetDefaultTable() { if (m_DefaultTableReference.ReferenceType == TableReference.Type.Empty) throw new Exception($"Trying to get the DefaultTable however the {GetType().Name} DefaultTable value has not been set. This can be configured in the Localization Settings."); return m_DefaultTableReference; } internal void RegisterCompletedTableOperation(AsyncOperationHandle tableOperation) { if (!tableOperation.IsDone) { tableOperation.Completed += m_RegisterCompletedTableOperationAction; return; } var table = tableOperation.Result; if (table == null) return; RegisterTableNameOperation(tableOperation, table.LocaleIdentifier, table.TableCollectionName); // If the table is already present then RegisterTableNameOperation will release the operation which may cause it to become invalid. if (tableOperation.IsValid()) RegisterSharedTableAndGuidOperation(tableOperation); } void RegisterTableNameOperation(AsyncOperationHandle tableOperation, LocaleIdentifier localeIdentifier, string tableName) { var key = (localeIdentifier, tableName); if (TableOperations.ContainsKey(key)) { // Dont hold onto this. return; } TableOperations[key] = tableOperation; if (TablePostprocessor != null) { // Patch the table contents if (tableOperation.IsDone) PatchTableContents(tableOperation); else tableOperation.Completed += m_PatchTableContentsAction; } } void RegisterSharedTableAndGuidOperation(AsyncOperationHandle tableOperation) { if (!tableOperation.IsDone) { tableOperation.Completed += m_RegisterSharedTableAndGuidOperationAction; return; } var table = tableOperation.Result; if (table == null) return; // Register the shared table data Guid. var tableNameGuid = table.SharedData.TableCollectionNameGuid; if (!SharedTableDataOperations.ContainsKey(tableNameGuid)) SharedTableDataOperations[tableNameGuid] = AddressablesInterface.ResourceManager.CreateCompletedOperation(table.SharedData, null); // Register the table via the locale identifier and guid. var localeAndGuid = (table.LocaleIdentifier, TableReference.StringFromGuid(tableNameGuid)); if (!TableOperations.ContainsKey(localeAndGuid)) { // We acquire when using the guid. AddressablesInterface.Acquire(tableOperation); TableOperations[localeAndGuid] = tableOperation; } } /// /// Returns the Default table. /// This method is asynchronous and may not have an immediate result. /// Check [IsDone](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.IsDone) to see if the data is available, /// if it is false then you can use the [Completed](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed) event to get a callback when it is finished, /// yield on the operation or call [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) /// to force the operation to complete. /// /// public AsyncOperationHandle GetDefaultTableAsync() { return GetTableAsync(GetDefaultTable()); } /// /// Returns the named table. /// This method is asynchronous and may not have an immediate result. /// Check [IsDone](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.IsDone) to see if the data is available, /// if it is false then you can use the [Completed](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed) event to get a callback when it is finished, /// yield on the operation or call [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) /// to force the operation to complete. /// /// /// Internally the following is performed when a table is requested: /// ![](../manual/images/scripting/GetTable.dot.svg) /// /// The table identifier. Can be either the name of the table or the table collection name Guid. /// The to load the table from, use null to default to . /// public virtual AsyncOperationHandle GetTableAsync(TableReference tableReference, Locale locale = null) { // Extract the Locale Id or use a placeholder if we are using the selected locale and it is not ready yet. bool localeAvailable = locale != null || LocalizationSettings.SelectedLocaleAsync.IsDone; bool useSelectedLocalePlaceholder = true; if (localeAvailable) { if (locale == null) { if (LocalizationSettings.SelectedLocaleAsync.Result == null) return AddressablesInterface.ResourceManager.CreateCompletedOperation(null, "SelectedLocale is null. Database could not get table."); locale = LocalizationSettings.SelectedLocaleAsync.Result; } useSelectedLocalePlaceholder = false; } // Do we have a cached operation already running? tableReference.Validate(); var tableIdString = tableReference.ReferenceType == TableReference.Type.Guid ? TableReference.StringFromGuid(tableReference.TableCollectionNameGuid) : tableReference.TableCollectionName; var localeId = useSelectedLocalePlaceholder ? k_SelectedLocaleId : locale.Identifier; if (TableOperations.TryGetValue((localeId, tableIdString), out var operationHandle)) { // Something has invalidated the handle, possibly caused by an unexpected release. We should remove it and try again. if (!operationHandle.IsValid()) TableOperations.Remove((localeId, tableIdString)); else return operationHandle; } // Start a new operation var operation = CreateLoadTableOperation(); operation.Init(this, tableReference, locale); operation.Dependency = LocalizationSettings.InitializationOperation; var handle = AddressablesInterface.ResourceManager.StartOperation(operation, LocalizationSettings.InitializationOperation); if (useSelectedLocalePlaceholder || tableReference.ReferenceType == TableReference.Type.Guid) { // When using a Guid we increment the reference count. // We do not increment for placeholders as we only ever have 1 reference for them, we dont share it between // table name and guid, because the register operation will use the actual selected locale and not the placeholder. // We treat the table name as default and do not increment for that one. if (!useSelectedLocalePlaceholder) AddressablesInterface.Acquire(handle); TableOperations[(localeId, tableIdString)] = handle; } else { // Register the table name and Guid RegisterTableNameOperation(handle, localeId, tableIdString); } // Register the table operation later. This will fully register everything including shared table data, table name and guid. RegisterCompletedTableOperation(handle); return handle; } /// /// Returns the named table. /// Uses [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) to force the loading to complete synchronously. /// Please note that [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) is not supported on /// [WebGL](https://docs.unity3d.com/Packages/com.unity.addressables@latest/index.html?subfolder=/manual/SynchronousAddressables.html#webgl). /// /// The table identifier. Can be either the name of the table or the table collection name Guid. /// The to load the table from, use null to default to cref="LocalizationSettings.SelectedLocale"/>. /// public virtual TTable GetTable(TableReference tableReference, Locale locale = null) => GetTableAsync(tableReference, locale).WaitForCompletion(); /// /// Preloads the selected table. If the table is an its assets will also be loaded. /// Check [IsDone](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.IsDone) to see if the data is available, /// if it is false then you can use the [Completed](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed) event to get a callback when it is finished, /// yield on the operation or call [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) /// to force the operation to complete. /// /// A reference to the table. A table reference can be either the name of the table or the table collection name Guid. /// The to use instead of the default /// public AsyncOperationHandle PreloadTables(TableReference tableReference, Locale locale = null) { // Start a new operation var operation = CreatePreloadTablesOperation(); operation.Init(this, new[] { tableReference }, locale); operation.Dependency = LocalizationSettings.InitializationOperation; var handle = AddressablesInterface.ResourceManager.StartOperation(operation, LocalizationSettings.InitializationOperation); if (LocalizationSettings.Instance.IsPlaying) handle.CompletedTypeless += ReleaseNextFrame; return handle; } /// /// Preloads the matching tables for the selected Locale. If the tables are then their assets will also be loaded. /// Check [IsDone](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.IsDone) to see if the data is available, /// if it is false then you can use the [Completed](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed) event to get a callback when it is finished, /// yield on the operation or call [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) /// to force the operation to complete. /// /// An IList of tableReferences to check for the string. /// The to use instead of the default /// /// /// This shows how to manually preload tables instead of marking them as Preload in the editor. /// /// public AsyncOperationHandle PreloadTables(IList tableReferences, Locale locale = null) { // Start a new operation var operation = CreatePreloadTablesOperation(); operation.Init(this, tableReferences, locale); operation.Dependency = LocalizationSettings.InitializationOperation; var handle = AddressablesInterface.ResourceManager.StartOperation(operation, LocalizationSettings.InitializationOperation); if (LocalizationSettings.Instance.IsPlaying) handle.CompletedTypeless += ReleaseNextFrame; return handle; } /// /// Releases all tables that are currently loaded in the database. /// This will also release any references to the providing there are no other references to it, such as different Locale versions of the table that have been loaded. /// /// The to release tables for, when all locales will be released. public void ReleaseAllTables(Locale locale = null) { using (HashSetPool.Get(out var releasedTables)) { foreach (var to in TableOperations.Values) { if (!to.IsValid()) continue; if (locale != null && to.Result.LocaleIdentifier != locale.Identifier) continue; // We may have multiple references to the table so we keep track in order to only call release once. if (to.Result != null && !releasedTables.Contains(to.Result)) { ReleaseTableContents(to.Result); releasedTables.Add(to.Result); } AddressablesInterface.Release(to); } } foreach (var shared in SharedTableDataOperations) { AddressablesInterface.SafeRelease(shared.Value); } SharedTableDataOperations.Clear(); if (m_PreloadOperationHandle.IsValid()) { //Debug.Assert(m_PreloadOperationHandle.IsDone, "Disposing an incomplete preload operation"); if (m_PreloadOperationHandle.IsDone) AddressablesInterface.Release(m_PreloadOperationHandle); m_PreloadOperationHandle = default; } TableOperations.Clear(); } /// /// Releases all references to the table that matches the and . /// This will also release any references to the providing there are no other references to it, such as different Locale versions of the table that have been loaded. /// A table is released by calling on it which decrements the ref-count. /// When a given Asset's ref-count is zero, that Asset is ready to be unloaded. /// For more information, read the Addressables section [on when memory is cleared](https://docs.unity3d.com/Packages/com.unity.addressables@latest/index.html?subfolder=/manual/MemoryManagement.html). /// /// A reference to the table. A table reference can be either the name of the table or the table collection name Guid. /// The Locale version of the table that should be unloaded. When the will be used. /// /// This shows how to release a table but prevent it from being unloaded. /// /// public void ReleaseTable(TableReference tableReference, Locale locale = null) { tableReference.Validate(); var usingSelectedLocale = locale == LocalizationSettings.SelectedLocaleAsync.Result; if (locale == null) { locale = LocalizationSettings.SelectedLocaleAsync.Result; usingSelectedLocale = true; if (locale == null) return; } // Get the shared table data SharedTableData sharedTableData; if (tableReference.ReferenceType == TableReference.Type.Guid) { if (!SharedTableDataOperations.TryGetValue(tableReference.TableCollectionNameGuid, out var sharedTableDataOperationHandle) || sharedTableDataOperationHandle.Result == null) return; sharedTableData = sharedTableDataOperationHandle.Result; } else { var nameAndLocale = (locale.Identifier, tableReference.TableCollectionName); if (!TableOperations.TryGetValue(nameAndLocale, out var operationHandleName) || operationHandleName.Result == null) return; sharedTableData = operationHandleName.Result.SharedData; } if (sharedTableData == null) return; // We may have multiple references to the table(Guid, Table name, placeholders etc) so we will iterate through and remove them all. // We also need to see if the Shared table data is still being used or if we can also release that. int sharedTableDataUsers = 0; bool removedContents = false; using (ListPool<(LocaleIdentifier localeIdentifier, string tableNameOrGuid)>.Get(out var itemsToRemove)) { foreach (var tableOperation in TableOperations) { if (!tableOperation.Value.IsValid() || tableOperation.Value.Result == null || tableOperation.Value.Result.SharedData != sharedTableData) continue; // Check locale and placeholder if (tableOperation.Key.localeIdentifier == locale.Identifier || usingSelectedLocale && tableOperation.Key.localeIdentifier == k_SelectedLocaleId) { // We only want to do this once. if (!removedContents) { ReleaseTableContents(tableOperation.Value.Result); removedContents = true; } AddressablesInterface.SafeRelease(tableOperation.Value); itemsToRemove.Add(tableOperation.Key); } else { sharedTableDataUsers++; } } // Remove the items from the dictionary foreach (var tableKey in itemsToRemove) { TableOperations.Remove(tableKey); } // If there's no other references to the shared table data then we can also remove that. if (sharedTableDataUsers == 0 && SharedTableDataOperations.TryGetValue(sharedTableData.TableCollectionNameGuid, out var sharedTableDataOperationHandle)) { AddressablesInterface.SafeRelease(sharedTableDataOperationHandle); SharedTableDataOperations.Remove(sharedTableData.TableCollectionNameGuid); } } } /// /// Returns all the tables available. /// This method is asynchronous and may not have an immediate result. /// Check [IsDone](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.IsDone) to see if the tables are available. /// if it is false then you can use the [Completed](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed) event to get a callback when it is finished, /// yield on the operation or call [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) /// to force the operation to complete.``` /// /// The to load the table from, use null to default to cref="LocalizationSettings.SelectedLocale"/>. /// public virtual AsyncOperationHandle> GetAllTables(Locale locale = null) { var operation = LoadAllTablesOperation.Pool.Get(); operation.Init(this, locale); operation.Dependency = LocalizationSettings.InitializationOperation; var handle = AddressablesInterface.ResourceManager.StartOperation(operation, LocalizationSettings.InitializationOperation); if (LocalizationSettings.Instance.IsPlaying) handle.CompletedTypeless += ReleaseNextFrame; return handle; } /// /// Checks if the table is currently loaded or not. /// /// The table identifier. Can be either the name of the table or the table collection name Guid. /// The to load the table from, use null to default to cref="LocalizationSettings.SelectedLocale"/>. /// public virtual bool IsTableLoaded(TableReference tableReference, Locale locale = null) { var tableIdString = tableReference.ReferenceType == TableReference.Type.Guid ? TableReference.StringFromGuid(tableReference.TableCollectionNameGuid) : tableReference.TableCollectionName; var localeAndName = locale != null ? (locale.Identifier, tableIdString) : (LocalizationSettings.SelectedLocaleAsync.Result.Identifier, tableIdString); if (TableOperations.TryGetValue(localeAndName, out var TableOperationHandle)) return TableOperationHandle.Status == AsyncOperationStatus.Succeeded; else return false; } internal virtual LoadTableOperation CreateLoadTableOperation() => LoadTableOperation.Pool.Get(); internal virtual PreloadTablesOperation CreatePreloadTablesOperation() => PreloadTablesOperation.Pool.Get(); /// /// Returns the entry from the requested table. A table entry will contain the localized item and metadata. /// This method is asynchronous and may not have an immediate result. /// Check [IsDone](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.IsDone) to see if the data is available, /// if it is false then you can use the [Completed](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.Completed) event to get a callback when it is finished, /// yield on the operation or call [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) /// to force the operation to complete. /// Once the Completed event has been called, during the next update, the internal operation will be returned to a pool so that it can be reused. /// If you do plan to keep hold of the handle after completion then you should call [Acquire](xref::UnityEngine.ResourceManagement.AsyncOperationHandle.Acquire) /// to prevent the operation being reused and to finally return the operation back to the pool. /// /// /// Internally the following is performed when an Entry is requested. /// First the table will be requested using . /// Once the table is loaded the entry will be extracted like so: /// ![](../manual/images/GetEntry.dot.svg) /// /// The table identifier. Can be either the name of the table or the table collection name Guid. /// A reference to the entry in the table. /// The to load the table from. Null will use . /// A Enum which determines if a Fallback should be used when no value could be found for the Locale. /// public virtual AsyncOperationHandle GetTableEntryAsync(TableReference tableReference, TableEntryReference tableEntryReference, Locale locale = null, FallbackBehavior fallbackBehavior = FallbackBehavior.UseProjectSettings) { var loadTableOperation = GetTableAsync(tableReference, locale); var getTableEntryOperation = GetTableEntryOperation.Pool.Get(); var useFallback = fallbackBehavior != FallbackBehavior.UseProjectSettings ? fallbackBehavior == FallbackBehavior.UseFallback : UseFallback; getTableEntryOperation.Init(this, loadTableOperation, tableReference, tableEntryReference, locale, useFallback, true); getTableEntryOperation.Dependency = loadTableOperation; var handle = AddressablesInterface.ResourceManager.StartOperation(getTableEntryOperation, loadTableOperation); return handle; } /// /// Returns the entry from the requested table. A table entry will contain the localized item and metadata. /// Uses [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) to force the loading to complete synchronously. /// Please note that [WaitForCompletion](xref:UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle.WaitForCompletion) is not supported on /// [WebGL](https://docs.unity3d.com/Packages/com.unity.addressables@latest/index.html?subfolder=/manual/SynchronousAddressables.html#webgl). /// /// The table identifier. Can be either the name of the table or the table collection name Guid. /// A reference to the entry in the table. /// The to load the table from. Null will use . /// A Enum which determines if a Fallback should be used when no value could be found for the Locale. /// The table entry result which contains the table public virtual TableEntryResult GetTableEntry(TableReference tableReference, TableEntryReference tableEntryReference, Locale locale = null, FallbackBehavior fallbackBehavior = FallbackBehavior.UseProjectSettings) { return GetTableEntryAsync(tableReference, tableEntryReference, locale, fallbackBehavior).WaitForCompletion(); } internal AsyncOperationHandle GetSharedTableData(Guid tableNameGuid) { if (SharedTableDataOperations.TryGetValue(tableNameGuid, out var sharedTableDataOp)) return sharedTableDataOp; sharedTableDataOp = AddressablesInterface.LoadAssetFromGUID(TableReference.StringFromGuid(tableNameGuid)); SharedTableDataOperations[tableNameGuid] = sharedTableDataOp; return sharedTableDataOp; } internal virtual void ReleaseTableContents(TTable table) {} /// /// Called before the LocaleChanged event is sent out in order to give the database a chance to prepare. /// /// public virtual void OnLocaleChanged(Locale locale) { ReleaseAllTables(); } void PatchTableContents(AsyncOperationHandle tableOperation) { // This should only be called once, after the table has loaded. It gives users the opurtunity to patch a Localized table. // For example you may want to read in some extra data from a csv file after the game has been built. if (TablePostprocessor != null && tableOperation.Result != null) TablePostprocessor.PostprocessTable(tableOperation.Result); } /// /// Resets the state of the provider by removing all the cached tables and clearing the preload operation. /// public void ResetState() { ReleaseAllTables(); } /// /// Calls .. /// void IDisposable.Dispose() { ReleaseAllTables(); } } }