419 lines
12 KiB
C#
419 lines
12 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Facepunch.Extend;
|
|
using Rust;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
public class AssetBundleBackend : FileSystemBackend, IDisposable
|
|
{
|
|
private AssetBundle rootBundle;
|
|
|
|
private AssetBundleManifest manifest;
|
|
|
|
private Dictionary<string, AssetBundle> bundles = new Dictionary<string, AssetBundle>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
private Dictionary<string, AssetBundle> files = new Dictionary<string, AssetBundle>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
private Dictionary<string, string> prefabToAssetSceneName = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
private Dictionary<string, AsyncOperation> assetSceneLoads = new Dictionary<string, AsyncOperation>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
private Dictionary<string, Dictionary<string, GameObject>> assetScenePrefabs = new Dictionary<string, Dictionary<string, GameObject>>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public string assetPath { get; private set; }
|
|
|
|
public static bool Enabled => true;
|
|
|
|
public void Load(string assetRoot)
|
|
{
|
|
try
|
|
{
|
|
isError = false;
|
|
string directoryName = Path.GetDirectoryName(assetRoot);
|
|
char directorySeparatorChar = Path.DirectorySeparatorChar;
|
|
assetPath = directoryName + directorySeparatorChar;
|
|
rootBundle = AssetBundle.LoadFromFile(assetRoot);
|
|
if (rootBundle == null)
|
|
{
|
|
LoadError("Couldn't load root AssetBundle - " + assetRoot);
|
|
return;
|
|
}
|
|
AssetBundleManifest[] array = rootBundle.LoadAllAssets<AssetBundleManifest>();
|
|
if (array.Length != 1)
|
|
{
|
|
LoadError($"Couldn't find AssetBundleManifest - {array.Length}");
|
|
return;
|
|
}
|
|
manifest = array[0];
|
|
string[] allAssetBundles = manifest.GetAllAssetBundles();
|
|
foreach (string text in allAssetBundles)
|
|
{
|
|
Debug.Log("Loading " + text);
|
|
LoadBundle(text);
|
|
}
|
|
AssetSceneManifest.Load(this);
|
|
prefabToAssetSceneName.Clear();
|
|
foreach (AssetSceneManifest.Entry scene in AssetSceneManifest.Current.Scenes)
|
|
{
|
|
foreach (string includedAsset in scene.IncludedAssets)
|
|
{
|
|
if (!prefabToAssetSceneName.TryAdd(includedAsset, scene.Name))
|
|
{
|
|
Debug.LogError("Duplicate prefab in asset scenes: " + includedAsset);
|
|
}
|
|
}
|
|
}
|
|
foreach (AssetSceneManifest.Entry scene2 in AssetSceneManifest.Current.Scenes)
|
|
{
|
|
if (scene2.AutoLoad)
|
|
{
|
|
Global.Runner.StartCoroutine(LoadAssetScene(scene2.Name));
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogException(exception);
|
|
}
|
|
}
|
|
|
|
private AssetBundle LoadBundle(string bundleName)
|
|
{
|
|
if (bundles.ContainsKey(bundleName))
|
|
{
|
|
Debug.Log("LoadBundle " + bundleName + " already loaded");
|
|
return null;
|
|
}
|
|
string text = assetPath + bundleName;
|
|
if (!File.Exists(text))
|
|
{
|
|
return null;
|
|
}
|
|
AssetBundle assetBundle = AssetBundle.LoadFromFile(text);
|
|
if (assetBundle == null)
|
|
{
|
|
LoadError("Couldn't load AssetBundle - " + text);
|
|
return null;
|
|
}
|
|
bundles.Add(bundleName, assetBundle);
|
|
return assetBundle;
|
|
}
|
|
|
|
public void BuildFileIndex()
|
|
{
|
|
files.Clear();
|
|
foreach (KeyValuePair<string, AssetBundle> bundle in bundles)
|
|
{
|
|
if (!bundle.Key.StartsWith("content", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string[] allAssetNames = bundle.Value.GetAllAssetNames();
|
|
foreach (string key in allAssetNames)
|
|
{
|
|
files.Add(key, bundle.Value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
manifest = null;
|
|
foreach (KeyValuePair<string, AssetBundle> bundle in bundles)
|
|
{
|
|
bundle.Value.Unload(unloadAllLoadedObjects: false);
|
|
UnityEngine.Object.DestroyImmediate(bundle.Value);
|
|
}
|
|
bundles.Clear();
|
|
if ((bool)rootBundle)
|
|
{
|
|
rootBundle.Unload(unloadAllLoadedObjects: false);
|
|
UnityEngine.Object.DestroyImmediate(rootBundle);
|
|
rootBundle = null;
|
|
}
|
|
}
|
|
|
|
public override List<string> UnloadBundles(string partialName)
|
|
{
|
|
List<string> list = new List<string>();
|
|
string[] array = bundles.Keys.ToArray();
|
|
foreach (string text in array)
|
|
{
|
|
if (text.IndexOf(partialName, StringComparison.OrdinalIgnoreCase) < 0)
|
|
{
|
|
continue;
|
|
}
|
|
AssetBundle assetBundle = bundles[text];
|
|
assetBundle.Unload(unloadAllLoadedObjects: false);
|
|
UnityEngine.Object.DestroyImmediate(assetBundle);
|
|
bundles.Remove(text);
|
|
assetBundle = LoadBundle(text);
|
|
string[] allAssetNames = assetBundle.GetAllAssetNames();
|
|
foreach (string text2 in allAssetNames)
|
|
{
|
|
files[text2] = assetBundle;
|
|
list.Add(text2);
|
|
if (cache.TryGetValue(text2, out var value))
|
|
{
|
|
cache.Remove(text2);
|
|
UnityEngine.Object.DestroyImmediate(value, allowDestroyingAssets: true);
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public float GetAssetSceneProgress()
|
|
{
|
|
List<string> autoLoadScenes = AssetSceneManifest.Current.AutoLoadScenes;
|
|
return GetAssetSceneProgress(autoLoadScenes);
|
|
}
|
|
|
|
public float GetAssetSceneProgress(List<string> sceneNames)
|
|
{
|
|
if (sceneNames == null || sceneNames.Count == 0)
|
|
{
|
|
return 1f;
|
|
}
|
|
int num = 0;
|
|
float num2 = 0f;
|
|
foreach (string sceneName in sceneNames)
|
|
{
|
|
float assetSceneProgress = GetAssetSceneProgress(sceneName);
|
|
num2 += assetSceneProgress;
|
|
if (assetSceneProgress >= 1f)
|
|
{
|
|
num++;
|
|
}
|
|
}
|
|
if (num >= sceneNames.Count)
|
|
{
|
|
return 1f;
|
|
}
|
|
return Mathf.Min(num2 / (float)sceneNames.Count, 0.999f);
|
|
}
|
|
|
|
public float GetAssetSceneProgress(string sceneName)
|
|
{
|
|
if (!assetSceneLoads.TryGetValue(sceneName, out var value))
|
|
{
|
|
AssetSceneUtil.Log("Asset scene '" + sceneName + "' is not currently loading or loaded - cannot get progress.");
|
|
return 0f;
|
|
}
|
|
return value.progress / 0.9f * 0.99f;
|
|
}
|
|
|
|
public IEnumerator LoadAssetScenes(List<string> sceneNames)
|
|
{
|
|
if (sceneNames == null || sceneNames.Count == 0)
|
|
{
|
|
yield break;
|
|
}
|
|
foreach (string sceneName in sceneNames)
|
|
{
|
|
if (!assetSceneLoads.ContainsKey(sceneName))
|
|
{
|
|
Global.Runner.StartCoroutine(LoadAssetScene(sceneName));
|
|
}
|
|
}
|
|
while (GetAssetSceneProgress(sceneNames) < 1f)
|
|
{
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
public IEnumerator UnloadAssetScenes(List<string> sceneNames, Action<string, Dictionary<string, GameObject>> onAssetSceneUnloaded)
|
|
{
|
|
List<AsyncOperation> unloadOperations = new List<AsyncOperation>();
|
|
foreach (string sceneName in sceneNames)
|
|
{
|
|
if (!assetSceneLoads.TryGetValue(sceneName, out var asyncOperation))
|
|
{
|
|
continue;
|
|
}
|
|
AssetSceneManifest.Entry entry = AssetSceneManifest.Current.Scenes.FindWith((AssetSceneManifest.Entry s) => s.Name, sceneName, StringComparer.OrdinalIgnoreCase);
|
|
if (entry == null || !entry.CanUnload)
|
|
{
|
|
Debug.LogError("Asset scene '" + sceneName + "' is not allowed to be unloaded.");
|
|
continue;
|
|
}
|
|
while (!asyncOperation.isDone)
|
|
{
|
|
yield return null;
|
|
}
|
|
AsyncOperation asyncOperation2 = SceneManager.UnloadSceneAsync(sceneName);
|
|
if (asyncOperation2 == null)
|
|
{
|
|
continue;
|
|
}
|
|
Dictionary<string, GameObject> valueOrDefault = assetScenePrefabs.GetValueOrDefault(sceneName);
|
|
if (valueOrDefault != null)
|
|
{
|
|
foreach (string key in valueOrDefault.Keys)
|
|
{
|
|
cache.Remove(key);
|
|
}
|
|
}
|
|
onAssetSceneUnloaded?.Invoke(sceneName, valueOrDefault);
|
|
assetSceneLoads.Remove(sceneName);
|
|
assetScenePrefabs.Remove(sceneName);
|
|
unloadOperations.Add(asyncOperation2);
|
|
asyncOperation = null;
|
|
}
|
|
while (unloadOperations.Any((AsyncOperation o) => !o.isDone))
|
|
{
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
public List<string> GetRequiredAssetScenesForPrefabs(IEnumerable<string> prefabPaths)
|
|
{
|
|
HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (string prefabPath in prefabPaths)
|
|
{
|
|
if (prefabToAssetSceneName.TryGetValue(prefabPath, out var value))
|
|
{
|
|
hashSet.Add(value);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("Prefab '" + prefabPath + "' does not have an associated asset scene.");
|
|
}
|
|
}
|
|
return hashSet.ToList();
|
|
}
|
|
|
|
public List<(string Path, GameObject Prefab)> GetAssetScenePrefabs(List<string> assetSceneNames = null)
|
|
{
|
|
return (from kvp in assetScenePrefabs.Where((KeyValuePair<string, Dictionary<string, GameObject>> kvp) => assetSceneNames == null || assetSceneNames.Contains(kvp.Key, StringComparer.OrdinalIgnoreCase)).SelectMany((KeyValuePair<string, Dictionary<string, GameObject>> d) => d.Value)
|
|
select (Path: kvp.Key, Prefab: kvp.Value)).ToList();
|
|
}
|
|
|
|
private IEnumerator LoadAssetScene(string sceneName)
|
|
{
|
|
LoadSceneParameters parameters = new LoadSceneParameters(LoadSceneMode.Additive, LocalPhysicsMode.Physics3D);
|
|
if (assetSceneLoads.ContainsKey(sceneName))
|
|
{
|
|
Debug.LogError("Already loaded asset scene: " + sceneName);
|
|
yield break;
|
|
}
|
|
AssetSceneUtil.Log("Starting to load asset scene: " + sceneName);
|
|
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName, parameters);
|
|
if (asyncLoad == null)
|
|
{
|
|
isError = true;
|
|
loadingError = "Failed to load asset scene: " + sceneName;
|
|
Debug.LogError(loadingError);
|
|
yield break;
|
|
}
|
|
asyncLoad.allowSceneActivation = false;
|
|
assetSceneLoads[sceneName] = asyncLoad;
|
|
Scene currentActiveScene = SceneManager.GetActiveScene();
|
|
while (!asyncLoad.isDone)
|
|
{
|
|
yield return null;
|
|
if (asyncLoad.progress >= 0.9f)
|
|
{
|
|
AssetSceneUtil.Log("Asset scene " + sceneName + " loaded, activating...");
|
|
currentActiveScene = SceneManager.GetActiveScene();
|
|
asyncLoad.allowSceneActivation = true;
|
|
SceneManager.SetActiveScene(currentActiveScene);
|
|
}
|
|
}
|
|
SceneManager.SetActiveScene(currentActiveScene);
|
|
Scene sceneByName = SceneManager.GetSceneByName(sceneName);
|
|
if (!sceneByName.IsValid() || !sceneByName.isLoaded)
|
|
{
|
|
isError = true;
|
|
loadingError = "Failed to get asset scene after loading: " + sceneName;
|
|
Debug.LogError(loadingError);
|
|
yield break;
|
|
}
|
|
AssetSceneUtil.Log("Asset scene " + sceneName + " activated successfully.");
|
|
GameObject[] rootGameObjects = sceneByName.GetRootGameObjects();
|
|
Dictionary<string, GameObject> dictionary = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase);
|
|
GameObject[] array = rootGameObjects;
|
|
foreach (GameObject gameObject in array)
|
|
{
|
|
string name = gameObject.name;
|
|
dictionary.TryAdd(name, gameObject);
|
|
gameObject.name = Path.GetFileNameWithoutExtension(name);
|
|
}
|
|
assetScenePrefabs[sceneName] = dictionary;
|
|
AssetSceneUtil.Log($"Asset scene {sceneName} root objects loaded: {rootGameObjects.Length}");
|
|
}
|
|
|
|
protected override T LoadAsset<T>(string filePath)
|
|
{
|
|
if (filePath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (prefabToAssetSceneName.TryGetValue(filePath, out var value))
|
|
{
|
|
if (assetScenePrefabs.TryGetValue(value, out var value2) && value2.TryGetValue(filePath, out var value3))
|
|
{
|
|
return value3 as T;
|
|
}
|
|
Debug.LogError((GetAssetSceneProgress(value) < 1f) ? ("Prefab '" + filePath + "' requires asset scene '" + value + "' to be loaded first.") : ("Prefab '" + filePath + "' is supposed to be in asset scene '" + value + "' but it was not found."));
|
|
}
|
|
else
|
|
{
|
|
AssetSceneUtil.Log("Prefab '" + filePath + "' not found in any asset scenes.");
|
|
}
|
|
return null;
|
|
}
|
|
if (!files.TryGetValue(filePath, out var value4))
|
|
{
|
|
return null;
|
|
}
|
|
return value4.LoadAsset<T>(filePath);
|
|
}
|
|
|
|
protected override string[] LoadAssetList(string folder, string search)
|
|
{
|
|
List<string> list = new List<string>();
|
|
foreach (KeyValuePair<string, Dictionary<string, GameObject>> assetScenePrefab in assetScenePrefabs)
|
|
{
|
|
assetScenePrefab.Deconstruct(out var _, out var value);
|
|
foreach (KeyValuePair<string, GameObject> item in value.Where((KeyValuePair<string, GameObject> x) => x.Key.StartsWith(folder, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
if (string.IsNullOrEmpty(search) || item.Key.IndexOf(search, StringComparison.OrdinalIgnoreCase) != -1)
|
|
{
|
|
list.Add(item.Key);
|
|
}
|
|
}
|
|
}
|
|
foreach (KeyValuePair<string, AssetBundle> item2 in files.Where((KeyValuePair<string, AssetBundle> x) => x.Key.StartsWith(folder, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
if (string.IsNullOrEmpty(search) || item2.Key.IndexOf(search, StringComparison.OrdinalIgnoreCase) != -1)
|
|
{
|
|
list.Add(item2.Key);
|
|
}
|
|
}
|
|
list.Sort(StringComparer.OrdinalIgnoreCase);
|
|
return list.ToArray();
|
|
}
|
|
|
|
public override T[] LoadAllFromBundle<T>(string bundleName, string editorSearch)
|
|
{
|
|
foreach (KeyValuePair<string, AssetBundle> bundle in bundles)
|
|
{
|
|
if (bundle.Key.EndsWith(bundleName))
|
|
{
|
|
return bundle.Value.LoadAllAssets<T>();
|
|
}
|
|
}
|
|
throw new Exception("LoadAllFromBundle found none");
|
|
}
|
|
|
|
public override bool HasAsset(string path)
|
|
{
|
|
if (!assetScenePrefabs.ContainsKey(path))
|
|
{
|
|
return files.ContainsKey(path);
|
|
}
|
|
return true;
|
|
}
|
|
}
|