using System; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; namespace BrewMonster.Scripts { /// /// Single entry for after gate /// (version fetch + optional ). Prevents early init from UI code /// (e.g. AUIManager) hitting remote URLs before CDN rewrite. /// public static class AddressablesInitService { static readonly object s_lock = new(); static UniTaskCompletionSource s_initTcs; static bool s_initialized; public static bool IsInitialized => s_initialized; /// /// Waits bootstrap gate, then initializes Addressables once. /// public static async UniTask EnsureInitializedAsync() { if (s_initialized) return; UniTaskCompletionSource waiter; lock (s_lock) { if (s_initialized) return; if (s_initTcs != null) { waiter = s_initTcs; } else { waiter = new UniTaskCompletionSource(); s_initTcs = waiter; } } if (waiter != s_initTcs) { await waiter.Task; return; } try { await GameContentBootstrap.WaitForPreAddressablesSetupIfAnyAsync(); Debug.Log("[Cuong] AddressablesInitService: Bootstrap gate OK — InitializeAsync..."); var handle = Addressables.InitializeAsync(); await handle.ToUniTask(); if (handle.Status != AsyncOperationStatus.Succeeded) { var msg = handle.OperationException?.Message ?? "InitializeAsync failed"; waiter.TrySetException(new InvalidOperationException(msg)); lock (s_lock) s_initTcs = null; throw handle.OperationException ?? new InvalidOperationException(msg); } s_initialized = true; waiter.TrySetResult(true); Debug.Log("[Cuong] AddressablesInitService: InitializeAsync xong."); } catch (Exception ex) { if (!waiter.Task.Status.IsCompleted()) waiter.TrySetException(ex); lock (s_lock) s_initTcs = null; throw; } } /// /// Sync wait for legacy Init() paths. Prefer async when possible. /// public static void EnsureInitializedBlocking() { EnsureInitializedAsync().GetAwaiter().GetResult(); } } }