diff --git a/Basis/Packages/com.basis.bundlemanagement/BasisBEEExtensionMeta.cs b/Basis/Packages/com.basis.bundlemanagement/BasisBEEExtensionMeta.cs index 4217be46c..0f0b556a8 100644 --- a/Basis/Packages/com.basis.bundlemanagement/BasisBEEExtensionMeta.cs +++ b/Basis/Packages/com.basis.bundlemanagement/BasisBEEExtensionMeta.cs @@ -7,4 +7,5 @@ public class BasisBEEExtensionMeta [SerializeField] public BasisStoredEncryptedBundle StoredLocal = new BasisStoredEncryptedBundle();//where we got bundle file from public string UniqueVersion; + public string DownloadedPlatform; } diff --git a/Basis/Packages/com.basis.bundlemanagement/BasisBeeManagement.cs b/Basis/Packages/com.basis.bundlemanagement/BasisBeeManagement.cs index 8597c0753..313b9eac2 100644 --- a/Basis/Packages/com.basis.bundlemanagement/BasisBeeManagement.cs +++ b/Basis/Packages/com.basis.bundlemanagement/BasisBeeManagement.cs @@ -5,6 +5,13 @@ using UnityEngine; public static class BasisBeeManagement { + private static bool HasCompatibleDownloadedPlatform(BasisBEEExtensionMeta metaInfo) + { + return metaInfo != null && + !string.IsNullOrEmpty(metaInfo.DownloadedPlatform) && + BasisIOManagement.CachePlatformMatchesCurrent(metaInfo.DownloadedPlatform); + } + /// /// this allows obtaining the entire bee file /// @@ -16,9 +23,16 @@ public static async Task HandleBundleAndMetaLoading(BasisTrackedBundleWrapper wr { bool IsMetaOnDisc = BasisLoadHandler.IsMetaDataOnDisc(wrapper.LoadableBundle.BasisRemoteBundleEncrypted.RemoteBeeFileLocation, out BasisBEEExtensionMeta MetaInfo); bool didForceRedownload = false; + bool shouldUseOnDiskMeta = IsMetaOnDisc; + + if (shouldUseOnDiskMeta && !HasCompatibleDownloadedPlatform(MetaInfo) && !string.IsNullOrEmpty(MetaInfo.DownloadedPlatform)) + { + BasisDebug.Log($"Cached bundle platform {MetaInfo.DownloadedPlatform} does not match {Application.platform}. Forcing re-download.", BasisDebug.LogTag.Event); + shouldUseOnDiskMeta = false; + } (BasisBundleGenerated, byte[], string) output; - if (IsMetaOnDisc) + if (shouldUseOnDiskMeta) { BasisDebug.Log("Process On Disc Meta Data Async", BasisDebug.LogTag.Event); output = await BasisBundleManagement.LocalLoadBundleConnector(wrapper, MetaInfo.StoredLocal, report, cancellationToken); @@ -54,7 +68,7 @@ public static async Task HandleBundleAndMetaLoading(BasisTrackedBundleWrapper wr { wrapper.AssetBundle = assetBundle; BasisDebug.Log($"we already have this AssetToLoadName in our loaded bundles using that instead! {AssetToLoadName}"); - await SaveMetaIfNeeded(wrapper, IsMetaOnDisc, didForceRedownload); + await SaveMetaIfNeeded(wrapper, shouldUseOnDiskMeta, didForceRedownload, output.Item1.Platform); return; } } @@ -63,10 +77,31 @@ public static async Task HandleBundleAndMetaLoading(BasisTrackedBundleWrapper wr try { AssetBundleCreateRequest bundleRequest = await BasisEncryptionToData.GenerateBundleFromFile(wrapper.LoadableBundle.UnlockPassword, output.Item2, output.Item1.AssetBundleCRC, report); + if (bundleRequest == null || bundleRequest.assetBundle == null) + { + if (shouldUseOnDiskMeta && !didForceRedownload) + { + BasisDebug.Log("Cached bundle bytes failed to load; forcing re-download.", BasisDebug.LogTag.Event); + output = await BasisBundleManagement.DownloadLoadBundleConnector(wrapper, report, cancellationToken, MaxDownloadSizeInBytes); + didForceRedownload = true; + + if (output.Item1 == null || output.Item2 == null || output.Item2.Length == 0 || !string.IsNullOrEmpty(output.Item3)) + { + throw new Exception($"Unable to reload bundle after cache mismatch. {output.Item3}"); + } + + bundleRequest = await BasisEncryptionToData.GenerateBundleFromFile(wrapper.LoadableBundle.UnlockPassword, output.Item2, output.Item1.AssetBundleCRC, report); + } + + if (bundleRequest == null || bundleRequest.assetBundle == null) + { + throw new Exception("AssetBundle creation failed after attempting to refresh the cached bundle."); + } + } wrapper.AssetBundle = bundleRequest.assetBundle; - await SaveMetaIfNeeded(wrapper, IsMetaOnDisc, didForceRedownload); + await SaveMetaIfNeeded(wrapper, shouldUseOnDiskMeta, didForceRedownload, output.Item1.Platform); } catch (Exception ex) { @@ -76,7 +111,7 @@ public static async Task HandleBundleAndMetaLoading(BasisTrackedBundleWrapper wr /// /// Saves or updates on-disc metadata when it is missing or was refreshed by a forced re-download. /// - private static async Task SaveMetaIfNeeded(BasisTrackedBundleWrapper wrapper, bool wasMetaOnDisc, bool didForceRedownload) + private static async Task SaveMetaIfNeeded(BasisTrackedBundleWrapper wrapper, bool wasMetaOnDisc, bool didForceRedownload, string downloadedPlatform) { if (!wasMetaOnDisc || didForceRedownload) { @@ -85,6 +120,7 @@ private static async Task SaveMetaIfNeeded(BasisTrackedBundleWrapper wrapper, bo StoredRemote = wrapper.LoadableBundle.BasisRemoteBundleEncrypted, StoredLocal = wrapper.LoadableBundle.BasisLocalEncryptedBundle, UniqueVersion = wrapper.LoadableBundle.BasisBundleConnector.UniqueVersion, + DownloadedPlatform = downloadedPlatform, }; await BasisLoadHandler.AddDiscInfo(newDiscInfo); @@ -124,6 +160,7 @@ public static async Task HandleMetaOnlyLoad(BasisTrackedBundleWrapper wrap StoredRemote = wrapper.LoadableBundle.BasisRemoteBundleEncrypted, StoredLocal = wrapper.LoadableBundle.BasisLocalEncryptedBundle, UniqueVersion = wrapper.LoadableBundle.BasisBundleConnector.UniqueVersion, + DownloadedPlatform = BasisIOManagement.GetCurrentCachePlatform(), }; await BasisLoadHandler.AddDiscInfo(newDiscInfo); diff --git a/Basis/Packages/com.basis.bundlemanagement/BasisIOManagement.cs b/Basis/Packages/com.basis.bundlemanagement/BasisIOManagement.cs index 386e6eb4f..86e5e5c66 100644 --- a/Basis/Packages/com.basis.bundlemanagement/BasisIOManagement.cs +++ b/Basis/Packages/com.basis.bundlemanagement/BasisIOManagement.cs @@ -7,6 +7,77 @@ public static class BasisIOManagement { + public static string GetCurrentCachePlatform() + { + return NormalizeCachePlatformName(Application.platform.ToString()); + } + + public static bool CachePlatformMatchesCurrent(string downloadedPlatform) + { + return string.Equals(NormalizeCachePlatformName(downloadedPlatform), GetCurrentCachePlatform(), StringComparison.OrdinalIgnoreCase); + } + + public static string NormalizeCachePlatformName(string platformName) + { + if (string.IsNullOrWhiteSpace(platformName)) + { + return string.Empty; + } + + string normalized = platformName.Trim(); + return normalized switch + { + nameof(RuntimePlatform.WindowsEditor) => "StandaloneWindows64", + nameof(RuntimePlatform.WindowsPlayer) => "StandaloneWindows64", + nameof(RuntimePlatform.WindowsServer) => "StandaloneWindows64", + nameof(RuntimePlatform.LinuxEditor) => "StandaloneLinux64", + nameof(RuntimePlatform.LinuxPlayer) => "StandaloneLinux64", + nameof(RuntimePlatform.LinuxServer) => "StandaloneLinux64", + nameof(RuntimePlatform.OSXEditor) => "StandaloneOSX", + nameof(RuntimePlatform.OSXPlayer) => "StandaloneOSX", + nameof(RuntimePlatform.Android) => "Android", + nameof(RuntimePlatform.IPhonePlayer) => "iOS", + _ => normalized, + }; + } + + public static string GetBeeCacheFilePath(string uniqueVersion, string downloadedPlatform = null) + { + return GenerateFilePath(BuildPlatformAwareCacheFileName(uniqueVersion, BasisBeeConstants.BasisEncryptedExtension, downloadedPlatform), BasisBeeConstants.AssetBundlesFolder); + } + + public static string GetMetaCacheFilePath(string uniqueVersion, string downloadedPlatform = null) + { + return GenerateFilePath(BuildPlatformAwareCacheFileName(uniqueVersion, BasisBeeConstants.BasisMetaExtension, downloadedPlatform), BasisBeeConstants.AssetBundlesFolder); + } + + public static string GetLegacyBeeCacheFilePath(string uniqueVersion) + { + return GenerateFilePath($"{uniqueVersion}{BasisBeeConstants.BasisEncryptedExtension}", BasisBeeConstants.AssetBundlesFolder); + } + + public static string GetLegacyMetaCacheFilePath(string uniqueVersion) + { + return GenerateFilePath($"{uniqueVersion}{BasisBeeConstants.BasisMetaExtension}", BasisBeeConstants.AssetBundlesFolder); + } + + private static string BuildPlatformAwareCacheFileName(string uniqueVersion, string extension, string downloadedPlatform) + { + if (string.IsNullOrWhiteSpace(uniqueVersion)) + throw new ArgumentException("Unique version is null or empty.", nameof(uniqueVersion)); + + string normalizedPlatform = string.IsNullOrWhiteSpace(downloadedPlatform) + ? GetCurrentCachePlatform() + : NormalizeCachePlatformName(downloadedPlatform); + + foreach (char invalidChar in Path.GetInvalidFileNameChars()) + { + normalizedPlatform = normalizedPlatform.Replace(invalidChar, '_'); + } + + return $"{uniqueVersion}.{normalizedPlatform}{extension}"; + } + public sealed class BeeDownloadResult { public BasisBundleConnector Connector { get; } @@ -156,14 +227,14 @@ public static async Task> DownloadBEEEx(string url, } // 5) Write local .bee (Int32 header + connector + section) - string fileName = $"{connector.UniqueVersion}{BasisBeeConstants.BasisEncryptedExtension}"; + string fileName = Path.GetFileName(GetBeeCacheFilePath(connector.UniqueVersion)); if (string.IsNullOrWhiteSpace(fileName)) return BeeResult.Fail("DownloadBEEEx: Connector has no UniqueVersion / file extension."); string localPath; try { - localPath = GenerateFilePath(fileName, BasisBeeConstants.AssetBundlesFolder); + localPath = GetBeeCacheFilePath(connector.UniqueVersion); } catch (Exception ex) { @@ -227,13 +298,13 @@ public static async Task> DownloadBEEEx(string url, } // 5) Write local .bee (Int32 header + connector + section) - string fileName = $"{connector.UniqueVersion}{BasisBeeConstants.BasisEncryptedExtension}"; + string fileName = Path.GetFileName(GetBeeCacheFilePath(connector.UniqueVersion)); if (string.IsNullOrWhiteSpace(fileName)) { return BeeResult<(BasisBundleConnector, string)>.Fail("DownloadBEEEx: Connector has no UniqueVersion / file extension."); } - string localPath = GenerateFilePath(fileName, BasisBeeConstants.AssetBundlesFolder); + string localPath = GetBeeCacheFilePath(connector.UniqueVersion); var connectorBytes = connectorRes.Value.Data; var writeRes = await WriteBeeFileAsync(localPath, connectorBytes, null, true); diff --git a/Basis/Packages/com.basis.bundlemanagement/BasisLoadhandler.cs b/Basis/Packages/com.basis.bundlemanagement/BasisLoadhandler.cs index f59f47b94..7fe2b6fc2 100644 --- a/Basis/Packages/com.basis.bundlemanagement/BasisLoadhandler.cs +++ b/Basis/Packages/com.basis.bundlemanagement/BasisLoadhandler.cs @@ -167,39 +167,146 @@ private static async Task HandleFirstBundleLoad(GameObject DisabledG BasisDebug.LogError($"{ex.Message} {ex.StackTrace}"); LoadedBundles.Remove(loadableBundle.BasisRemoteBundleEncrypted.RemoteBeeFileLocation, out var data); CleanupFiles(loadableBundle.BasisLocalEncryptedBundle); - OnDiscData.TryRemove(loadableBundle.BasisRemoteBundleEncrypted.RemoteBeeFileLocation, out _); return null; } } + + public static string GetDiscInfoKey(string remoteUrl, string downloadedPlatform) + { + string safeUrl = remoteUrl ?? string.Empty; + string safePlatform = string.IsNullOrWhiteSpace(downloadedPlatform) ? "legacy" : downloadedPlatform.Trim(); + return $"{safePlatform}|{safeUrl}"; + } + + private static bool TryResolveStoredBeePath(BasisBEEExtensionMeta discInfo, out string beePath) + { + beePath = discInfo.StoredLocal.DownloadedBeeFileLocation; + if (!string.IsNullOrWhiteSpace(beePath) && File.Exists(beePath)) + { + return true; + } + + if (!string.IsNullOrWhiteSpace(discInfo.UniqueVersion)) + { + string platformAwarePath = BasisIOManagement.GetBeeCacheFilePath(discInfo.UniqueVersion, discInfo.DownloadedPlatform); + if (File.Exists(platformAwarePath)) + { + discInfo.StoredLocal.DownloadedBeeFileLocation = platformAwarePath; + beePath = platformAwarePath; + return true; + } + + string legacyPath = BasisIOManagement.GetLegacyBeeCacheFilePath(discInfo.UniqueVersion); + if (File.Exists(legacyPath)) + { + discInfo.StoredLocal.DownloadedBeeFileLocation = legacyPath; + beePath = legacyPath; + return true; + } + } + + beePath = null; + return false; + } + + private static bool TryLazyLoadDiscInfo(string metaUrl, string currentPlatform, out BasisBEEExtensionMeta info) + { + info = null; + + string path = BasisIOManagement.GenerateFolderPath(BasisBeeConstants.AssetBundlesFolder); + if (!Directory.Exists(path)) + { + return false; + } + + BasisBEEExtensionMeta legacyCandidate = null; + + foreach (string file in Directory.GetFiles(path, $"*{BasisBeeConstants.BasisMetaExtension}")) + { + try + { + byte[] fileData = File.ReadAllBytes(file); + BasisBEEExtensionMeta discInfo = BasisSerialization.DeserializeValue(fileData); + if (discInfo?.StoredRemote?.RemoteBeeFileLocation != metaUrl) + { + continue; + } + + OnDiscData[GetDiscInfoKey(discInfo.StoredRemote.RemoteBeeFileLocation, discInfo.DownloadedPlatform)] = discInfo; + + if (!TryResolveStoredBeePath(discInfo, out _)) + { + continue; + } + + if (BasisIOManagement.CachePlatformMatchesCurrent(discInfo.DownloadedPlatform)) + { + info = discInfo; + return true; + } + + if (string.IsNullOrWhiteSpace(discInfo.DownloadedPlatform) && legacyCandidate == null) + { + legacyCandidate = discInfo; + } + } + catch (Exception ex) + { + BasisDebug.LogWarning($"Failed lazy-loading disc info from {file}: {ex.Message}", BasisDebug.LogTag.Event); + } + } + + if (legacyCandidate != null) + { + info = legacyCandidate; + return true; + } + + return false; + } + public static bool IsMetaDataOnDisc(string MetaURL, out BasisBEEExtensionMeta info) { lock (_discInfoLock) { + string currentPlatform = BasisIOManagement.GetCurrentCachePlatform(); + BasisBEEExtensionMeta legacyCandidate = null; + foreach (var discInfo in OnDiscData.Values) { if (discInfo.StoredRemote.RemoteBeeFileLocation == MetaURL) { - info = discInfo; - - if (string.IsNullOrEmpty(discInfo.StoredLocal.DownloadedBeeFileLocation)) + if (!string.IsNullOrWhiteSpace(discInfo.DownloadedPlatform) && + !BasisIOManagement.CachePlatformMatchesCurrent(discInfo.DownloadedPlatform)) { - string BEEPath = BasisIOManagement.GenerateFilePath($"{info.UniqueVersion}{BasisBeeConstants.BasisEncryptedExtension}", BasisBeeConstants.AssetBundlesFolder); - if (File.Exists(BEEPath)) - { - discInfo.StoredLocal.DownloadedBeeFileLocation = BEEPath; - return true; - } + continue; } - else + + if (TryResolveStoredBeePath(discInfo, out _)) { - if (File.Exists(discInfo.StoredLocal.DownloadedBeeFileLocation)) + if (BasisIOManagement.CachePlatformMatchesCurrent(discInfo.DownloadedPlatform)) { + info = discInfo; return true; } + + legacyCandidate = discInfo; } } } + if (legacyCandidate != null) + { + info = legacyCandidate; + return true; + } + + if (TryLazyLoadDiscInfo(MetaURL, currentPlatform, out BasisBEEExtensionMeta lazyLoadedInfo)) + { + info = lazyLoadedInfo; + return true; + } + info = new BasisBEEExtensionMeta(); return false; } @@ -207,19 +314,21 @@ public static bool IsMetaDataOnDisc(string MetaURL, out BasisBEEExtensionMeta in public static async Task AddDiscInfo(BasisBEEExtensionMeta discInfo) { - if (OnDiscData.TryAdd(discInfo.StoredRemote.RemoteBeeFileLocation, discInfo)) - { - } - else - { - OnDiscData[discInfo.StoredRemote.RemoteBeeFileLocation] = discInfo; - BasisDebug.Log("Disc info updated.", BasisDebug.LogTag.Event); - } - string filePath = BasisIOManagement.GenerateFilePath($"{discInfo.UniqueVersion}{BasisBeeConstants.BasisMetaExtension}", BasisBeeConstants.AssetBundlesFolder); + string discKey = GetDiscInfoKey(discInfo.StoredRemote.RemoteBeeFileLocation, discInfo.DownloadedPlatform); + OnDiscData[discKey] = discInfo; + string filePath = BasisIOManagement.GetMetaCacheFilePath(discInfo.UniqueVersion, discInfo.DownloadedPlatform); byte[] serializedData = BasisSerialization.SerializeValue(discInfo); try { + if (!string.IsNullOrWhiteSpace(discInfo.UniqueVersion)) + { + string legacyMetaPath = BasisIOManagement.GetLegacyMetaCacheFilePath(discInfo.UniqueVersion); + if (File.Exists(legacyMetaPath)) + { + File.Delete(legacyMetaPath); + } + } if (File.Exists(filePath)) { File.Delete(filePath); @@ -238,7 +347,7 @@ public static void RemoveDiscInfo(string metaUrl) BasisStorageManagement.DeleteStoredFile(metaUrl); } - private static async Task EnsureInitializationComplete() + public static async Task EnsureInitializationComplete() { if (!IsInitialized) { @@ -275,7 +384,7 @@ private static async Task LoadAllDiscData() { byte[] fileData = await File.ReadAllBytesAsync(file); BasisBEEExtensionMeta discInfo = BasisSerialization.DeserializeValue(fileData); - OnDiscData.TryAdd(discInfo.StoredRemote.RemoteBeeFileLocation, discInfo); + OnDiscData[GetDiscInfoKey(discInfo.StoredRemote.RemoteBeeFileLocation, discInfo.DownloadedPlatform)] = discInfo; } catch (Exception ex) { @@ -292,7 +401,7 @@ private static async Task LoadAllDiscData() private static void CleanupFiles(BasisStoredEncryptedBundle bundle) { - if (File.Exists(bundle.DownloadedBeeFileLocation)) + if (!string.IsNullOrWhiteSpace(bundle?.DownloadedBeeFileLocation) && File.Exists(bundle.DownloadedBeeFileLocation)) { File.Delete(bundle.DownloadedBeeFileLocation); } diff --git a/Basis/Packages/com.basis.bundlemanagement/BasisStorageManagement.cs b/Basis/Packages/com.basis.bundlemanagement/BasisStorageManagement.cs index d7de86b5c..f49d5d3c7 100644 --- a/Basis/Packages/com.basis.bundlemanagement/BasisStorageManagement.cs +++ b/Basis/Packages/com.basis.bundlemanagement/BasisStorageManagement.cs @@ -26,10 +26,12 @@ public static class BasisStorageManagement /// public class StoredBeeFileInfo { + public string DiscKey; public string RemoteUrl; public string LocalPath; public string MetaPath; public string UniqueVersion; + public string DownloadedPlatform; public long FileSizeBytes; public DateTime LastWriteTimeUtc; public bool IsLoadedInMemory; @@ -72,20 +74,18 @@ public static List GetAllStoredFiles() foreach (var kvp in BasisLoadHandler.OnDiscData) { + string discKey = kvp.Key; string remoteUrl = kvp.Key; BasisBEEExtensionMeta meta = kvp.Value; + remoteUrl = meta.StoredRemote.RemoteBeeFileLocation; string beePath = meta.StoredLocal.DownloadedBeeFileLocation; if (string.IsNullOrEmpty(beePath)) { - beePath = BasisIOManagement.GenerateFilePath( - $"{meta.UniqueVersion}{BasisBeeConstants.BasisEncryptedExtension}", - BasisBeeConstants.AssetBundlesFolder); + beePath = BasisIOManagement.GetBeeCacheFilePath(meta.UniqueVersion, meta.DownloadedPlatform); } - string metaFilePath = BasisIOManagement.GenerateFilePath( - $"{meta.UniqueVersion}{BasisBeeConstants.BasisMetaExtension}", - BasisBeeConstants.AssetBundlesFolder); + string metaFilePath = BasisIOManagement.GetMetaCacheFilePath(meta.UniqueVersion, meta.DownloadedPlatform); long fileSize = 0; DateTime lastWrite = DateTime.MinValue; @@ -108,10 +108,12 @@ public static List GetAllStoredFiles() result.Add(new StoredBeeFileInfo { + DiscKey = discKey, RemoteUrl = remoteUrl, LocalPath = beePath, MetaPath = metaFilePath, UniqueVersion = meta.UniqueVersion, + DownloadedPlatform = meta.DownloadedPlatform, FileSizeBytes = fileSize, LastWriteTimeUtc = lastWrite, IsLoadedInMemory = isLoaded, @@ -133,28 +135,21 @@ public static bool DeleteStoredFile(string remoteUrl) if (string.IsNullOrEmpty(remoteUrl)) return false; - // Remove from OnDiscData and get the meta info - if (BasisLoadHandler.OnDiscData.TryRemove(remoteUrl, out BasisBEEExtensionMeta meta)) + bool removedAny = false; + foreach (var kvp in BasisLoadHandler.OnDiscData.ToList()) { - // Delete the .BEE file - string beePath = meta.StoredLocal.DownloadedBeeFileLocation; - if (string.IsNullOrEmpty(beePath)) + if (!string.Equals(kvp.Value.StoredRemote.RemoteBeeFileLocation, remoteUrl, StringComparison.Ordinal)) { - beePath = BasisIOManagement.GenerateFilePath( - $"{meta.UniqueVersion}{BasisBeeConstants.BasisEncryptedExtension}", - BasisBeeConstants.AssetBundlesFolder); + continue; } - TryDeleteFile(beePath); - // Delete the .BME meta file - string metaPath = BasisIOManagement.GenerateFilePath( - $"{meta.UniqueVersion}{BasisBeeConstants.BasisMetaExtension}", - BasisBeeConstants.AssetBundlesFolder); - TryDeleteFile(metaPath); - - BasisDebug.Log($"Deleted stored BEE file: {meta.UniqueVersion} (source: {remoteUrl})", BasisDebug.LogTag.Event); + if (DeleteStoredEntry(kvp.Key, kvp.Value)) + { + removedAny = true; + } } - else + + if (!removedAny) { BasisDebug.LogWarning($"No OnDiscData entry found for URL: {remoteUrl}", BasisDebug.LogTag.Event); return false; @@ -186,11 +181,27 @@ public static bool DeleteStoredFile(string remoteUrl) public static void ClearAllCache() { // Get all keys first to avoid concurrent modification - var keys = BasisLoadHandler.OnDiscData.Keys.ToList(); + var entries = BasisLoadHandler.OnDiscData.ToList(); - foreach (string key in keys) + foreach (var entry in entries) { - DeleteStoredFile(key); + DeleteStoredEntry(entry.Key, entry.Value); + } + + foreach (var loadedEntry in BasisLoadHandler.LoadedBundles.ToList()) + { + if (BasisLoadHandler.LoadedBundles.TryRemove(loadedEntry.Key, out BasisTrackedBundleWrapper wrapper) && + wrapper?.AssetBundle != null) + { + try + { + wrapper.AssetBundle.Unload(true); + } + catch (Exception ex) + { + BasisDebug.LogError($"Error unloading AssetBundle during cache clear: {ex.Message}"); + } + } } // Also clean up any orphaned files not tracked in OnDiscData @@ -229,7 +240,8 @@ public static void EnforceCacheSizeLimit() break; long freed = file.FileSizeBytes; - if (DeleteStoredFile(file.RemoteUrl)) + if (BasisLoadHandler.OnDiscData.TryGetValue(file.DiscKey, out BasisBEEExtensionMeta meta) && + DeleteStoredEntry(file.DiscKey, meta)) { currentSize -= freed; BasisDebug.Log($"Evicted: {file.UniqueVersion} ({FormatBytes(freed)}). Remaining: {FormatBytes(currentSize)}", BasisDebug.LogTag.Event); @@ -259,6 +271,27 @@ private static string GetCacheFolderPath() return BasisIOManagement.GenerateFolderPath(BasisBeeConstants.AssetBundlesFolder); } + private static bool DeleteStoredEntry(string discKey, BasisBEEExtensionMeta meta) + { + if (!BasisLoadHandler.OnDiscData.TryRemove(discKey, out _)) + { + return false; + } + + string beePath = meta.StoredLocal.DownloadedBeeFileLocation; + if (string.IsNullOrEmpty(beePath)) + { + beePath = BasisIOManagement.GetBeeCacheFilePath(meta.UniqueVersion, meta.DownloadedPlatform); + } + TryDeleteFile(beePath); + + string metaPath = BasisIOManagement.GetMetaCacheFilePath(meta.UniqueVersion, meta.DownloadedPlatform); + TryDeleteFile(metaPath); + + BasisDebug.Log($"Deleted stored BEE file: {meta.UniqueVersion} [{meta.DownloadedPlatform}] (source: {meta.StoredRemote.RemoteBeeFileLocation})", BasisDebug.LogTag.Event); + return true; + } + private static void TryDeleteFile(string path) { if (string.IsNullOrEmpty(path)) diff --git a/Basis/Packages/com.basis.framework/BasisUI/Menus/Library/LibraryProvider.cs b/Basis/Packages/com.basis.framework/BasisUI/Menus/Library/LibraryProvider.cs index 8bb5cd059..b9a6205a4 100644 --- a/Basis/Packages/com.basis.framework/BasisUI/Menus/Library/LibraryProvider.cs +++ b/Basis/Packages/com.basis.framework/BasisUI/Menus/Library/LibraryProvider.cs @@ -276,6 +276,8 @@ public static async Task LoadWrapperFromDisc(BasisDa wrapper = CreateNewWrapperFromItem(item); } + await BasisLoadHandler.EnsureInitializationComplete(); + // If the metadata is missing on disk, remove the key and DO NOT attempt to create a bundle from it. if (BasisLoadHandler.IsMetaDataOnDisc(item.Url, out BasisBEEExtensionMeta info)) { diff --git a/Basis/Packages/com.basis.framework/Networking/BasisNetworkPreloadManager.cs b/Basis/Packages/com.basis.framework/Networking/BasisNetworkPreloadManager.cs index ea6934173..78122fa63 100644 --- a/Basis/Packages/com.basis.framework/Networking/BasisNetworkPreloadManager.cs +++ b/Basis/Packages/com.basis.framework/Networking/BasisNetworkPreloadManager.cs @@ -76,6 +76,8 @@ private static async Task HandlePreload(LocalLoadResource resource) BasisProgressReport report = new BasisProgressReport(); CancellationToken cancel = default; + await BasisLoadHandler.EnsureInitializationComplete(); + // Check if the full BEE file is already on disk bool isOnDisc = BasisLoadHandler.IsMetaDataOnDisc(resource.CombinedURL, out BasisBEEExtensionMeta metaInfo);