diff --git a/project/Aki.Custom/Patches/EasyAssetsPatch.cs b/project/Aki.Custom/Patches/EasyAssetsPatch.cs index 3ebeb97..c62465c 100644 --- a/project/Aki.Custom/Patches/EasyAssetsPatch.cs +++ b/project/Aki.Custom/Patches/EasyAssetsPatch.cs @@ -1,21 +1,19 @@ -using Aki.Reflection.Patching; -using Diz.Jobs; +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 System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; using Aki.Common.Utils; using Aki.Custom.Models; using Aki.Custom.Utils; -using DependencyGraph = DependencyGraph; +using Aki.Reflection.Patching; using Aki.Reflection.Utils; +using DependencyGraph = DependencyGraph; namespace Aki.Custom.Patches { @@ -38,40 +36,33 @@ namespace Aki.Custom.Patches protected override MethodBase GetTargetMethod() { - return typeof(EasyAssets).GetMethods(PatchConstants.PublicDeclaredFlags).SingleCustom(IsTargetMethod); - } - - private static bool IsTargetMethod(MethodInfo mi) - { - var parameters = mi.GetParameters(); - return (parameters.Length == 6 - && parameters[0].Name == "bundleLock" - && parameters[1].Name == "defaultKey" - && parameters[4].Name == "shouldExclude"); + return typeof(EasyAssets).GetMethod(nameof(EasyAssets.Create)); } [PatchPrefix] - private static bool PatchPrefix(ref Task __result, EasyAssets __instance, [CanBeNull] IBundleLock bundleLock, string defaultKey, string rootPath, + private static bool PatchPrefix(ref Task __result, GameObject gameObject, [CanBeNull] IBundleLock bundleLock, string defaultKey, string rootPath, string platformName, [CanBeNull] Func shouldExclude, [CanBeNull] Func bundleCheck) { - __result = Init(__instance, bundleLock, defaultKey, rootPath, platformName, shouldExclude, bundleCheck); + var easyAsset = gameObject.AddComponent(); + __result = Init(easyAsset, bundleLock, defaultKey, rootPath, platformName, shouldExclude, bundleCheck); + return false; } - private static async Task Init(EasyAssets instance, [CanBeNull] IBundleLock bundleLock, string defaultKey, string rootPath, string platformName, [CanBeNull] Func shouldExclude, Func bundleCheck) + private static async Task Init(EasyAssets instance, [CanBeNull] IBundleLock bundleLock, string defaultKey, string rootPath, string platformName, [CanBeNull] Func shouldExclude, Func bundleCheck) { // platform manifest - var path = $"{rootPath.Replace("file:///", string.Empty).Replace("file://", string.Empty)}/{platformName}/"; - var filepath = path + platformName; + var eftBundlesPath = $"{rootPath.Replace("file:///", string.Empty).Replace("file://", string.Empty)}/{platformName}/"; + var filepath = eftBundlesPath + platformName; var jsonfile = filepath + ".json"; - var manifest = File.Exists(jsonfile) + var manifest = VFS.Exists(jsonfile) ? await GetManifestJson(jsonfile) : await GetManifestBundle(filepath); // lazy-initialize aki bundles if (BundleManager.Bundles.Keys.Count == 0) { - await BundleManager.GetBundles(); + await BundleManager.DownloadManifest(); } // create bundles array from obfuscated type @@ -85,27 +76,43 @@ namespace Aki.Custom.Patches bundleLock = new BundleLock(int.MaxValue); } - // create bundle of obfuscated type var bundles = (IEasyBundle[])Array.CreateInstance(EasyBundleHelper.Type, 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)) + { + // 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[] { - bundleNames[i], + key, path, manifest, bundleLock, bundleCheck }); - - await JobScheduler.Yield(EJobPriority.Immediate); } // create dependency graph instance.Manifest = manifest; _bundlesField.SetValue(instance, bundles); instance.System = new DependencyGraph(bundles, defaultKey, shouldExclude); + + return instance; } // NOTE: used by: diff --git a/project/Aki.Custom/Patches/EasyBundlePatch.cs b/project/Aki.Custom/Patches/EasyBundlePatch.cs index f5e727c..b95593d 100644 --- a/project/Aki.Custom/Patches/EasyBundlePatch.cs +++ b/project/Aki.Custom/Patches/EasyBundlePatch.cs @@ -1,12 +1,12 @@ using System; -using Aki.Reflection.Patching; -using Diz.DependencyManager; -using UnityEngine.Build.Pipeline; using System.IO; using System.Linq; using System.Reflection; +using Diz.DependencyManager; +using UnityEngine.Build.Pipeline; using Aki.Custom.Models; using Aki.Custom.Utils; +using Aki.Reflection.Patching; namespace Aki.Custom.Patches { @@ -38,7 +38,7 @@ namespace Aki.Custom.Patches : bundle.Dependencies; // set path to either cache (HTTP) or mod (local) - filepath = BundleManager.GetBundlePath(bundle); + filepath = BundleManager.GetBundleFilePath(bundle); } _ = new EasyBundleHelper(__instance) diff --git a/project/Aki.Custom/Utils/BundleManager.cs b/project/Aki.Custom/Utils/BundleManager.cs index c2e13a9..a868bd3 100644 --- a/project/Aki.Custom/Utils/BundleManager.cs +++ b/project/Aki.Custom/Utils/BundleManager.cs @@ -10,74 +10,51 @@ namespace Aki.Custom.Utils { public static class BundleManager { + private const string CachePath = "user/cache/bundles/"; private static readonly ManualLogSource _logger; public static readonly ConcurrentDictionary Bundles; - public static string CachePath; static BundleManager() { _logger = Logger.CreateLogSource(nameof(BundleManager)); Bundles = new ConcurrentDictionary(); - CachePath = "user/cache/bundles/"; } public static string GetBundlePath(BundleItem bundle) { return RequestHandler.IsLocal ? $"{bundle.ModPath}/bundles/{bundle.FileName}" - : CachePath + bundle.FileName; + : CachePath; } - public static async Task GetBundles() + public static string GetBundleFilePath(BundleItem bundle) + { + return GetBundlePath(bundle) + bundle.FileName; + } + + public static async Task DownloadManifest() { // get bundles - var json = RequestHandler.GetJson("/singleplayer/bundles"); + var json = await RequestHandler.GetJsonAsync("/singleplayer/bundles"); var bundles = JsonConvert.DeserializeObject(json); - // register bundles - var toDownload = new ConcurrentBag(); - foreach (var bundle in bundles) { Bundles.TryAdd(bundle.FileName, bundle); - - if (await ShouldReaquire(bundle)) - { - // mark for download - toDownload.Add(bundle); - } - } - - if (RequestHandler.IsLocal) - { - // loading from local mods - _logger.LogInfo("CACHE: Loading all bundles from mods on disk."); - return; - } - else - { - // download bundles - // NOTE: assumes bundle keys to be unique - foreach (var bundle in toDownload) - { - // download bundle - var filepath = GetBundlePath(bundle); - var data = await RequestHandler.GetDataAsync($"/files/bundle/{bundle.FileName}"); - await VFS.WriteFileAsync(filepath, data); - } } } - private static async Task ShouldReaquire(BundleItem bundle) + public static async Task DownloadBundle(BundleItem bundle) { - if (RequestHandler.IsLocal) - { - // only handle remote bundles - return false; - } + var filepath = GetBundleFilePath(bundle); + var data = await RequestHandler.GetDataAsync($"/files/bundle/{bundle.FileName}"); + await VFS.WriteFileAsync(filepath, data); + } + public static async Task ShouldReaquire(BundleItem bundle) + { // read cache - var filepath = CachePath + bundle.FileName; + var filepath = GetBundleFilePath(bundle); if (VFS.Exists(filepath)) { @@ -88,7 +65,11 @@ namespace Aki.Custom.Utils if (crc == bundle.Crc) { // file is up-to-date - _logger.LogInfo($"CACHE: Loading locally {bundle.FileName}"); + var location = RequestHandler.IsLocal + ? "MOD" + : "CACHE"; + + _logger.LogInfo($"{location}: Loading locally {bundle.FileName}"); return false; } else