forked from SubnauticaNitrox/Nitrox
-
Notifications
You must be signed in to change notification settings - Fork 1
/
AssetsBundleManager.cs
147 lines (124 loc) · 5.78 KB
/
AssetsBundleManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using AssetsTools.NET;
using AssetsTools.NET.Extra;
using NitroxModel.DataStructures.Unity;
namespace NitroxServer_Subnautica.Resources.Parsers.Helper;
public class AssetsBundleManager : AssetsManager
{
private readonly string aaRootPath;
private readonly Dictionary<AssetsFileInstance, string[]> dependenciesByAssetFileInst = new();
private ThreadSafeMonoCecilTempGenerator monoTempGenerator;
public AssetsBundleManager(string aaRootPath)
{
this.aaRootPath = aaRootPath;
}
public string CleanBundlePath(string bundlePath)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
bundlePath = bundlePath.Replace('\\', '/');
}
return aaRootPath + bundlePath.Substring(bundlePath.IndexOf('}') + 1);
}
public AssetsFileInstance LoadBundleWithDependencies(string[] bundlePaths)
{
BundleFileInstance bundleFile = LoadBundleFile(CleanBundlePath(bundlePaths[0]));
AssetsFileInstance assetFileInstance = LoadAssetsFileFromBundle(bundleFile, 0);
dependenciesByAssetFileInst[assetFileInstance] = bundlePaths;
return assetFileInstance;
}
/// <summary>
/// Copied from https://github.com/nesrak1/AssetsTools.NET#full-monobehaviour-writing-example
/// </summary>
/// <param name="inst"><see cref="AssetsFileInstance" /> instance currently used</param>
/// <param name="targetGameObjectValue"><see cref="AssetFileInfo" /> of the target GameObject</param>
/// <param name="targetClassName">Class name of the target MonoBehaviour</param>
public AssetFileInfo GetMonoBehaviourFromGameObject(AssetsFileInstance inst, AssetFileInfo targetGameObjectValue, string targetClassName)
{
//example for finding a specific script and modifying the script on a GameObject
AssetTypeValueField playerBf = GetBaseField(inst, targetGameObjectValue);
AssetTypeValueField playerComponentArr = playerBf["m_Component"]["Array"];
AssetFileInfo monoBehaviourInf = null;
//first let's search for the MonoBehaviour we want in a GameObject
foreach (AssetTypeValueField child in playerComponentArr.Children)
{
//get component info (but don't deserialize yet, loading assets we don't need is wasteful)
AssetTypeValueField childPtr = child["component"];
AssetExternal childExt = GetExtAsset(inst, childPtr, true);
AssetFileInfo childInf = childExt.info;
//skip if not MonoBehaviour
if (childInf.GetTypeId(inst.file) != (int)AssetClassID.MonoBehaviour)
{
continue;
}
//actually deserialize the MonoBehaviour asset now
AssetTypeValueField childBf = GetExtAssetSafe(inst, childPtr).baseField;
AssetTypeValueField monoScriptPtr = childBf["m_Script"];
//get MonoScript from MonoBehaviour
AssetExternal monoScriptExt = GetExtAsset(childExt.file, monoScriptPtr);
AssetTypeValueField monoScriptBf = monoScriptExt.baseField;
string className = monoScriptBf["m_ClassName"].AsString;
if (className == targetClassName)
{
monoBehaviourInf = childInf;
break;
}
}
return monoBehaviourInf;
}
public NitroxTransform GetTransformFromGameObject(AssetsFileInstance assetFileInst, AssetTypeValueField rootGameObject)
{
AssetTypeValueField componentArray = rootGameObject["m_Component"]["Array"];
AssetTypeValueField transformRef = componentArray[0]["component"];
AssetTypeValueField transformField = GetExtAsset(assetFileInst, transformRef).baseField;
return new(transformField["m_LocalPosition"].ToNitroxVector3(), transformField["m_LocalRotation"].ToNitroxQuaternion(), transformField["m_LocalScale"].ToNitroxVector3());
}
public new void SetMonoTempGenerator(IMonoBehaviourTemplateGenerator generator)
{
monoTempGenerator = (ThreadSafeMonoCecilTempGenerator)generator;
base.SetMonoTempGenerator(generator);
}
/// <summary>
/// Returns a ready to use <see cref="AssetsManager" /> with loaded <see cref="AssetsManager.classDatabase" />, <see cref="AssetsManager.classPackage" /> and
/// <see cref="IMonoBehaviourTemplateGenerator" />.
/// </summary>
public AssetsBundleManager Clone()
{
AssetsBundleManager bundleManagerInst = new(aaRootPath) { classDatabase = classDatabase, classPackage = classPackage };
bundleManagerInst.SetMonoTempGenerator(monoTempGenerator);
return bundleManagerInst;
}
/// <inheritdoc cref="AssetsManager.UnloadAll" />
public new void UnloadAll(bool unloadClassData = false)
{
if (unloadClassData)
{
monoTempGenerator.Dispose();
}
dependenciesByAssetFileInst.Clear();
base.UnloadAll(unloadClassData);
}
private AssetExternal GetExtAssetSafe(AssetsFileInstance relativeTo, AssetTypeValueField valueField)
{
string[] bundlePaths = dependenciesByAssetFileInst[relativeTo];
for (int i = 0; i < bundlePaths.Length; i++)
{
if (i != 0)
{
BundleFileInstance dependenciesBundleFile = LoadBundleFile(CleanBundlePath(bundlePaths[i]));
LoadAssetsFileFromBundle(dependenciesBundleFile, 0);
}
try
{
return GetExtAsset(relativeTo, valueField);
}
catch (Exception)
{
// ignored
}
}
throw new InvalidOperationException("Could find AssetTypeValueField in given dependencies");
}
}