using System;
using System.Collections.Generic;
using UnityEngine.AddressableAssets;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
using UnityEngine.Pool;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace UnityEngine.Localization.Operations
{
///
/// Performs preloading for a single in the following order:
///
/// - BeginPreloading: Load preload resources - these are the tables that are marked with the preload tag.
/// - LoadTables: Load the tables using the resources, if any exist.
/// - LoadTableContents: Preload the tables contents, if a table implements .
///
///
///
///
class PreloadLocaleOperation : WaitForCurrentOperationAsyncOperationBase>
where TTable : DetailedLocalizationTable
where TEntry : TableEntry
{
readonly Action>> m_LoadTablesAction;
readonly Action> m_LoadTableContentsAction;
readonly Action m_FinishPreloadingAction;
readonly Action>> m_PreloadTablesCompletedAction;
LocalizedDatabase m_Database;
Locale m_Locale;
AsyncOperationHandle> m_LoadResourcesOperation;
AsyncOperationHandle> m_LoadTablesGroupOperation;
AsyncOperationHandle> m_LoadTableContentsOperation;
readonly List m_LoadTablesOperations = new List();
readonly List m_PreloadTableContentsOperations = new List();
readonly List m_ResourceLabels = new List();
float m_Progress;
protected override float Progress => m_Progress;
protected override string DebugName => $"Preload ({m_Locale}) {m_Database.GetType()}";
public static readonly ObjectPool> Pool = new ObjectPool>(
() => new PreloadLocaleOperation(), collectionCheck: false);
public PreloadLocaleOperation()
{
m_LoadTablesAction = LoadTables;
m_LoadTableContentsAction = LoadTableContents;
m_FinishPreloadingAction = FinishPreloading;
m_PreloadTablesCompletedAction = PreloadTablesCompleted;
}
public void Init(LocalizedDatabase database, Locale locale)
{
Debug.Assert(locale != null);
m_Database = database;
m_Locale = locale;
m_LoadTablesOperations.Clear();
m_PreloadTableContentsOperations.Clear();
}
protected override void Execute()
{
BeginPreloading();
}
void BeginPreloading()
{
m_Progress = 0;
var localeLabel = AddressHelper.FormatAssetLabel(m_Locale.Identifier);
m_ResourceLabels.Clear();
m_ResourceLabels.Add(localeLabel);
m_ResourceLabels.Add(LocalizationSettings.PreloadLabel);
m_LoadResourcesOperation = AddressablesInterface.LoadResourceLocationsWithLabelsAsync(m_ResourceLabels, Addressables.MergeMode.Intersection, typeof(TTable));
if (!m_LoadResourcesOperation.IsValid())
{
CompleteAndRelease(true, null);
return;
}
if (m_LoadResourcesOperation.IsDone)
{
LoadTables(m_LoadResourcesOperation);
}
else
{
CurrentOperation = m_LoadResourcesOperation;
m_LoadResourcesOperation.Completed += m_LoadTablesAction;
}
}
void LoadTables(AsyncOperationHandle> loadResourcesOperation)
{
if (loadResourcesOperation.Status != AsyncOperationStatus.Succeeded)
{
CompleteAndRelease(false, "Failed to locate preload tables for " + m_Locale);
return;
}
// Do we need to preload any tables?
if (loadResourcesOperation.Result.Count == 0)
{
m_Progress = 1;
CompleteAndRelease(true, null);
return;
}
// Load the tables
foreach (var resourceLocation in loadResourcesOperation.Result)
{
var tableOperation = AddressablesInterface.LoadTableFromLocation(resourceLocation);
m_LoadTablesOperations.Add(tableOperation);
if (tableOperation.IsDone)
LoadTableContents(tableOperation);
else
tableOperation.Completed += m_LoadTableContentsAction;
}
m_LoadTablesGroupOperation = AddressablesInterface.CreateGroupOperation(m_LoadTablesOperations);
if (m_LoadTablesGroupOperation.IsDone)
{
PreloadTablesCompleted(m_LoadTablesGroupOperation);
}
else
{
CurrentOperation = m_LoadTablesGroupOperation;
m_LoadTablesGroupOperation.Completed += m_PreloadTablesCompletedAction;
}
}
void LoadTableContents(AsyncOperationHandle operation)
{
// Update progress.
m_Progress += 1.0f / m_LoadTablesOperations.Count;
if (operation.Result == null)
return;
var table = operation.Result;
var tableCollectionName = table.TableCollectionName;
if (m_Database.TableOperations.TryGetValue((table.LocaleIdentifier, tableCollectionName), out var tableOp))
{
// We need to release this operation but if we do it now it may cause errors in Addressables so we will defer it to later.
LocalizationBehaviour.ReleaseNextFrame(operation);
// If the operation is still loading then we can leave it to continue, no need to register this operation.
if (tableOp.IsDone && !ReferenceEquals(tableOp.Result, table))
{
Debug.LogError($"A table with the same key `{tableCollectionName}` already exists. Something went wrong during preloading of {m_Locale}. Table {table} does not match {tableOp.Result}.");
return;
}
}
else
{
m_Database.RegisterCompletedTableOperation(operation);
}
if (table is IPreloadRequired preloadRequired)
{
var preloadOperation = preloadRequired.PreloadOperation;
if (!preloadOperation.IsDone)
{
m_PreloadTableContentsOperations.Add(preloadOperation);
}
}
}
void PreloadTablesCompleted(AsyncOperationHandle> obj)
{
if (m_PreloadTableContentsOperations.Count == 0)
{
CompleteAndRelease(true, null);
return;
}
m_LoadTableContentsOperation = AddressablesInterface.CreateGroupOperation(m_PreloadTableContentsOperations);
if (m_LoadTableContentsOperation.IsDone)
{
FinishPreloading(m_LoadTableContentsOperation);
}
else
{
CurrentOperation = m_LoadTableContentsOperation;
m_LoadTableContentsOperation.CompletedTypeless += m_FinishPreloadingAction;
}
}
void FinishPreloading(AsyncOperationHandle op)
{
m_Progress = 1;
CompleteAndRelease(op.Status == AsyncOperationStatus.Succeeded, null);
}
void CompleteAndRelease(bool success, string errorMsg)
{
AddressablesInterface.ReleaseAndReset(ref m_LoadResourcesOperation);
AddressablesInterface.ReleaseAndReset(ref m_LoadTablesGroupOperation);
AddressablesInterface.ReleaseAndReset(ref m_LoadTableContentsOperation);
Complete(m_Database, success, errorMsg);
}
protected override void Destroy()
{
base.Destroy();
Pool.Release(this);
}
}
}