mirror of
https://github.com/sp-tarkov/modules.git
synced 2025-02-13 09:50:43 -05:00
![Lacyway](/assets/img/avatar_default.png)
This gives users feedback that the game is currently loading bundles, which prevents confusion when a large amount of them are being loaded and the game is "seemingly" infinitely loading. The implementation is very bare bones, feel free to modify it to your liking if you want it to use a more fancy user interface. I went for clarity (black background, white text). With the standard `OnGUI` skin it's very hard to discern the text. Tested on 1440p and 1080p. ![image](/attachments/31a818d6-d101-4e5d-a2c5-541c8b48ba71) Co-authored-by: Lacyway <20912169+Lacyway@users.noreply.github.com> Reviewed-on: SPT/Modules#140 Co-authored-by: Lacyway <lacyway@noreply.dev.sp-tarkov.com> Co-committed-by: Lacyway <lacyway@noreply.dev.sp-tarkov.com>
177 lines
6.8 KiB
C#
177 lines
6.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
using Diz.Resources;
|
|
using JetBrains.Annotations;
|
|
using Newtonsoft.Json;
|
|
using UnityEngine;
|
|
using UnityEngine.Build.Pipeline;
|
|
using SPT.Common.Utils;
|
|
using SPT.Custom.Models;
|
|
using SPT.Custom.Utils;
|
|
using SPT.Reflection.Patching;
|
|
using DependencyGraph = DependencyGraph<IEasyBundle>;
|
|
using SPT.Reflection.Utils;
|
|
|
|
namespace SPT.Custom.Patches
|
|
{
|
|
public class EasyAssetsPatch : ModulePatch
|
|
{
|
|
private static readonly FieldInfo _bundlesField;
|
|
|
|
static EasyAssetsPatch()
|
|
{
|
|
_bundlesField = typeof(EasyAssets).GetFields(PatchConstants.PrivateFlags).FirstOrDefault(field => field.FieldType == typeof(EasyAssetHelperClass[]));
|
|
}
|
|
|
|
public EasyAssetsPatch()
|
|
{
|
|
_ = nameof(IEasyBundle.SameNameAsset);
|
|
_ = nameof(IBundleLock.IsLocked);
|
|
_ = nameof(BundleLock.MaxConcurrentOperations);
|
|
_ = nameof(DependencyGraph.GetDefaultNode);
|
|
}
|
|
|
|
protected override MethodBase GetTargetMethod()
|
|
{
|
|
return typeof(EasyAssets).GetMethod(nameof(EasyAssets.Create));
|
|
}
|
|
|
|
[PatchPrefix]
|
|
private static bool PatchPrefix(ref Task<EasyAssets> __result, GameObject gameObject, [CanBeNull] IBundleLock bundleLock, string defaultKey, string rootPath,
|
|
string platformName, [CanBeNull] Func<string, bool> shouldExclude, [CanBeNull] Func<string, Task> bundleCheck)
|
|
{
|
|
var easyAsset = gameObject.AddComponent<EasyAssets>();
|
|
__result = Init(easyAsset, bundleLock, defaultKey, rootPath, platformName, shouldExclude, bundleCheck);
|
|
|
|
return false;
|
|
}
|
|
|
|
private static async Task<EasyAssets> Init(EasyAssets instance, [CanBeNull] IBundleLock bundleLock, string defaultKey, string rootPath, string platformName, [CanBeNull] Func<string, bool> shouldExclude, Func<string, Task> bundleCheck)
|
|
{
|
|
// platform manifest
|
|
var eftBundlesPath = $"{rootPath.Replace("file:///", string.Empty).Replace("file://", string.Empty)}/{platformName}/";
|
|
var filepath = eftBundlesPath + platformName;
|
|
var jsonfile = filepath + ".json";
|
|
var manifest = VFS.Exists(jsonfile)
|
|
? await GetManifestJson(jsonfile)
|
|
: await GetManifestBundle(filepath);
|
|
|
|
// lazy-initialize SPT bundles
|
|
if (BundleManager.Bundles.Keys.Count == 0)
|
|
{
|
|
await BundleManager.DownloadManifest();
|
|
}
|
|
|
|
// create bundles array from obfuscated type
|
|
var bundleNames = manifest.GetAllAssetBundles()
|
|
.Union(BundleManager.Bundles.Keys)
|
|
.ToArray();
|
|
|
|
// create bundle lock
|
|
if (bundleLock == null)
|
|
{
|
|
bundleLock = new BundleLock(int.MaxValue);
|
|
}
|
|
|
|
var bundles = (IEasyBundle[])Array.CreateInstance(EasyBundleHelper.Type, bundleNames.Length);
|
|
|
|
var bundleUtils = BundleUtils.Create();
|
|
bundleUtils.Init(bundleNames.Length);
|
|
|
|
for (var i = 0; i < bundleNames.Length; i++)
|
|
{
|
|
var key = bundleNames[i];
|
|
var path = eftBundlesPath;
|
|
|
|
// acquire external bundle
|
|
if (BundleManager.Bundles.TryGetValue(key, out var bundleInfo))
|
|
{
|
|
bundleUtils.SetProgress(i, bundleInfo.FileName);
|
|
|
|
// we need base path without file extension
|
|
path = BundleManager.GetBundlePath(bundleInfo);
|
|
|
|
// only download when connected externally
|
|
if (await BundleManager.ShouldReaquire(bundleInfo))
|
|
{
|
|
await BundleManager.DownloadBundle(bundleInfo);
|
|
}
|
|
}
|
|
|
|
// create bundle of obfuscated type
|
|
bundles[i] = (IEasyBundle)Activator.CreateInstance(EasyBundleHelper.Type, new object[]
|
|
{
|
|
key,
|
|
path,
|
|
manifest,
|
|
bundleLock,
|
|
bundleCheck
|
|
});
|
|
}
|
|
|
|
bundleUtils.Dispose();
|
|
|
|
// create dependency graph
|
|
instance.Manifest = manifest;
|
|
_bundlesField.SetValue(instance, bundles);
|
|
instance.System = new DependencyGraph(bundles, defaultKey, shouldExclude);
|
|
|
|
return instance;
|
|
}
|
|
|
|
// NOTE: used by:
|
|
// - EscapeFromTarkov_Data/StreamingAssets/Windows/cubemaps
|
|
// - EscapeFromTarkov_Data/StreamingAssets/Windows/defaultmaterial
|
|
// - EscapeFromTarkov_Data/StreamingAssets/Windows/dissonancesetup
|
|
// - EscapeFromTarkov_Data/StreamingAssets/Windows/Doge
|
|
// - EscapeFromTarkov_Data/StreamingAssets/Windows/shaders
|
|
private static async Task<CompatibilityAssetBundleManifest> GetManifestBundle(string filepath)
|
|
{
|
|
var manifestLoading = AssetBundle.LoadFromFileAsync(filepath);
|
|
await manifestLoading.Await();
|
|
|
|
var assetBundle = manifestLoading.assetBundle;
|
|
var assetLoading = assetBundle.LoadAllAssetsAsync();
|
|
await assetLoading.Await();
|
|
|
|
return (CompatibilityAssetBundleManifest)assetLoading.allAssets[0];
|
|
}
|
|
|
|
private static async Task<CompatibilityAssetBundleManifest> GetManifestJson(string filepath)
|
|
{
|
|
var text = await VFS.ReadTextFileAsync(filepath);
|
|
|
|
/* we cannot parse directly as <string, BundleDetails>, because...
|
|
[Error : Unity Log] JsonSerializationException: Expected string when reading UnityEngine.Hash128 type, got 'StartObject' <>. Path '['assets/content/weapons/animations/simple_animations.bundle'].Hash', line 1, position 176.
|
|
...so we need to first convert it to a slimmed-down type (BundleItem), then convert back to BundleDetails.
|
|
*/
|
|
var raw = JsonConvert.DeserializeObject<Dictionary<string, BundleItem>>(text);
|
|
var converted = raw.ToDictionary(GetPairKey, GetPairValue);
|
|
|
|
// initialize manifest
|
|
var manifest = ScriptableObject.CreateInstance<CompatibilityAssetBundleManifest>();
|
|
manifest.SetResults(converted);
|
|
|
|
return manifest;
|
|
}
|
|
|
|
public static string GetPairKey(KeyValuePair<string, BundleItem> x)
|
|
{
|
|
return x.Key;
|
|
}
|
|
|
|
public static BundleDetails GetPairValue(KeyValuePair<string, BundleItem> x)
|
|
{
|
|
return new BundleDetails
|
|
{
|
|
FileName = x.Value.FileName,
|
|
Crc = x.Value.Crc,
|
|
Dependencies = x.Value.Dependencies
|
|
};
|
|
}
|
|
}
|
|
}
|