UI HUGE UPDATE
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.AddressableAssets.ResourceLocators;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
public static class DlcRemoteContentService
|
||||
{
|
||||
private sealed class RuntimeLoadedState
|
||||
{
|
||||
public readonly List<AsyncOperationHandle> retainedAssetHandles = new List<AsyncOperationHandle>();
|
||||
public readonly List<AsyncOperationHandle> retainedCatalogHandles = new List<AsyncOperationHandle>();
|
||||
public readonly List<DlcRuntimePackage> packages = new List<DlcRuntimePackage>();
|
||||
}
|
||||
|
||||
private sealed class DlcRemoteContentRunner : MonoBehaviour
|
||||
{
|
||||
private void Start()
|
||||
{
|
||||
if (DlcRemoteContentService.autoRefreshOnStart)
|
||||
{
|
||||
DlcRemoteContentService.RefreshInstalledDlc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DlcRemoteContentRunner runner;
|
||||
private static RuntimeLoadedState activeState = new RuntimeLoadedState();
|
||||
private static bool autoRefreshOnStart = true;
|
||||
private static bool refreshRequestedOnBoot;
|
||||
|
||||
public static bool IsRefreshing { get; private set; }
|
||||
|
||||
public static event Action<bool> RefreshCompleted;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void EnsureRunner()
|
||||
{
|
||||
if (runner != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject go = new GameObject(nameof(DlcRemoteContentService));
|
||||
UnityEngine.Object.DontDestroyOnLoad(go);
|
||||
runner = go.AddComponent<DlcRemoteContentRunner>();
|
||||
refreshRequestedOnBoot = false;
|
||||
}
|
||||
|
||||
public static void SetAutoRefreshOnStart(bool enabled)
|
||||
{
|
||||
autoRefreshOnStart = enabled;
|
||||
}
|
||||
|
||||
public static void RefreshInstalledDlc()
|
||||
{
|
||||
EnsureRunner();
|
||||
if (runner == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsRefreshing)
|
||||
{
|
||||
refreshRequestedOnBoot = true;
|
||||
return;
|
||||
}
|
||||
|
||||
runner.StartCoroutine(RefreshInstalledDlcRoutine());
|
||||
}
|
||||
|
||||
public static void ClearRuntimeDlc()
|
||||
{
|
||||
ReleaseState(activeState);
|
||||
activeState = new RuntimeLoadedState();
|
||||
DlcRuntimeRegistry.ClearAll();
|
||||
InvalidateRuntimeCaches();
|
||||
}
|
||||
|
||||
private static IEnumerator RefreshInstalledDlcRoutine()
|
||||
{
|
||||
if (IsRefreshing)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
IsRefreshing = true;
|
||||
refreshRequestedOnBoot = false;
|
||||
|
||||
List<DlcManifestRuntimeEntry> manifests = DlcManifestService.LoadInstalledEntries();
|
||||
RuntimeLoadedState nextState = new RuntimeLoadedState();
|
||||
bool success = true;
|
||||
|
||||
for (int i = 0; i < manifests.Count; i++)
|
||||
{
|
||||
DlcManifestRuntimeEntry manifest = manifests[i];
|
||||
if (manifest == null || manifest.entry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return LoadManifestEntry(manifest, nextState, result => success &= result);
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
RuntimeLoadedState oldState = activeState;
|
||||
activeState = nextState;
|
||||
DlcRuntimeRegistry.ReplaceAll(nextState.packages);
|
||||
InvalidateRuntimeCaches();
|
||||
ReleaseState(oldState);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReleaseState(nextState);
|
||||
}
|
||||
|
||||
IsRefreshing = false;
|
||||
RefreshCompleted?.Invoke(success);
|
||||
|
||||
if (refreshRequestedOnBoot)
|
||||
{
|
||||
RefreshInstalledDlc();
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator LoadManifestEntry(
|
||||
DlcManifestRuntimeEntry manifest,
|
||||
RuntimeLoadedState state,
|
||||
Action<bool> reportResult)
|
||||
{
|
||||
bool localSuccess = true;
|
||||
AsyncOperationHandle<IResourceLocator> catalogHandle = Addressables.LoadContentCatalogAsync(manifest.resolvedCatalogPath, false);
|
||||
yield return catalogHandle;
|
||||
|
||||
if (catalogHandle.Status != AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
Debug.LogWarning("[DLC] Failed to load catalog: " + manifest.resolvedCatalogPath);
|
||||
reportResult?.Invoke(false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
state.retainedCatalogHandles.Add(catalogHandle);
|
||||
|
||||
if (manifest.entry.autoDownloadDependencies)
|
||||
{
|
||||
string[] dependencyKeys = manifest.entry.GetDependencyKeys();
|
||||
for (int i = 0; i < dependencyKeys.Length; i++)
|
||||
{
|
||||
string dependencyKey = dependencyKeys[i];
|
||||
if (string.IsNullOrWhiteSpace(dependencyKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AsyncOperationHandle downloadHandle = Addressables.DownloadDependenciesAsync(dependencyKey, false);
|
||||
yield return downloadHandle;
|
||||
|
||||
if (downloadHandle.Status == AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
state.retainedAssetHandles.Add(downloadHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
localSuccess = false;
|
||||
Debug.LogWarning("[DLC] Failed to download dependencies for key '" + dependencyKey + "'.");
|
||||
Addressables.Release(downloadHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<dlcData> loadedDlcs = new List<dlcData>();
|
||||
List<SongData> loadedSongs = new List<SongData>();
|
||||
List<SongDlcContentSO> loadedSongContents = new List<SongDlcContentSO>();
|
||||
List<HeroSkinSO> loadedHeroSkins = new List<HeroSkinSO>();
|
||||
|
||||
yield return LoadAssetsByLabels(manifest.entry.GetDlcDataLabels(), loadedDlcs, state.retainedAssetHandles, result => localSuccess &= result);
|
||||
yield return LoadAssetsByLabels(manifest.entry.GetSongLabels(), loadedSongs, state.retainedAssetHandles, result => localSuccess &= result);
|
||||
yield return LoadAssetsByLabels(manifest.entry.GetSongContentLabels(), loadedSongContents, state.retainedAssetHandles, result => localSuccess &= result);
|
||||
yield return LoadAssetsByLabels(manifest.entry.GetHeroSkinLabels(), loadedHeroSkins, state.retainedAssetHandles, result => localSuccess &= result);
|
||||
|
||||
DlcRuntimePackage package = new DlcRuntimePackage
|
||||
{
|
||||
packageId = manifest.GetRuntimePackageId(),
|
||||
dlcKey = manifest.entry.GetSafeDlcKey(),
|
||||
version = manifest.entry.version ?? string.Empty,
|
||||
sourceCatalogPath = manifest.resolvedCatalogPath,
|
||||
dlcs = loadedDlcs.ToArray(),
|
||||
songs = loadedSongs.ToArray(),
|
||||
songContents = loadedSongContents.ToArray(),
|
||||
heroSkins = loadedHeroSkins.ToArray()
|
||||
};
|
||||
|
||||
state.packages.Add(package);
|
||||
reportResult?.Invoke(localSuccess);
|
||||
}
|
||||
|
||||
private static IEnumerator LoadAssetsByLabels<T>(
|
||||
string[] labels,
|
||||
List<T> output,
|
||||
List<AsyncOperationHandle> retainedHandles,
|
||||
Action<bool> reportResult) where T : UnityEngine.Object
|
||||
{
|
||||
if (labels == null || labels.Length == 0)
|
||||
{
|
||||
reportResult?.Invoke(true);
|
||||
yield break;
|
||||
}
|
||||
|
||||
List<object> keys = new List<object>(labels.Length);
|
||||
for (int i = 0; i < labels.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(labels[i]))
|
||||
{
|
||||
keys.Add(labels[i].Trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (keys.Count == 0)
|
||||
{
|
||||
reportResult?.Invoke(true);
|
||||
yield break;
|
||||
}
|
||||
|
||||
AsyncOperationHandle<IList<T>> loadHandle = Addressables.LoadAssetsAsync<T>(keys, null, Addressables.MergeMode.Union, false);
|
||||
yield return loadHandle;
|
||||
|
||||
if (loadHandle.Status != AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
Debug.LogWarning("[DLC] Failed to load runtime assets of type " + typeof(T).Name + ".");
|
||||
Addressables.Release(loadHandle);
|
||||
reportResult?.Invoke(false);
|
||||
yield break;
|
||||
}
|
||||
|
||||
retainedHandles.Add(loadHandle);
|
||||
|
||||
IList<T> result = loadHandle.Result;
|
||||
if (result != null)
|
||||
{
|
||||
for (int i = 0; i < result.Count; i++)
|
||||
{
|
||||
T asset = result[i];
|
||||
if (asset != null && !output.Contains(asset))
|
||||
{
|
||||
output.Add(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reportResult?.Invoke(true);
|
||||
}
|
||||
|
||||
private static void InvalidateRuntimeCaches()
|
||||
{
|
||||
RuntimeResourcesCache.InvalidateAll();
|
||||
SongDlcContentResolver.InvalidateCache();
|
||||
HeroSkinResolver.InvalidateCache();
|
||||
DlcOwnershipService.InvalidateCache();
|
||||
|
||||
SongDataLibrary library = SongDataLibrary.Instance;
|
||||
if (library != null)
|
||||
{
|
||||
library.RefreshLibrary();
|
||||
}
|
||||
|
||||
loadDlcListPrefab[] uiPanels = UnityEngine.Object.FindObjectsByType<loadDlcListPrefab>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
for (int i = 0; i < uiPanels.Length; i++)
|
||||
{
|
||||
if (uiPanels[i] != null && uiPanels[i].isActiveAndEnabled)
|
||||
{
|
||||
uiPanels[i].RefreshUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReleaseState(RuntimeLoadedState state)
|
||||
{
|
||||
if (state == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < state.retainedAssetHandles.Count; i++)
|
||||
{
|
||||
AsyncOperationHandle handle = state.retainedAssetHandles[i];
|
||||
if (handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
}
|
||||
|
||||
state.retainedAssetHandles.Clear();
|
||||
|
||||
for (int i = 0; i < state.retainedCatalogHandles.Count; i++)
|
||||
{
|
||||
AsyncOperationHandle handle = state.retainedCatalogHandles[i];
|
||||
if (handle.IsValid())
|
||||
{
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
}
|
||||
|
||||
state.retainedCatalogHandles.Clear();
|
||||
state.packages.Clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user