using System.Collections.Generic; using System.Linq; using UnityEngine; using Logger = NewHorizons.Utility.Logger; namespace NewHorizons.Handlers { /// /// handles streaming meshes so they stay loaded /// public static class StreamingHandler { private static readonly Dictionary _materialCache = new(); private static readonly Dictionary _objectCache = new(); private static readonly Dictionary> _sectorCache = new(); public static void Init() { _materialCache.Clear(); _objectCache.Clear(); _sectorCache.Clear(); } /// /// makes it so that this object's streaming stuff will be connected to the given sector /// public static void SetUpStreaming(GameObject obj, Sector sector) { // find the asset bundles to load // tries the cache first, then builds if (!_objectCache.TryGetValue(obj, out var assetBundles)) { var assetBundlesList = new List(); var tables = Resources.FindObjectsOfTypeAll(); foreach (var streamingHandle in obj.GetComponentsInChildren()) { var assetBundle = streamingHandle.assetBundle; assetBundlesList.SafeAdd(assetBundle); if (streamingHandle is StreamingRenderMeshHandle or StreamingSkinnedMeshHandle) { var materials = streamingHandle.GetComponent().sharedMaterials; if (materials.Length == 0) continue; // Gonna assume that if theres more than one material its probably in the same asset bundle anyway right if (_materialCache.TryGetValue(materials[0], out assetBundle)) { assetBundlesList.SafeAdd(assetBundle); } else { foreach (var table in tables) { foreach (var lookup in table._materialPropertyLookups) { if (materials.Contains(lookup.material)) { _materialCache.SafeAdd(lookup.material, table.assetBundle); assetBundlesList.SafeAdd(table.assetBundle); } } } } } } assetBundles = assetBundlesList.ToArray(); _objectCache[obj] = assetBundles; } foreach (var assetBundle in assetBundles) { StreamingManager.LoadStreamingAssets(assetBundle); // Track the sector even if its null. null means stay loaded forever if (!_sectorCache.ContainsKey(assetBundle)) _sectorCache.Add(assetBundle, new List()); if (!_sectorCache[assetBundle].Contains(sector)) _sectorCache[assetBundle].Add(sector); } if (sector) { sector.OnOccupantEnterSector += _ => { // If both are already in then it already loaded if (sector.ContainsOccupant(DynamicOccupant.Player) && sector.ContainsOccupant(DynamicOccupant.Probe)) return; foreach (var assetBundle in assetBundles) StreamingManager.LoadStreamingAssets(assetBundle); }; /* sector.OnOccupantExitSector += _ => { // UnloadStreamingAssets is patched to check IsBundleInUse first before unloading if (!sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe)) foreach (var assetBundle in assetBundles) StreamingManager.UnloadStreamingAssets(assetBundle); }; */ } } public static bool IsBundleInUse(string assetBundle) { // If a sector in the list is null then it is always in use if(_sectorCache.TryGetValue(assetBundle, out var sectors)) foreach (var sector in sectors) if (sector == null || sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe)) return true; return false; } } }