fix bug class run before addressable remote
This commit is contained in:
@@ -99,7 +99,19 @@ namespace BrewMonster.Scripts
|
||||
}
|
||||
|
||||
Debug.Log("[Cuong] AddressableManager: Bootstrap gate xong — đang InitializeAsync Addressables...");
|
||||
Addressables.InitializeAsync().Completed += OnInitializeComplete;
|
||||
try
|
||||
{
|
||||
await AddressablesInitService.EnsureInitializedAsync();
|
||||
_isInitialized = true;
|
||||
_initializationTcs.TrySetResult();
|
||||
BMLogger.Log("AddressableManager: Initialized");
|
||||
Debug.Log("[Cuong] AddressableManager: InitializeAsync xong — sẵn sàng load asset.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BMLogger.LogError($"AddressableManager: Failed to initialize: {e.Message}");
|
||||
Debug.LogError($"[Cuong] AddressableManager: InitializeAsync thất bại — {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
async UniTask<bool> WaitForBootstrapGateWithTimeoutAsync()
|
||||
@@ -142,23 +154,6 @@ namespace BrewMonster.Scripts
|
||||
#endregion
|
||||
|
||||
#region private functions
|
||||
private void OnInitializeComplete(AsyncOperationHandle<IResourceLocator> handle)
|
||||
{
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
_isInitialized = true;
|
||||
_initializationTcs.TrySetResult();
|
||||
BMLogger.Log($"AddressableManager: Initialized");
|
||||
Debug.Log("[Cuong] AddressableManager: InitializeAsync xong — sẵn sàng load asset.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// print out the error
|
||||
BMLogger.LogError($"AddressableManager: Failed to initialize: {handle.OperationException?.Message} {handle.OperationException?.StackTrace}");
|
||||
Debug.LogError($"[Cuong] AddressableManager: InitializeAsync thất bại — {handle.OperationException?.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveFromReleaseAssetDictionary(string assetPath)
|
||||
{
|
||||
if (_releaseAssetTimestamps.ContainsKey(assetPath))
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace BrewMonster.Scripts
|
||||
/// </summary>
|
||||
public static async UniTask EnsureInitializedAsync()
|
||||
{
|
||||
await Addressables.InitializeAsync().ToUniTask();
|
||||
await AddressablesInitService.EnsureInitializedAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace BrewMonster.Scripts
|
||||
{
|
||||
/// <summary>
|
||||
/// Single entry for <see cref="Addressables.InitializeAsync"/> after <see cref="GameContentBootstrap"/> gate
|
||||
/// (version fetch + optional <see cref="AddressablesRuntimeUrlRewriter"/>). Prevents early init from UI code
|
||||
/// (e.g. <c>AUIManager</c>) hitting remote URLs before CDN rewrite.
|
||||
/// </summary>
|
||||
public static class AddressablesInitService
|
||||
{
|
||||
static readonly object s_lock = new();
|
||||
static UniTaskCompletionSource<bool> s_initTcs;
|
||||
static bool s_initialized;
|
||||
|
||||
public static bool IsInitialized => s_initialized;
|
||||
|
||||
/// <summary>
|
||||
/// Waits bootstrap gate, then initializes Addressables once.
|
||||
/// </summary>
|
||||
public static async UniTask EnsureInitializedAsync()
|
||||
{
|
||||
if (s_initialized)
|
||||
return;
|
||||
|
||||
UniTaskCompletionSource<bool> waiter;
|
||||
lock (s_lock)
|
||||
{
|
||||
if (s_initialized)
|
||||
return;
|
||||
|
||||
if (s_initTcs != null)
|
||||
{
|
||||
waiter = s_initTcs;
|
||||
}
|
||||
else
|
||||
{
|
||||
waiter = new UniTaskCompletionSource<bool>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sync wait for legacy <c>Init()</c> paths. Prefer async when possible.
|
||||
/// </summary>
|
||||
public static void EnsureInitializedBlocking()
|
||||
{
|
||||
EnsureInitializedAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,7 +236,15 @@ namespace BrewMonster.Scripts
|
||||
AddressablesRuntimeUrlRewriter.InstallPrefixRewrite(
|
||||
_bakedRemoteUrlPrefixForRewrite,
|
||||
fetch.AssetsBaseUrl);
|
||||
Debug.Log("[Cuong] GameContentBootstrap: Đã gắn URL rewrite cho remote CDN.");
|
||||
Debug.Log(
|
||||
$"[Cuong] GameContentBootstrap: URL rewrite | from={_bakedRemoteUrlPrefixForRewrite.Trim()} → to={fetch.AssetsBaseUrl.Trim()}");
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(fetch.AssetsBaseUrl) &&
|
||||
string.IsNullOrWhiteSpace(_bakedRemoteUrlPrefixForRewrite))
|
||||
{
|
||||
Debug.LogWarning(
|
||||
"[Cuong] GameContentBootstrap: assetsBaseUrl có nhưng _bakedRemoteUrlPrefixForRewrite trống — " +
|
||||
"catalog/bundle vẫn dùng URL bake trong build (vd CDN cũ). Điền prefix URL lúc build.");
|
||||
}
|
||||
|
||||
AllowAddressablesInit("version-and-url-ready");
|
||||
@@ -264,8 +272,18 @@ namespace BrewMonster.Scripts
|
||||
var catalog = await AddressablesCatalogUpdater.CheckAndUpdateCatalogsIfNeededAsync(false);
|
||||
if (!catalog.Success)
|
||||
{
|
||||
Debug.LogError($"[Cuong] GameContentBootstrap: Catalog update thất bại — {catalog.Error?.Message}");
|
||||
return new BootstrapResult(false, true, catalog.Error?.Message ?? "Catalog update failed", fetch.ContentVersion);
|
||||
var errMsg = catalog.Error?.Message ?? "Catalog update failed";
|
||||
Debug.LogError($"[Cuong] GameContentBootstrap: Catalog update thất bại — {errMsg}");
|
||||
if (IsLikelyRemoteTlsOrNetworkError(errMsg))
|
||||
{
|
||||
Debug.LogError(
|
||||
"[Cuong] GameContentBootstrap: Gợi ý — SSL CA / mạng trên mobile. " +
|
||||
"Kiểm tra chuỗi chứng chỉ CDN; đảm bảo _bakedRemoteUrlPrefixForRewrite khớp host trong catalog build " +
|
||||
"(vd https://prefect-world-asset....wcsapi.com/) và assetsBaseUrl trỏ CDN HTTPS hợp lệ. " +
|
||||
"Không gọi Addressables.InitializeAsync trước bootstrap (AUIManager đã dùng AddressablesInitService).");
|
||||
}
|
||||
|
||||
return new BootstrapResult(false, true, errMsg, fetch.ContentVersion);
|
||||
}
|
||||
Debug.Log("[Cuong] GameContentBootstrap: Catalog OK.");
|
||||
|
||||
@@ -402,6 +420,16 @@ namespace BrewMonster.Scripts
|
||||
return s.Trim();
|
||||
}
|
||||
|
||||
static bool IsLikelyRemoteTlsOrNetworkError(string message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return false;
|
||||
return message.IndexOf("SSL", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
message.IndexOf("certificate", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
message.IndexOf("ConnectionError", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||
message.IndexOf("unable to load from url", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
}
|
||||
|
||||
public readonly struct BootstrapResult
|
||||
{
|
||||
public BootstrapResult(bool success, bool didContentWork, string errorMessage, string serverContentVersion)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BrewMonster.Scripts;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
@@ -282,8 +283,8 @@ namespace BrewMonster.UI
|
||||
{
|
||||
try
|
||||
{
|
||||
// Initialize Addressables if not already initialized (Unity-safe)
|
||||
Addressables.InitializeAsync().WaitForCompletion();
|
||||
// Wait for GameContentBootstrap gate + URL rewrite, then single shared init.
|
||||
AddressablesInitService.EnsureInitializedBlocking();
|
||||
|
||||
// Load using Addressables directly with WaitForCompletion (Unity-safe, won't deadlock)
|
||||
// This matches the pattern used in EC_Game.cs
|
||||
|
||||
Reference in New Issue
Block a user