From 8cbc7777644b456b055e16c57c17b584190440f1 Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Fri, 22 May 2026 18:42:19 +0700 Subject: [PATCH] add debug --- .../Addressable/AddressablesLabelDebug.cs | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 Assets/PerfectWorld/Scripts/Addressable/AddressablesLabelDebug.cs diff --git a/Assets/PerfectWorld/Scripts/Addressable/AddressablesLabelDebug.cs b/Assets/PerfectWorld/Scripts/Addressable/AddressablesLabelDebug.cs new file mode 100644 index 0000000000..4576e3e869 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Addressable/AddressablesLabelDebug.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.AddressableAssets; +using UnityEngine.AddressableAssets.ResourceLocators; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceLocations; +using UnityEngine.ResourceManagement.ResourceProviders; + +namespace BrewMonster.Scripts +{ + /// + /// Runtime diagnostics for Addressables labels (e.g. RemoteContent) — log with [Cuong] prefix. + /// + public static class AddressablesLabelDebug + { + const string LogPrefix = "[Cuong] AddressablesLabelDebug:"; + + /// + /// After , logs locators, label lookup (exact + case variants), + /// and sample remote InternalIds. Labels are case-sensitive in Addressables. + /// + public static async UniTask LogLabelDiagnosticsAsync(string primaryLabel, int maxSampleLocations = 5) + { + if (string.IsNullOrWhiteSpace(primaryLabel)) + { + Debug.LogWarning($"{LogPrefix} primaryLabel is empty — skip."); + return; + } + + var label = primaryLabel.Trim(); + var sb = new StringBuilder(2048); + sb.AppendLine($"{LogPrefix} === Label diagnostics (runtime catalog) ==="); + sb.AppendLine($"{LogPrefix} Primary label (exact, case-sensitive): \"{label}\""); + + int locatorCount = 0; + if (Addressables.ResourceLocators != null) + { + foreach (IResourceLocator locator in Addressables.ResourceLocators) + { + locatorCount++; + var id = locator?.LocatorId ?? "(null)"; + var keyCount = 0; + if (locator?.Keys != null) + { + foreach (var _ in locator.Keys) + keyCount++; + } + + sb.AppendLine($"{LogPrefix} Locator #{locatorCount}: id={id}, keys~={keyCount}"); + } + } + + if (locatorCount == 0) + sb.AppendLine($"{LogPrefix} WARNING: No ResourceLocators — catalog chưa load hoặc init thất bại."); + + await LogLabelLookupAsync(sb, label, maxSampleLocations); + + var wrongCase = char.IsUpper(label[0]) + ? char.ToLowerInvariant(label[0]) + label.Substring(1) + : char.ToUpperInvariant(label[0]) + label.Substring(1); + if (!string.Equals(wrongCase, label, StringComparison.Ordinal)) + { + sb.AppendLine($"{LogPrefix} --- Case mismatch test (should be 0 locations if case matters) ---"); + await LogLabelLookupAsync(sb, wrongCase, 2, suffix: " [wrong case]"); + } + + await LogAllCatalogLabelsAsync(sb, label); + + sb.AppendLine($"{LogPrefix} Ghi chú: Label trong Addressables Groups ≠ file trên CDN. " + + "CDN có catalog_*.json + bundle; label nằm TRONG catalog JSON sau khi build. " + + "Group Include In Build = false → entry chỉ có sau khi remote catalog tải thành công."); + Debug.Log(sb.ToString()); + } + + static async UniTask LogLabelLookupAsync( + StringBuilder sb, + string label, + int maxSampleLocations, + string suffix = "") + { + AsyncOperationHandle> handle = default; + try + { + handle = Addressables.LoadResourceLocationsAsync(label, typeof(UnityEngine.Object)); + await handle.ToUniTask(); + + if (handle.Status != AsyncOperationStatus.Succeeded) + { + sb.AppendLine( + $"{LogPrefix} LoadResourceLocationsAsync(\"{label}\"){suffix} FAILED: {handle.OperationException?.Message}"); + return; + } + + var locations = handle.Result; + int count = locations?.Count ?? 0; + sb.AppendLine($"{LogPrefix} Label \"{label}\"{suffix} → {count} location(s)."); + + if (count == 0) + return; + + int remoteCount = 0; + int sample = Mathf.Min(maxSampleLocations, count); + for (int i = 0; i < count; i++) + { + var loc = locations[i]; + if (loc == null) + continue; + + bool isRemote = IsRemoteLocation(loc); + if (isRemote) + remoteCount++; + + if (i < sample) + { + sb.AppendLine( + $"{LogPrefix} [{i}] remote={isRemote} provider={loc.ProviderId} id={Truncate(loc.InternalId, 120)}"); + } + } + + sb.AppendLine($"{LogPrefix} … remote locations: {remoteCount}/{count} (rest omitted)."); + } + catch (Exception e) + { + sb.AppendLine($"{LogPrefix} Label \"{label}\"{suffix} exception: {e.GetType().Name}: {e.Message}"); + } + finally + { + if (handle.IsValid()) + Addressables.Release(handle); + } + } + + static async UniTask LogAllCatalogLabelsAsync(StringBuilder sb, string highlightLabel) + { + AsyncOperationHandle> handle = default; + try + { + handle = Addressables.GetLabels(); + await handle.ToUniTask(); + + if (handle.Status != AsyncOperationStatus.Succeeded) + { + sb.AppendLine($"{LogPrefix} GetLabels() failed: {handle.OperationException?.Message}"); + return; + } + + var labels = handle.Result; + int n = labels?.Count ?? 0; + sb.AppendLine($"{LogPrefix} GetLabels() → {n} label(s) in loaded catalog(s):"); + + if (n == 0) + { + sb.AppendLine($"{LogPrefix} (empty — catalog build cũ hoặc chưa có remote catalog)"); + return; + } + + bool foundHighlight = false; + for (int i = 0; i < n; i++) + { + var l = labels[i]; + if (string.IsNullOrEmpty(l)) + continue; + + bool match = string.Equals(l, highlightLabel, StringComparison.Ordinal); + bool matchIgnoreCase = string.Equals(l, highlightLabel, StringComparison.OrdinalIgnoreCase); + if (match) + foundHighlight = true; + + string flag = match ? " <-- EXACT MATCH" : + matchIgnoreCase ? " <-- same letters, different case" : ""; + sb.AppendLine($"{LogPrefix} [{i}] \"{l}\"{flag}"); + } + + if (!foundHighlight) + { + sb.AppendLine( + $"{LogPrefix} WARNING: \"{highlightLabel}\" NOT in GetLabels() list — " + + "InvalidKeyException khi GetDownloadSizeAsync/DownloadDependencies là đúng."); + } + } + catch (Exception e) + { + sb.AppendLine($"{LogPrefix} GetLabels() exception: {e.GetType().Name}: {e.Message}"); + } + finally + { + if (handle.IsValid()) + Addressables.Release(handle); + } + } + + static bool IsRemoteLocation(IResourceLocation loc) + { + if (loc == null || string.IsNullOrEmpty(loc.InternalId)) + return false; + + var id = loc.InternalId; + if (id.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || + id.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + return true; + + return loc.ResourceType == typeof(IAssetBundleResource); + } + + static string Truncate(string s, int maxLen) + { + if (string.IsNullOrEmpty(s) || s.Length <= maxLen) + return s ?? ""; + return s.Substring(0, maxLen) + "..."; + } + } +}