RustDedicated/Rust.FileSystem/AssetBundleBackend.cs
2025-08-09 20:48:06 +09:30

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;
}
}