diff --git a/AssetHelper.slnx b/AssetHelper.slnx index 8291a26..83e59ae 100644 --- a/AssetHelper.slnx +++ b/AssetHelper.slnx @@ -1,4 +1,5 @@ + diff --git a/AssetHelper/AssetHelper.csproj b/AssetHelper/AssetHelper.csproj index 0c0e6af..d2e1cac 100644 --- a/AssetHelper/AssetHelper.csproj +++ b/AssetHelper/AssetHelper.csproj @@ -14,9 +14,6 @@ Silksong.AssetHelper $(NoWarn);MSB3270 - True - - True true @@ -41,7 +38,7 @@ all - + all runtime; build; native; contentfiles; analyzers @@ -72,9 +69,32 @@ - + + + + false + ../../AssetHelperLib/AssetHelperLib/bin/Debug/netstandard2.1 + + + + + + + + + $(AssetHelperLibFolder)/AssetHelperLib.dll + + + + + $(PkgAssetHelperLib)/lib/netstandard2.1 + + @@ -101,8 +121,8 @@ - - + + @@ -110,24 +130,13 @@ - + - + - + diff --git a/AssetHelper/CatalogTools/CatalogEntryUtils.cs b/AssetHelper/CatalogTools/CatalogEntryUtils.cs index 0ef10a0..04cba80 100644 --- a/AssetHelper/CatalogTools/CatalogEntryUtils.cs +++ b/AssetHelper/CatalogTools/CatalogEntryUtils.cs @@ -56,7 +56,7 @@ public static ContentCatalogDataEntry CreateBundleEntry( return bundleEntry; } - /// + /// public static ContentCatalogDataEntry CreateAssetEntry( string internalId, Type assetType, @@ -66,7 +66,7 @@ out string primaryKey { primaryKey = internalId; - return CreateAssetEntry(internalId, assetType, dependencyKeys, primaryKey); + return CreateAssetEntry(internalId, assetType, dependencyKeys, [primaryKey]); } /// @@ -75,33 +75,28 @@ out string primaryKey /// The internal ID of the asset. This is the name of the asset within the bundle. /// Unity type of the asset. Eg: GameObject /// Primary keys of the bundle dependencies. These should be in the catalog. - /// The primary key used to access the asset with Addressables. + /// The primary keys used to access the asset with Addressables. public static ContentCatalogDataEntry CreateAssetEntry( string internalId, Type assetType, List dependencyKeys, - string primaryKey + List primaryKeys ) { object[] deps = dependencyKeys.Cast().ToArray(); + object[] pkeys = primaryKeys.Cast().ToArray(); return new ContentCatalogDataEntry( assetType, internalId, "UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider", - new object[] { primaryKey }, + pkeys, deps, null ); } - /// - /// Create a catalog entry representing a child gameobject of - /// the gameObject loaded by parentPrimaryKey. - /// - /// The primary key of the parent. - /// The path of the child relative to the parent, with no leading slash. - /// The primary key of the added entry. + /// public static ContentCatalogDataEntry CreateChildGameObjectEntry( string parentPrimaryKey, string relativePath, @@ -128,30 +123,37 @@ out string primaryKey primaryKey = $"{parentPrimaryKey}/{relativePath}"; } - return CreateChildGameObjectEntry(parentPrimaryKey, relativePath, primaryKey); + return CreateChildGameObjectEntry(parentPrimaryKey, relativePath, [primaryKey]); } - /// + /// + /// Create a catalog entry representing a child gameobject of + /// the gameObject loaded by parentPrimaryKey. + /// + /// The primary key of the parent. + /// The path of the child relative to the parent, with no leading slash. + /// The primary keys of the added entry. public static ContentCatalogDataEntry CreateChildGameObjectEntry( string parentPrimaryKey, string relativePath, - string primaryKey + List primaryKeys ) { object[] deps = new object[] { parentPrimaryKey }; + object[] pkeys = primaryKeys.Cast().ToArray(); return new ContentCatalogDataEntry( typeof(GameObject), // Put the parent primary key to ensure the internal ID is unique $"{relativePath}/{ChildGameObjectProvider.InternalIdSeparator}/{parentPrimaryKey}", ChildGameObjectProvider.ClassProviderId, - new object[] { primaryKey }, + pkeys, deps, null ); } - /// + /// public static ContentCatalogDataEntry CreateEntryFromLocation( IResourceLocation location, out string primaryKey @@ -159,25 +161,27 @@ out string primaryKey { primaryKey = $"{nameof(AssetHelper)}:{location.PrimaryKey}"; - return CreateEntryFromLocation(location, primaryKey); + return CreateEntryFromLocation(location, [primaryKey]); } /// /// Create a catalog entry based on the given location. /// /// The location. - /// The primary key for the new catalog entry. + /// The primary keys for the new catalog entry. /// public static ContentCatalogDataEntry CreateEntryFromLocation( IResourceLocation location, - string primaryKey + List primaryKeys ) { + object[] pkeys = primaryKeys.Cast().ToArray(); + return new ContentCatalogDataEntry( location.ResourceType, location.InternalId, location.ProviderId, - new object[] { primaryKey }, + pkeys, null, location.Data ); diff --git a/AssetHelper/Core/AddressablesData.cs b/AssetHelper/Core/AddressablesData.cs index c171948..5294038 100644 --- a/AssetHelper/Core/AddressablesData.cs +++ b/AssetHelper/Core/AddressablesData.cs @@ -145,7 +145,7 @@ internal static bool TryLoadBundleKeys() } /// - /// Convert a name to an asset bundle key. + /// Convert a name to an asset bundle (addressables) key. /// public static string ToBundleKey(string name) { diff --git a/AssetHelper/Internal/CachedFileMetadata.cs b/AssetHelper/Internal/CachedFileMetadata.cs new file mode 100644 index 0000000..35d7aec --- /dev/null +++ b/AssetHelper/Internal/CachedFileMetadata.cs @@ -0,0 +1,24 @@ +using Silksong.AssetHelper.Core; + +namespace Silksong.AssetHelper.Internal; + +internal class CachedFileMetadata +{ + public required string SilksongVersion { get; init; } + + public required string PluginVersion { get; init; } + + public required string OSFolderName { get; init; } + + public static CachedFileMetadata CreateNew() + { + CachedFileMetadata data = new() + { + SilksongVersion = VersionData.SilksongVersion, + PluginVersion = AssetHelperPlugin.Version, + OSFolderName = AssetPaths.OSFolderName, + }; + + return data; + } +} diff --git a/AssetHelper/Internal/CachedObject.cs b/AssetHelper/Internal/CachedObject.cs index 5aade0d..eb1785c 100644 --- a/AssetHelper/Internal/CachedObject.cs +++ b/AssetHelper/Internal/CachedObject.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using System.IO; using Newtonsoft.Json; using Silksong.AssetHelper.Core; @@ -15,28 +16,31 @@ internal class CachedObject { private CachedObject() { } - [JsonProperty] - public required string SilksongVersion { get; init; } - - [JsonProperty] - public required string PluginVersion { get; init; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] + [DefaultValue(null)] + public required CachedFileMetadata Metadata { get; init; } [JsonProperty] public required T Value { get; set; } private bool IsValid() { - if (SilksongVersion == null || PluginVersion == null) + if (Metadata == null || Metadata.SilksongVersion == null || Metadata.PluginVersion == null) + { + return false; + } + + if (Metadata.OSFolderName != AssetPaths.OSFolderName) { return false; } - if (VersionData.SilksongVersion != SilksongVersion) + if (VersionData.SilksongVersion != Metadata.SilksongVersion) { return false; } - if (!VersionData.EarliestAcceptableGeneralVersion.AllowCachedData(this.PluginVersion)) + if (!VersionData.EarliestAcceptableGeneralVersion.AllowCachedData(Metadata.PluginVersion)) { return false; } @@ -85,8 +89,7 @@ out CachedObject? fromCache CachedObject created = new() { - SilksongVersion = VersionData.SilksongVersion, - PluginVersion = AssetHelperPlugin.Version, + Metadata = CachedFileMetadata.CreateNew(), Value = createDefault(), }; created.SerializeToFile(filePath); diff --git a/AssetHelper/ManagedAssets/ManagedAsset.cs b/AssetHelper/ManagedAssets/ManagedAsset.cs index f1d7cfc..5b4ee80 100644 --- a/AssetHelper/ManagedAssets/ManagedAsset.cs +++ b/AssetHelper/ManagedAssets/ManagedAsset.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using Silksong.AssetHelper.Plugin; using UnityEngine; using UnityEngine.AddressableAssets; @@ -15,28 +14,15 @@ namespace Silksong.AssetHelper.ManagedAssets; /// /// The type of the asset to load. /// The Addressables Key used to load the asset. -public class ManagedAsset(string key) : IManagedAsset +public class ManagedAsset(string key) : ManagedAssetBase { /// /// The Addressables Key used to load the asset. /// public string Key { get; } = key; - private AsyncOperationHandle? _handle; - - /// - /// The operation handle containing the asset. This will be null if the asset has not been loaded. - /// - /// This handle should not be unloaded manually; instead, the method - /// on this instance should be used. - /// - /// Exception thrown if this instance has not been loaded when accessing the handle. - public AsyncOperationHandle Handle => - _handle.HasValue - ? _handle.Value - : throw new InvalidOperationException( - $"Addressable asset with key {Key} must be loaded before accessing the handle!" - ); + /// + protected internal override string Identifier => Key; /// /// Construct an instance for the given scene asset. @@ -97,54 +83,13 @@ public static ManagedAsset FromNonSceneAsset(string assetName, string? bundle return new(key); } - /// - /// Load the underlying asset. This operation is idempotent. - /// - /// This should be called prior to using the asset. - /// - /// The handle used to load the asset. - public AsyncOperationHandle Load() - { - if (_handle == null) - { - _handle = Addressables.LoadAssetAsync(Key); - } - return Handle; - } - - object? IManagedAsset.Load() => Load(); - - /// - /// Unload the underlying asset. This operation is idempotent. - /// - /// This should not be called if the asset is still in use. - /// - public void Unload() - { - if (_handle.HasValue) - { - Addressables.Release(_handle.Value); - _handle = null; - } - } - - /// - /// Whether or not the asset has finished loading. - /// - public bool IsLoaded => HasBeenLoaded && Handle.IsDone; - - /// - /// Whether or not the asset load request has been made. - /// - public bool HasBeenLoaded => _handle.HasValue; + /// + protected override AsyncOperationHandle DoLoad() + => Addressables.LoadAssetAsync(Key); /// /// Create a new instance that wraps the same underlying asset. /// The new instance starts out unloaded. - /// - /// If a library has defined a instance but it is not - /// guaranteed to be loaded, then you should clone the instance and load the clone - /// rather than loading the instance owned by the library. /// public ManagedAsset Clone() { diff --git a/AssetHelper/ManagedAssets/ManagedAssetBase.cs b/AssetHelper/ManagedAssets/ManagedAssetBase.cs new file mode 100644 index 0000000..d770d26 --- /dev/null +++ b/AssetHelper/ManagedAssets/ManagedAssetBase.cs @@ -0,0 +1,79 @@ +using System; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace Silksong.AssetHelper.ManagedAssets; + +/// +/// Base class for objects that wrap an Addressables key and load the underlying asset. +/// +/// The type parameter of the AsyncOperationHandle loaded by Addressables. +public abstract class ManagedAssetBase : IManagedAsset +{ + /// + /// An identifier associated with this asset. + /// + protected internal abstract string Identifier { get; } + + private AsyncOperationHandle? _handle; + + /// + /// The operation handle containing the asset. This will be null if the asset has not been loaded. + /// + /// This handle should not be unloaded manually; instead, the method + /// on this instance should be used. + /// + /// Exception thrown if this instance has not been loaded when accessing the handle. + public AsyncOperationHandle Handle => + _handle.HasValue + ? _handle.Value + : throw new InvalidOperationException( + $"Addressable asset with identifier {Identifier} must be loaded before accessing the handle!" + ); + + /// + /// Load the underlying asset. This operation is idempotent. + /// + /// This should be called prior to using the asset. + /// + /// The handle used to load the asset. + public AsyncOperationHandle Load() + { + if (_handle == null) + { + _handle = DoLoad(); + } + return Handle; + } + + /// + /// Function to load the underlying asset. + /// + protected abstract AsyncOperationHandle DoLoad(); + + object? IManagedAsset.Load() => Load(); + + /// + /// Unload the underlying asset. This operation is idempotent. + /// + /// This should not be called if the asset is still in use. + /// + public void Unload() + { + if (_handle.HasValue) + { + Addressables.Release(_handle.Value); + _handle = null; + } + } + + /// + /// Whether or not the asset has finished loading. + /// + public bool IsLoaded => HasBeenLoaded && Handle.IsDone; + + /// + /// Whether or not the asset load request has been made. + /// + public bool HasBeenLoaded => _handle.HasValue; +} diff --git a/AssetHelper/ManagedAssets/ManagedAssetBundle.cs b/AssetHelper/ManagedAssets/ManagedAssetBundle.cs new file mode 100644 index 0000000..c091a67 --- /dev/null +++ b/AssetHelper/ManagedAssets/ManagedAssetBundle.cs @@ -0,0 +1,41 @@ +using Silksong.AssetHelper.Core; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceProviders; + +namespace Silksong.AssetHelper.ManagedAssets; + +/// +/// Class wrapping an asset bundle that can be loaded by Addressables. +/// +/// Typically assets should be loaded directly (e.g. via a ) +/// because loading bundles in this way may not load dependencies. +/// +/// The name of the bundle, given as a path relative to +/// the StandaloneX folder. +public class ManagedAssetBundle(string bundleName) : ManagedAssetBase +{ + /// + /// The name of the bundle, given as a path relative to + /// the StandaloneX folder. + /// + public string BundleName { get; } = bundleName; + + /// + protected internal override string Identifier => BundleName; + + /// + protected override AsyncOperationHandle DoLoad() + => Addressables.LoadAssetAsync( + AddressablesData.ToBundleKey(BundleName)); + + /// + /// Create a new instance that wraps the same underlying asset. + /// The new instance starts out unloaded. + /// + public ManagedAssetBundle Clone() + { + return new(BundleName); + } + +} diff --git a/AssetHelper/ManagedAssets/ManagedAssetExtensions.cs b/AssetHelper/ManagedAssets/ManagedAssetExtensions.cs index b16601f..262d5e2 100644 --- a/AssetHelper/ManagedAssets/ManagedAssetExtensions.cs +++ b/AssetHelper/ManagedAssets/ManagedAssetExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; namespace Silksong.AssetHelper.ManagedAssets; @@ -10,6 +11,7 @@ public static class ManagedAssetExtensions /// /// Instantiate the asset managed by this instance. /// + /// If the asset has not finished loading. public static T InstantiateAsset(this ManagedAsset asset) where T : UObject { @@ -35,12 +37,12 @@ public static T InstantiateAsset(this ManagedAsset asset) /// This method will write an error message to the log if there was an exception during loading, /// but this method will not throw. /// - public static void EnsureLoaded(this ManagedAsset asset) + public static void EnsureLoaded(this ManagedAssetBase asset) { if (!asset.HasBeenLoaded) { AssetHelperPlugin.InstanceLogger.LogWarning( - $"{nameof(EnsureLoaded)} has been called on {asset.Key} before loading the asset!"); + $"{nameof(EnsureLoaded)} has been called on {asset.Identifier} before loading the asset!"); asset.Load(); } if (!asset.IsLoaded) @@ -50,10 +52,17 @@ public static void EnsureLoaded(this ManagedAsset asset) if (asset.Handle.OperationException != null) { - AssetHelperPlugin.InstanceLogger.LogError($"Operation exception when loading asset with key {asset.Key}\n" + asset.Handle.OperationException); + AssetHelperPlugin.InstanceLogger.LogError( + $"Operation exception when loading asset {asset.Identifier}\n" + asset.Handle.OperationException); } } + // Kept for backward compatibility + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static void EnsureLoaded(this ManagedAsset asset) + => EnsureLoaded((ManagedAssetBase)asset); + /// /// Instantiate an asset in this group accessed by key. /// @@ -67,4 +76,35 @@ public static T InstantiateAsset(this ManagedAssetGroup group, string key) return UObject.Instantiate(group[key].Result); } + + /// + /// Instantiate an asset from a . + /// If multiple assets match the predicate then the first will be instantiated; + /// which one this is will be arbitrary. + /// + /// + /// + /// Function used to check if a given asset is the one being looked for. Commonly + /// this will inspect the children or components of the given asset. + /// This function should not mutate the argument. + /// + /// If the asset has not finished loading. + /// If none of the assets match the predicate. + public static T InstantiateAsset(this ManagedAssetList asset, Func predicate) where T : UObject + { + if (!asset.IsLoaded) + { + throw new InvalidOperationException($"The asset has not finished loading!"); + } + + foreach (T t in asset.Handle.Result) + { + if (predicate(t)) + { + return UObject.Instantiate(t); + } + } + + throw new ArgumentException($"No matching asset for managed asset list with key {asset.Key} was found!"); + } } diff --git a/AssetHelper/ManagedAssets/ManagedAssetList.cs b/AssetHelper/ManagedAssets/ManagedAssetList.cs new file mode 100644 index 0000000..1ee82e1 --- /dev/null +++ b/AssetHelper/ManagedAssets/ManagedAssetList.cs @@ -0,0 +1,75 @@ +using Silksong.AssetHelper.Plugin; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace Silksong.AssetHelper.ManagedAssets; + +/// +/// Class that wraps the load of a group of addressable assets with the same primary key. +/// +/// This is commonly used when multiple game objects in a (repacked) scene have the same name. +/// +/// +/// +public class ManagedAssetList(string key) : ManagedAssetBase> +{ + /// + /// The Addressables Key used to load the asset. + /// + public string Key { get; } = key; + + /// + protected internal override string Identifier => Key; + + + /// + protected override AsyncOperationHandle> DoLoad() + => Addressables.LoadAssetsAsync(Key); + + /// + /// Create a new instance that wraps the same underlying asset. + /// The new instance starts out unloaded. + /// + public ManagedAssetList Clone() + { + return new(Key); + } + + /// + /// Construct an instance for the given scene asset. + /// + /// Doing this during your plugin's Awake method will cause it to be requested automatically. + /// via the API. + /// + /// The name of the scene. + /// The hierarchical path to the game object. + /// + public static ManagedAssetList FromSceneAsset(string sceneName, string objPath) + { + if (typeof(T) != typeof(GameObject)) + { + AssetHelperPlugin.InstanceLogger.LogWarning( + $"{nameof(ManagedAssetList<>)} instances for scene assets should have GameObject as the type argument!" + ); + } + + if (AssetRequestAPI.RequestApiAvailable) + { + AssetRequestAPI.RequestSceneAsset(sceneName, objPath); + } + else + { + if (!AssetRequestAPI.Request.SceneAssets.TryGetValue(sceneName.ToLowerInvariant(), out HashSet objNames) + || !objNames.Contains(objPath)) + { + AssetHelperPlugin.InstanceLogger.LogWarning( + $"Constructing managed asset list from scene {sceneName}, {objPath} after Awake may not work unless the asset has been requested first!"); + } + } + + string key = CatalogKeys.GetKeyForSceneAsset(sceneName, objPath); + return new(key); + } +} diff --git a/AssetHelper/Plugin/AssetRequest.cs b/AssetHelper/Plugin/AssetRequest.cs index 23f095e..c6015f6 100644 --- a/AssetHelper/Plugin/AssetRequest.cs +++ b/AssetHelper/Plugin/AssetRequest.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Text; using Newtonsoft.Json; +using Silksong.AssetHelper.Internal; namespace Silksong.AssetHelper.Plugin; @@ -9,6 +9,7 @@ internal class AssetRequest { public Dictionary> SceneAssets { get; set; } = []; + [JsonConverter(typeof(DictListConverter<(string bundleName, string assetName), Type>))] public Dictionary<(string bundleName, string assetName), Type> NonSceneAssets { get; set; } = []; [JsonIgnore] diff --git a/AssetHelper/Plugin/BaseStartupTask.cs b/AssetHelper/Plugin/BaseStartupTask.cs new file mode 100644 index 0000000..f95d030 --- /dev/null +++ b/AssetHelper/Plugin/BaseStartupTask.cs @@ -0,0 +1,24 @@ +using Silksong.AssetHelper.Plugin.LoadingPage; +using System.Collections; + +namespace Silksong.AssetHelper.Plugin; + +/// +/// Base class for tasks that happen at startup. +/// +public abstract class BaseStartupTask +{ + // Note - for tasks defined by AssetHelper the enumerator will be executed safely + // and the objects yielded will be passed to unity. + /// + /// Run the startup task. The enumerator will be executed by unity. + /// + /// A loading screen. + /// + /// Tasks executed this way will usually be fairly slow and cpu-intensive tasks. + /// There is not a good way to decide when to yield, except that: + /// - After updating the loading screen, you should yield or the change may not be visible + /// - You should yield after every chunk of work, so that Unity knows your program hasn't hung + /// + public abstract IEnumerator Run(ILoadingScreen loadingScreen); +} diff --git a/AssetHelper/Plugin/CatalogKeys.cs b/AssetHelper/Plugin/CatalogKeys.cs index 33bb968..2ede473 100644 --- a/AssetHelper/Plugin/CatalogKeys.cs +++ b/AssetHelper/Plugin/CatalogKeys.cs @@ -40,4 +40,16 @@ public static string GetKeyForNonSceneAsset(string assetName) { return $"{NonSceneCatalogId}/{assetName}"; } + + /// + /// Get the primary key for a scene bundle asset with a particular transform path ID. + /// + /// + /// This is essentially a secondary primary key for the asset, guaranteed to be unique, + /// but not intended to be used by clients. + /// + internal static string GetKeyForAssetAtTransform(string sceneName, long tPathId) + { + return $"{SceneCatalogId}/RepackedTransforms/{sceneName}/t{tPathId}.prefab"; + } } diff --git a/AssetHelper/Plugin/CatalogMetadata.cs b/AssetHelper/Plugin/CatalogMetadata.cs index c3e342a..a24f9eb 100644 --- a/AssetHelper/Plugin/CatalogMetadata.cs +++ b/AssetHelper/Plugin/CatalogMetadata.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using Newtonsoft.Json; using Silksong.AssetHelper.Internal; @@ -7,9 +8,9 @@ namespace Silksong.AssetHelper.Plugin; internal class CatalogMetadata { - public string SilksongVersion { get; set; } = VersionData.SilksongVersion; - - public string PluginVersion { get; set; } = AssetHelperPlugin.Version; + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] + [DefaultValue(null)] + public CachedFileMetadata Metadata { get; set; } = CachedFileMetadata.CreateNew(); } /// diff --git a/AssetHelper/Plugin/CustomCatalogBuilder.cs b/AssetHelper/Plugin/CustomCatalogBuilder.cs index 08a3031..e24682b 100644 --- a/AssetHelper/Plugin/CustomCatalogBuilder.cs +++ b/AssetHelper/Plugin/CustomCatalogBuilder.cs @@ -5,6 +5,7 @@ using AssetHelperLib.Repacking; using Silksong.AssetHelper.CatalogTools; using Silksong.AssetHelper.Core; +using Silksong.AssetHelper.Plugin.Tasks; using UnityEngine; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.ResourceLocations; @@ -55,7 +56,7 @@ public CustomCatalogBuilder(string primaryKeyPrefix = "AssetHelper") string primaryKey = $"{_primaryKeyPrefix}/DependencyBundles/{bundleName}"; ContentCatalogDataEntry entry = CatalogEntryUtils.CreateEntryFromLocation( location, - primaryKey + [primaryKey] ); _baseBundleEntries.Add(bundleName, entry); @@ -82,7 +83,8 @@ public bool TryDeclareBundleDep(string bundleName, [NotNullWhen(true)] out strin return true; } - public void AddRepackedSceneData(string sceneName, RepackedBundleData data, string bundlePath, string? serializedBundlePath = null) + public void AddRepackedSceneData( + string sceneName, RepackedBundleData data, SceneCatalogInfo info, string bundlePath, string? serializedBundlePath = null) { // Create an entry for the bundle string repackedSceneBundleKey = $"{_primaryKeyPrefix}/SceneBundles/{sceneName}"; @@ -116,13 +118,29 @@ string dep in BundleMetadata.DetermineCatalogDeps( } // Create entries for the assets - foreach ((string containerPath, string objPath) in data.GameObjectAssets ?? []) + foreach (var rootGoInfo in info.RootGameObjects) { ContentCatalogDataEntry entry = CatalogEntryUtils.CreateAssetEntry( - containerPath, + rootGoInfo.ContainerPath, typeof(GameObject), dependencyKeys, - $"{_primaryKeyPrefix}/Assets/{sceneName}/{objPath}" + [ + $"{_primaryKeyPrefix}/Assets/{sceneName}/{rootGoInfo.ObjPath}", + CatalogKeys.GetKeyForAssetAtTransform(sceneName, rootGoInfo.TransformPathId) + ] + ); + _addedEntries.Add(entry); + } + + foreach (var childGoInfo in info.ChildGameObjects) + { + ContentCatalogDataEntry entry = CatalogEntryUtils.CreateChildGameObjectEntry( + CatalogKeys.GetKeyForAssetAtTransform(sceneName, childGoInfo.AncestorTransformPathId), + childGoInfo.RelativePath, + [ + $"{_primaryKeyPrefix}/Assets/{sceneName}/{childGoInfo.ObjPath}", + CatalogKeys.GetKeyForAssetAtTransform(sceneName, childGoInfo.TransformPathId) + ] ); _addedEntries.Add(entry); } @@ -157,7 +175,7 @@ public void AddAssets(string bundle, List<(string asset, Type assetType)> data) asset, assetType, dependencyKeys, - $"{_primaryKeyPrefix}/{asset}" + [$"{_primaryKeyPrefix}/{asset}"] ); _addedEntries.Add(entry); } diff --git a/AssetHelper/Plugin/LoadingPage/ILoadingScreen.cs b/AssetHelper/Plugin/LoadingPage/ILoadingScreen.cs index 0603187..2146ca3 100644 --- a/AssetHelper/Plugin/LoadingPage/ILoadingScreen.cs +++ b/AssetHelper/Plugin/LoadingPage/ILoadingScreen.cs @@ -5,14 +5,32 @@ namespace Silksong.AssetHelper.Plugin.LoadingPage; /// /// Interface defining the contract for a loading screen. /// -internal interface ILoadingScreen +public interface ILoadingScreen { + /// + /// Set the large text above the progress bar. + /// Typically used to describe the operation that is going on (e.g. "Repacking Scenes"). + /// public void SetText(string text); + /// + /// Set the small text below the progress bar. + /// Typically used for more detailed updates (e.g. a particular scene name). + /// This will often be left blank. + /// + /// public void SetSubtext(string text); + /// + /// Set the progress of the progress bar. + /// + /// A float between 0 and 1 indicating the progress. public void SetProgress(float progress); + /// + /// Set whether the screen should be visible. + /// This method should rarely be used. + /// public void SetVisible(bool visible); } diff --git a/AssetHelper/Plugin/RepackedBundleMetadata.cs b/AssetHelper/Plugin/RepackedBundleMetadata.cs index 25ba018..585dc4a 100644 --- a/AssetHelper/Plugin/RepackedBundleMetadata.cs +++ b/AssetHelper/Plugin/RepackedBundleMetadata.cs @@ -1,23 +1,23 @@ using AssetHelperLib.Repacking; +using Newtonsoft.Json; using Silksong.AssetHelper.Internal; +using Silksong.AssetHelper.Plugin.Tasks; +using System.ComponentModel; namespace Silksong.AssetHelper.Plugin; /// /// Data about a repacked scene bundle used by AssetHelper. /// -public sealed class RepackedSceneBundleData +internal sealed class RepackedSceneBundleData { /// - /// The Silksong version used to create the bundle. + /// The metadata when creating the bundle. /// - public string SilksongVersion { get; init; } = VersionData.SilksongVersion; - - /// - /// The Asset Helper version used to create the bundle. - /// - public string PluginVersion { get; init; } = AssetHelperPlugin.Version; - + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] + [DefaultValue(null)] + public CachedFileMetadata Metadata { get; init; } = CachedFileMetadata.CreateNew(); + /// /// The name of the scene used to generate the bundle. /// @@ -32,4 +32,9 @@ public sealed class RepackedSceneBundleData /// The data generated for the repacked bundle. /// public RepackedBundleData? Data { get; set; } = null; + + /// + /// Info used to build the catalog entries for the repacked bundle. + /// + public SceneCatalogInfo? CatalogInfo { get; set; } = null; } diff --git a/AssetHelper/Plugin/Tasks/BaseStartupTask.cs b/AssetHelper/Plugin/Tasks/BaseStartupTask.cs deleted file mode 100644 index cbdc84e..0000000 --- a/AssetHelper/Plugin/Tasks/BaseStartupTask.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Silksong.AssetHelper.Plugin.LoadingPage; -using System.Collections; - -namespace Silksong.AssetHelper.Plugin.Tasks; - -/// -/// Base class for tasks that happen at startup. -/// -internal abstract class BaseStartupTask -{ - /// - /// Run the startup task. The objects yielded by this enumerator will - /// be passed through to Unity. - /// - /// A loading screen. - public abstract IEnumerator Run(ILoadingScreen loadingScreen); -} diff --git a/AssetHelper/Plugin/Tasks/NonSceneCatalog.cs b/AssetHelper/Plugin/Tasks/NonSceneCatalog.cs index cdc3aab..4ebfe47 100644 --- a/AssetHelper/Plugin/Tasks/NonSceneCatalog.cs +++ b/AssetHelper/Plugin/Tasks/NonSceneCatalog.cs @@ -59,8 +59,10 @@ private IEnumerator CreateNonSceneAssetCatalog(ILoadingScreen screen) bool shouldWriteCatalog = false; if (JsonExtensions.TryLoadFromFile(catalogMetadataPath, out NonSceneCatalogMetadata? existingCatalogData) - && existingCatalogData.SilksongVersion == VersionData.SilksongVersion - && VersionData.EarliestAcceptableNonSceneCatalogVersion.AllowCachedData(existingCatalogData.PluginVersion)) + && existingCatalogData.Metadata != null + && existingCatalogData.Metadata.SilksongVersion == VersionData.SilksongVersion + && VersionData.EarliestAcceptableNonSceneCatalogVersion.AllowCachedData(existingCatalogData.Metadata.PluginVersion) + && existingCatalogData.Metadata.OSFolderName == AssetPaths.OSFolderName) { toCatalog = existingCatalogData.CatalogAssets; } diff --git a/AssetHelper/Plugin/Tasks/SceneCatalogInfo.cs b/AssetHelper/Plugin/Tasks/SceneCatalogInfo.cs new file mode 100644 index 0000000..d695ffb --- /dev/null +++ b/AssetHelper/Plugin/Tasks/SceneCatalogInfo.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Silksong.AssetHelper.Plugin.Tasks; + +/// +/// Class containing the data used to construct the catalog entries for a single repacked scene. +/// +internal class SceneCatalogInfo +{ + public record GameObjectInfo(string ObjPath, string ContainerPath, long TransformPathId); + public record ChildGameObjectInfo( + string ObjPath, + long TransformPathId, + string AncestorObjPath, + string RelativePath, + long AncestorTransformPathId); + + /// + /// Game objects which are at the root of the repacked bundle (not necessarily a root in the original scene). + /// + public List RootGameObjects { get; init; } = []; + + /// + /// Game objects which are not at the root in the repacked bundle, but should still be made addressable. + /// + public List ChildGameObjects { get; init; } = []; + + public IEnumerable LoadableAssets => [ + .. RootGameObjects.Select(x => x.ObjPath), + .. ChildGameObjects.Select(x => x.ObjPath) + ]; +} diff --git a/AssetHelper/Plugin/Tasks/SceneRepacking.cs b/AssetHelper/Plugin/Tasks/SceneRepacking.cs index 86f1fa6..d3a8beb 100644 --- a/AssetHelper/Plugin/Tasks/SceneRepacking.cs +++ b/AssetHelper/Plugin/Tasks/SceneRepacking.cs @@ -1,161 +1,360 @@ -using AssetHelperLib.Repacking; -using Silksong.AssetHelper.CatalogTools; -using Silksong.AssetHelper.Internal; -using AssetHelperLib.Util; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using AssetHelperLib.BundleTools; +using AssetHelperLib.PreloadTable; +using AssetHelperLib.Repacking; using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.ResourceProviders; -using RepackDataCollection = System.Collections.Generic.Dictionary; using Silksong.AssetHelper.Core; -using AssetHelperLib.PreloadTable; -using CPPCache = System.Collections.Generic.Dictionary; -using System; +using Silksong.AssetHelper.Internal; using Silksong.AssetHelper.Plugin.LoadingPage; +using CPPCache = System.Collections.Generic.Dictionary; +using GoInfo = AssetHelperLib.BundleTools.GameObjectLookup.GameObjectInfo; +using RepackDataCollection = System.Collections.Generic.Dictionary; namespace Silksong.AssetHelper.Plugin.Tasks; -/// -/// Run the routine to repack scene assets, create a catalog and load it. -/// internal class SceneRepacking : BaseStartupTask { - // Path to the scene catalog .bin file private static string SceneCatalogPath => Path.Combine(AssetPaths.CatalogFolder, $"{CatalogKeys.SceneCatalogId}.bin"); - // (scene, gameObjs) that need to be repacked - private Dictionary> _toRepack = []; + private static string CatalogMetadataPath => Path.ChangeExtension(SceneCatalogPath, ".json"); // Data about the repacked assets in the bundles on disk private RepackDataCollection _repackData = []; - private bool _didRepack = false; - public override IEnumerator Run(ILoadingScreen loadingScreen) { - return RepackAndCatalogScenes(loadingScreen); - } + if (AssetRequestAPI.Request.SceneAssets.Count == 0) + { + AssetHelperPlugin.InstanceLogger.LogInfo("Not running scene repack operation: no scenes in request"); + } - private IEnumerator RepackAndCatalogScenes(ILoadingScreen bar) - { - IEnumerator repack = PrepareAndRun(bar); + // Prepare operation + if (JsonExtensions.TryLoadFromFile(AssetPaths.RepackedSceneBundleMetadataPath, out RepackDataCollection? repackData)) + { + _repackData = repackData; + } + else + { + _repackData = []; + } - bar.SetText(LanguageKeys.REPACKING_SCENE.GetLocalized()); - yield return null; + // This block sets _repackData to include only the data for scenes that do not need to be repacked + _repackData = _repackData + // If some data is missing, we must repack + .Where(kvp => kvp.Value.CatalogInfo is not null && kvp.Value.Data is not null) + // If the metadata (plugin version, bundle hash) changes, we must repack + .Where(kvp => !MetadataMismatch(kvp.Key, kvp.Value)) + // If the bundle does not exist, we must repack + .Where(kvp => File.Exists(GetBundlePathForScene(kvp.Key))) + // If the existing data does not support everything in the request, we repack + .Where(kvp => CanLoadAll(kvp.Value.CatalogInfo!, kvp.Key)) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - while (repack.MoveNext()) + List scenesToRepack; + + // Repack scenes that need to be repacked { - // Yield after each repack op is done + loadingScreen.Reset(); + loadingScreen.SetText(LanguageKeys.REPACKING_SCENE.GetLocalized()); yield return null; - } - bar.Reset(); - bar.SetText(LanguageKeys.BULDING_SCENE.GetLocalized()); - yield return null; + CachedObject SyncedCppCache = CachedObject.CreateSynced( + "container_pointer_preloads_cache.json", () => new(), mutable: true, out IDisposable? cppSyncHandle); - IEnumerator catalogCreate = CreateSceneAssetCatalog(_repackData, bar); - while (catalogCreate.MoveNext()) - { + ContainerPointerPreloads cpp = new(ResolveCab) { Cache = SyncedCppCache.Value }; + PreloadTableResolver resolver = new([new DefaultPreloadTableResolver(), cpp]); + SceneRepacker repacker = new StrippedSceneRepacker(resolver); + + scenesToRepack = AssetRequestAPI.Request.SceneAssets.Keys + .Where(x => !_repackData.ContainsKey(x)) + .ToList(); + + Stopwatch mainSw = Stopwatch.StartNew(); + + int total = scenesToRepack.Count; + int count = 0; + + AssetHelperPlugin.InstanceLogger.LogInfo($"Repacking {total} scenes"); + + foreach (string scene in scenesToRepack) + { + HashSet request = AssetRequestAPI.Request.SceneAssets[scene]; + Stopwatch sw = Stopwatch.StartNew(); + AssetHelperPlugin.InstanceLogger.LogInfo($"Repacking {request.Count} objects in scene {scene}"); + loadingScreen.SetSubtext(scene); + + RepackedSceneBundleData sceneRepackData = DoRepack(scene, request, repacker); + + sw.Stop(); + _repackData[scene] = sceneRepackData; + // Serialize after each step so that interrupted operations can continue + _repackData.SerializeToFile(AssetPaths.RepackedSceneBundleMetadataPath); + AssetHelperPlugin.InstanceLogger.LogInfo($"Repacked {scene} in {sw.ElapsedMilliseconds} ms"); + + count += 1; + loadingScreen.SetProgress((float)count / (float)total); + + yield return null; + } + + mainSw.Stop(); + AssetHelperPlugin.InstanceLogger.LogInfo($"Finished scene repacking after {mainSw.ElapsedMilliseconds} ms"); + + loadingScreen.Reset(); + + // Save and dispose the cpp cache + cppSyncHandle?.Dispose(); yield return null; } - yield return null; - - // Only load the catalog if anyone's requested scene assets - if (_repackData.Count > 0) + // Write catalog + // We can skip the catalog writing if nothing was freshly repacked + // and the catalog exists with the correct metadata. { - bar.SetText(LanguageKeys.LOADING_SCENE.GetLocalized()); - yield return null; + bool shouldWriteCatalog = (scenesToRepack.Count > 0) || MustWriteCatalog(); + + if (shouldWriteCatalog) + { + loadingScreen.Reset(); + loadingScreen.SetText(LanguageKeys.BULDING_SCENE.GetLocalized()); + yield return null; + + AssetHelperPlugin.InstanceLogger.LogInfo($"Creating catalog"); + Stopwatch sw = Stopwatch.StartNew(); + + CustomCatalogBuilder cbr = new(CatalogKeys.SceneCatalogId); + + foreach ((string scene, RepackedSceneBundleData data) in _repackData) + { + if (data.Data == null || data.CatalogInfo == null) continue; + string bundlePath = GetBundlePathForScene(scene); + string bundleFileName = Path.GetFileName(bundlePath); + string serializedBundlePath = $"{GetSerializedBundleDirPrefix()}/{bundleFileName}"; - AssetHelperPlugin.InstanceLogger.LogInfo($"Loading scene catalog"); - AsyncOperationHandle catalogLoadOp = Addressables.LoadContentCatalogAsync(SceneCatalogPath); - yield return catalogLoadOp; - AssetRequestAPI.SceneAssetLocator = catalogLoadOp.Result; + cbr.AddRepackedSceneData(scene, data.Data, data.CatalogInfo, bundlePath, serializedBundlePath); + } + sw.Stop(); + AssetHelperPlugin.InstanceLogger.LogInfo($"Prepared catalog in {sw.ElapsedMilliseconds} ms"); + + loadingScreen.SetText(LanguageKeys.WRITING_SCENE.GetLocalized()); + loadingScreen.SetProgress(0); + yield return null; + + sw = Stopwatch.StartNew(); + + int catCount = 0; + using IEnumerator serializationRoutine = cbr.BuildRoutine(); + while (serializationRoutine.MoveNext()) + { + float progress = serializationRoutine.Current; + catCount++; + if (catCount % 10 == 0) + { + loadingScreen.SetProgress(progress); + yield return null; + } + } + + sw.Stop(); + AssetHelperPlugin.InstanceLogger.LogInfo($"Finished writing catalog in {sw.ElapsedMilliseconds} ms"); + + SceneCatalogMetadata metadata = new(); + metadata.SerializeToFile(CatalogMetadataPath); + } + else + { + AssetHelperPlugin.InstanceLogger.LogInfo($"Not creating catalog"); + } } + + yield return null; + + // Load catalog + loadingScreen.SetText(LanguageKeys.LOADING_SCENE.GetLocalized()); + yield return null; + + AssetHelperPlugin.InstanceLogger.LogInfo($"Loading scene catalog"); + AsyncOperationHandle catalogLoadOp = Addressables.LoadContentCatalogAsync(SceneCatalogPath); + yield return catalogLoadOp; + AssetRequestAPI.SceneAssetLocator = catalogLoadOp.Result; + yield return null; } - private IEnumerator PrepareAndRun(ILoadingScreen bar) + private RepackedSceneBundleData DoRepack( + string scene, + HashSet request, + SceneRepacker repacker + ) { - Prepare(); + Dictionary>> transformSeqs = null!; - if (_toRepack.Count > 0) + string containerPrefix = $"{nameof(AssetHelper)}/{scene}"; + + RepackingParams rParams = new() { - _didRepack = true; - return RunRepacking(bar); - } - else + SceneBundlePath = AssetPaths.GetScenePath(scene), + ObjectNames = request.ToList(), + ContainerPrefix = containerPrefix, + OutBundlePath = GetBundlePathForScene(scene), + LateCallback = (ctx, data) => transformSeqs = BuildTransformSequences(ctx, data, request), + }; + RepackedBundleData repackData = repacker.Repack(rParams); + + string? hash = null; + if (AddressablesData.TryGetLocationForScene(scene, out IResourceLocation? location) && location.Data is AssetBundleRequestOptions opts) { - return Enumerable.Empty().GetEnumerator(); + hash = opts.Hash; } + + SceneCatalogInfo catInfo = BuildSceneCatalogInfo(repackData, transformSeqs, containerPrefix); + + RepackedSceneBundleData sceneRepackData = new() + { + SceneName = scene, + BundleHash = hash, + Data = repackData, + CatalogInfo = catInfo, + }; + + return sceneRepackData; } - /// - /// Prepare the repacking request. - /// - /// True if there is any repacking to be done. - private void Prepare() + private SceneCatalogInfo BuildSceneCatalogInfo( + RepackedBundleData repackData, Dictionary>> transformSeqs, string containerPrefix) { - if (JsonExtensions.TryLoadFromFile(AssetPaths.RepackedSceneBundleMetadataPath, out RepackDataCollection? repackData)) + SceneCatalogInfo info = new(); + + HashSet rootGos = repackData.GameObjectAssets?.Values.Distinct().ToHashSet() ?? []; + Dictionary rootTransformPathIds = []; + foreach (string rootGo in rootGos) { - _repackData = repackData; + foreach (List transformSeq in transformSeqs[rootGo]) + { + string containerPath = repackData.GameObjectAssets! + .First(kvp => kvp.Key.StartsWith($"{containerPrefix}/{transformSeq[0]}") && kvp.Value == rootGo) + .Key; + info.RootGameObjects.Add(new(rootGo, containerPath, transformSeq[0])); + rootTransformPathIds.Add(transformSeq[0], rootGo); + } } - else + + foreach (string goPath in transformSeqs.Keys) { - _repackData = []; + if (rootGos.Contains(goPath)) continue; + + foreach (List transformSeq in transformSeqs[goPath]) + { + long ancestorPathId = transformSeq.FirstOrDefault(x => rootTransformPathIds.ContainsKey(x)); + if (ancestorPathId == 0) + { + throw new Exception($"Unexpectedly failed to find ancestor for {goPath} [{transformSeq[0]}]"); + } + string ancestor = rootTransformPathIds[ancestorPathId]; + if (!goPath.StartsWith(ancestor + "/")) + { + throw new Exception($"Object {goPath} unexpectedly matched ancestor {ancestor}"); + } + string relativePath = goPath.Substring(ancestor.Length + 1); + + info.ChildGameObjects.Add(new( + goPath, + transformSeq[0], + ancestor, + relativePath, + ancestorPathId + )); + } } - // Any data with a metadata mismatch should be removed from the dictionary - // Also remove data if they have manually deleted the file, as a way to individually - // reset scene data - _repackData = _repackData - .Where(kvp => !MetadataMismatch(kvp.Key, kvp.Value) && File.Exists(GetBundlePathForScene(kvp.Key))) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + return info; + } - _toRepack = []; + /// + /// For each game object path, compute all transform path sequences for that game object path. + /// + /// A transform path sequence is a list of transform path IDs [id0, id1, ..., idn], where id0 is the + /// transform path id for the game object, id1 is its parent, ..., idn is the root. + /// + /// The number of path sequences for each game object path will match the number of game objects with + /// that game object path. + /// + private Dictionary>> BuildTransformSequences( + RepackingContext ctx, RepackedBundleData data, HashSet request) + { + GameObjectLookup goLookup = ctx.GameObjLookup + ?? GameObjectLookup.CreateFromFile(ctx.SceneAssetsManager, ctx.MainAssetsFileInstance); - foreach ((string scene, HashSet request) in AssetRequestAPI.Request.SceneAssets) + Dictionary>> transformSeqs = []; + + // We need to find transform sequences for all game objects in the request, + // and also all game objects in the bundle. The latter will only matter if there is a root game object + // in the repacked bundle that wasn't in the request, but has been included due to being a dependency + // for a requested asset. + HashSet requiredPaths = [ + .. request, + .. data.GameObjectAssets?.Values ?? Enumerable.Empty() + ]; + + foreach (string objPath in requiredPaths) { - if (!_repackData.TryGetValue(scene, out RepackedSceneBundleData existingBundleData)) + List> objTransformSeqs = []; + + if (!goLookup.TryLookupName(objPath, out List? infos)) { - _toRepack[scene] = request; - continue; - } - if (existingBundleData.Data is null) - { - _toRepack[scene] = request; - continue; + throw new Exception($"Failed to find {objPath} in bundle"); } - if (request.All(x => existingBundleData.Data.TriedToRepack(x))) + foreach (GoInfo info in infos) { - // No need to re-repack as there's nothing new to try - continue; + GoInfo currentInfo = info; + List tPathSeq = []; + long tPathId = currentInfo.TransformPathId; + + tPathSeq.Add(tPathId); + + while (currentInfo.ParentPathId != 0) + { + currentInfo = goLookup.LookupTransform(currentInfo.ParentPathId); + tPathSeq.Add(currentInfo.TransformPathId); + } + + objTransformSeqs.Add(tPathSeq); } - // Include everything from the old bundle - perhaps this should be a config option? - _toRepack[scene] = new(request - .Union(existingBundleData.Data.GameObjectAssets?.Values ?? Enumerable.Empty()) - // I don't think we should try to repack failed assets unless they're re-requested - // .Union(existingBundleData.Data.NonRepackedAssets ?? Enumerable.Empty()) - ); + transformSeqs[objPath] = objTransformSeqs; } - } - private static string GetSerializedBundleDirPrefix() - { - return $$"""{Silksong.{{nameof(AssetHelper)}}.Core.{{nameof(AssetPaths)}}.{{nameof(AssetPaths.RepackedSceneBundleDir)}}}"""; + return transformSeqs; } - private static string GetBundlePathForScene(string sceneName) + private bool MustWriteCatalog() { - return Path.Combine(AssetPaths.RepackedSceneBundleDir, $"repacked_{sceneName}.bundle"); + if (!JsonExtensions.TryLoadFromFile(CatalogMetadataPath, out SceneCatalogMetadata? oldMeta) + || oldMeta.Metadata == null + || oldMeta.Metadata.SilksongVersion != VersionData.SilksongVersion + || !VersionData.EarliestAcceptableSceneRepackVersion.AllowCachedData(oldMeta.Metadata.PluginVersion) + || oldMeta.Metadata.OSFolderName != AssetPaths.OSFolderName + ) + { + return true; + } + + if (!File.Exists(SceneCatalogPath)) + { + return true; + } + + return false; } /// @@ -163,14 +362,25 @@ private static string GetBundlePathForScene(string sceneName) /// private static bool MetadataMismatch(string scene, RepackedSceneBundleData existingData) { - if (!VersionData.EarliestAcceptableSceneRepackVersion.AllowCachedData(existingData.PluginVersion)) + if (existingData.Metadata == null) + { + return true; + } + + if (!VersionData.EarliestAcceptableSceneRepackVersion.AllowCachedData(existingData.Metadata.PluginVersion)) { // Mismatch: the version of the plugin used to repack needs to be after the last acceptable version. // We do not accept versions from the future. return true; } - if (existingData.SilksongVersion == VersionData.SilksongVersion) + if (existingData.Metadata.OSFolderName != AssetPaths.OSFolderName) + { + // Different OS strings mean the base game bundles may be different + return true; + } + + if (existingData.Metadata.SilksongVersion == VersionData.SilksongVersion) { // If the Silksong version matches, then we're definitely fine. return false; @@ -189,178 +399,59 @@ private static bool MetadataMismatch(string scene, RepackedSceneBundleData exist return true; } - private static bool ResolveCab(string cabName, out string? bundlePath) + /// + /// Return true if the provided catalog info is capable of loading all assets requested + /// for the given scene. + /// + /// + /// + /// + private bool CanLoadAll(SceneCatalogInfo catalogInfo, string sceneName) { - bundlePath = null; - - if (cabName.Contains("unity")) - { - // this isn't a game bundle (not a cab name) so we should skip - return true; - } - - if (!BundleMetadata.CabLookup.TryGetValue(cabName.ToLowerInvariant(), out string? bundleName)) - { - return false; - } + HashSet existingAssets = new(catalogInfo.LoadableAssets); - if (bundleName.Contains("monoscripts") || bundleName.Contains("builtinassets")) + if (!AssetRequestAPI.Request.SceneAssets.TryGetValue(sceneName, out HashSet requested)) { - // Skip these because we shouldn't follow any deps there + // If nothing was requested, then certainly everything requested can be loaded. return true; } - bundlePath = Path.Combine(AssetPaths.BundleFolder, bundleName); - return true; + return requested.IsSubsetOf(existingAssets); } - /// - /// Run the repacking procedure so that by the end, anything in the request which could be repacked has been. - /// - private IEnumerator RunRepacking(ILoadingScreen bar) + private static string GetSerializedBundleDirPrefix() { - CachedObject SyncedCppCache = CachedObject.CreateSynced( - "container_pointer_preloads_cache.json", () => new(), mutable: true, out IDisposable? cppSyncHandle); - - ContainerPointerPreloads cpp = new(ResolveCab) { Cache = SyncedCppCache.Value }; - PreloadTableResolver resolver = new([new DefaultPreloadTableResolver(), cpp]); - - SceneRepacker repacker = new StrippedSceneRepacker(resolver); - - Stopwatch mainSw = Stopwatch.StartNew(); - - int total = _toRepack.Count; - int count = 0; - - AssetHelperPlugin.InstanceLogger.LogInfo($"Repacking {_toRepack.Count} scenes"); - foreach ((string scene, HashSet request) in _toRepack) - { - Stopwatch sw = Stopwatch.StartNew(); - AssetHelperPlugin.InstanceLogger.LogInfo($"Repacking {request.Count} objects in scene {scene}"); - bar.SetSubtext(scene); - - RepackingParams rParams = new() - { - SceneBundlePath = AssetPaths.GetScenePath(scene), - ObjectNames = request.ToList(), - ContainerPrefix = $"{nameof(AssetHelper)}/{scene}", - OutBundlePath = GetBundlePathForScene(scene), - }; - RepackedBundleData repackData = repacker.Repack(rParams); - - string? hash = null; - if (AddressablesData.TryGetLocationForScene(scene, out IResourceLocation? location) && location.Data is AssetBundleRequestOptions opts) - { - hash = opts.Hash; - } - - RepackedSceneBundleData sceneRepackData = new() - { - SceneName = scene, - BundleHash = hash, - Data = repackData - }; - - _repackData[scene] = sceneRepackData; - _repackData.SerializeToFile(AssetPaths.RepackedSceneBundleMetadataPath); - AssetHelperPlugin.InstanceLogger.LogInfo($"Repacked {scene} in {sw.ElapsedMilliseconds} ms"); - - count++; - bar.SetProgress((float)count / (float)total); - - yield return null; - } - - mainSw.Stop(); - AssetHelperPlugin.InstanceLogger.LogInfo($"Finished scene repacking after {mainSw.ElapsedMilliseconds} ms"); + return $$"""{Silksong.{{nameof(AssetHelper)}}.Core.{{nameof(AssetPaths)}}.{{nameof(AssetPaths.RepackedSceneBundleDir)}}}"""; + } - bar.Reset(); - cppSyncHandle?.Dispose(); + private static string GetBundlePathForScene(string sceneName) + { + return Path.Combine(AssetPaths.RepackedSceneBundleDir, $"repacked_{sceneName}.bundle"); } - private IEnumerator CreateSceneAssetCatalog(RepackDataCollection data, ILoadingScreen screen) + private static bool ResolveCab(string cabName, out string? bundlePath) { - string catalogMetadataPath = Path.ChangeExtension(SceneCatalogPath, ".json"); + bundlePath = null; - if (!_didRepack - && JsonExtensions.TryLoadFromFile(catalogMetadataPath, out SceneCatalogMetadata? oldMeta) - && oldMeta.SilksongVersion == VersionData.SilksongVersion - && VersionData.EarliestAcceptableSceneRepackVersion.AllowCachedData(oldMeta.PluginVersion) - ) + if (cabName.Contains("unity")) { - // We can skip only if there's no change in the repacked bundles and no change to the version metadata - yield break; + // This isn't a game bundle (not a cab name) so we should silently skip + return true; } - AssetHelperPlugin.InstanceLogger.LogInfo($"Creating catalog"); - - Stopwatch sw = Stopwatch.StartNew(); - - CustomCatalogBuilder cbr = new(CatalogKeys.SceneCatalogId); - foreach ((string sceneName, RepackedSceneBundleData repackBunData) in data) + if (!BundleMetadata.CabLookup.TryGetValue(cabName.ToLowerInvariant(), out string? bundleName)) { - if (repackBunData.Data == null) continue; - string bundlePath = GetBundlePathForScene(sceneName); - string bundleFileName = Path.GetFileName(bundlePath); - string serializedBundlePath = $"{GetSerializedBundleDirPrefix()}/{bundleFileName}"; - cbr.AddRepackedSceneData(sceneName, repackBunData.Data, bundlePath, serializedBundlePath); - - // Add in requested child paths - if (AssetRequestAPI.Request.SceneAssets.TryGetValue(sceneName, out HashSet requested)) - { - foreach (string child in requested) - { - if (!ObjPathUtil.TryFindAncestor( - repackBunData.Data.GameObjectAssets?.Values.ToList(), - child, - out string? ancestorPath, - out string? relativePath - )) - { - AssetHelperPlugin.InstanceLogger.LogWarning($"Failed to find {child} in bundle for {sceneName} as loadable"); - continue; - } - if (string.IsNullOrEmpty(relativePath)) - { - // Directly in bundle so no need to include as child - continue; - } - - string parentKey = CatalogKeys.GetKeyForSceneAsset(sceneName, ancestorPath); - string childKey = CatalogKeys.GetKeyForSceneAsset(sceneName, child); - ContentCatalogDataEntry entry = CatalogEntryUtils.CreateChildGameObjectEntry(parentKey, relativePath, childKey); - cbr.AddCatalogEntry(entry); - } - } + // Surprisingly failed to resolve a cab, so we should ensure a warning is emitted + return false; } - sw.Stop(); - AssetHelperPlugin.InstanceLogger.LogInfo($"Prepared catalog in {sw.ElapsedMilliseconds} ms"); - - screen.SetText(LanguageKeys.WRITING_SCENE.GetLocalized()); - yield return null; - - sw = Stopwatch.StartNew(); - - int count = 0; - using IEnumerator serializationRoutine = cbr.BuildRoutine(); - while (serializationRoutine.MoveNext()) + if (bundleName.Contains("monoscripts") || bundleName.Contains("builtinassets")) { - float progress = serializationRoutine.Current; - count++; - if (count%10 == 0) - { - screen.SetProgress(progress); - yield return null; - } + // Silently skip these because we shouldn't follow any deps there + return true; } - sw.Stop(); - AssetHelperPlugin.InstanceLogger.LogInfo($"Finished writing catalog in {sw.ElapsedMilliseconds} ms"); - - SceneCatalogMetadata metadata = new(); - metadata.SerializeToFile(catalogMetadataPath); - - yield return null; + bundlePath = Path.Combine(AssetPaths.BundleFolder, bundleName); + return true; } } diff --git a/AssetHelper/packages.lock.json b/AssetHelper/packages.lock.json deleted file mode 100644 index 93650cc..0000000 --- a/AssetHelper/packages.lock.json +++ /dev/null @@ -1,432 +0,0 @@ -{ - "version": 1, - "dependencies": { - ".NETStandard,Version=v2.1": { - "AssetHelperLib": { - "type": "Direct", - "requested": "[0.11.0, )", - "resolved": "0.11.0", - "contentHash": "1YIHnp3S+gBLa1NzFTtSmMFA+zT7D0mCgCPqPhVa4pv5CKpJQgSiZPeiRxzMDvDMK0sdGa6O6/Uuh1pPIrBYlw==" - }, - "AssetsTools.NET": { - "type": "Direct", - "requested": "[3.0.4, )", - "resolved": "3.0.4", - "contentHash": "NqYy5UIucwKwdkpamFTi8lr53hD+V8qQmbHzztQj1v4Pq1G4TjNlpkUWOhktTLBqBm4aPRaAkpFgB+3TDEuomw==" - }, - "BepInEx.Analyzers": { - "type": "Direct", - "requested": "[1.0.8, )", - "resolved": "1.0.8", - "contentHash": "xrfNmunsPhBx+vStTxLonq/aHkRrDH77c9tG/x3m5eejrKe5B0nf7cJPRRt6x330sGI0bLaPTtygdeHUgvI3wQ==" - }, - "BepInEx.Core": { - "type": "Direct", - "requested": "[5.4.21, )", - "resolved": "5.4.21", - "contentHash": "NMUPlbHTTfJ+qIQCI90uIvjuUQ4wnwt4cpRsK3ItBh1DhsWFzAHXNiZjBxZkPysljEKQ2iu89sxMTga4bxBXVQ==", - "dependencies": { - "BepInEx.BaseLib": "5.4.20", - "HarmonyX": "2.7.0" - } - }, - "DataDrivenConstants": { - "type": "Direct", - "requested": "[1.1.0, )", - "resolved": "1.1.0", - "contentHash": "HwaWo9+GHQjS8DQnV9L+sPyXfgpkBItQCRbOZ4jaDmYxsJnF2JJWS+wCFFMx5tKDupaaxK8y4m0CkwWRPX3tHA==" - }, - "Hamunii.BepInEx.AutoPlugin": { - "type": "Direct", - "requested": "[2.1.0, )", - "resolved": "2.1.0", - "contentHash": "EuH0DoeBkpEiLYwCVZrlrQiNoqYi7U42+MCGpVim22erghvYYjPosEQTCHMsmyyBjLO/0NuLCu4I9aO/EkjVRQ==" - }, - "HarmonyX": { - "type": "Direct", - "requested": "[2.9.0, )", - "resolved": "2.9.0", - "contentHash": "vB03a0lJJefzSnPP8VJSzyTtgSx81dnxU2olfirHTBnbXY8Uc2BMoxCODPK1nzg4C53lQR0NOi+bp9170sYVFA==", - "dependencies": { - "MonoMod.RuntimeDetour": "22.1.29.1", - "System.Reflection.Emit": "4.7.0" - } - }, - "Krafs.Publicizer": { - "type": "Direct", - "requested": "[2.3.0, )", - "resolved": "2.3.0", - "contentHash": "DjktTgctwxUMhMkWKrRECer3LR1lHzanCOlE4mpinAiY8SfWJq4DG/QitP5h1A+WBjyWHzQSOG+204i3VpO1FA==" - }, - "Microsoft.Unity.Analyzers": { - "type": "Direct", - "requested": "[1.26.0, )", - "resolved": "1.26.0", - "contentHash": "lU4QRpGxzwBGkDJs86Q18CwhiqigssQrItJFr+c1Ijra35Y/uTwV/bRiSIYcXSaWl9RsXCi6dqG4xVAXb9sCtQ==" - }, - "MonoDetour": { - "type": "Direct", - "requested": "[*, )", - "resolved": "0.7.10", - "contentHash": "st/61d+jjzIN/TfxU8gwTiD9NWhG36yiIRdo3ANcV1bpQhOr/i2WYUJ3j/Omi00+yX+kfwzMu43ygDufELWw/g==", - "dependencies": { - "MonoMod.RuntimeDetour": "21.12.13.1" - } - }, - "MonoDetour.HookGen": { - "type": "Direct", - "requested": "[0.7.*, )", - "resolved": "0.7.12", - "contentHash": "h9UhcqMDiXqlO5omQ8J3n17n/3vVT3IzxiL14HQ9QxYnGVX8kBiSkIV0mT+sZ2yccmgEu3daJwHQxUFphVrwpA==", - "dependencies": { - "MonoDetour": "0.5.0" - } - }, - "PolySharp": { - "type": "Direct", - "requested": "[1.15.0, )", - "resolved": "1.15.0", - "contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g==" - }, - "Silksong.GameLibs": { - "type": "Direct", - "requested": "[*-*, )", - "resolved": "1.2.0-silksong1.0.29315", - "contentHash": "MwcUC0FMUn7aCwYHWgQegOBxNGgNBDie9nfg0GeodqbnfrHOjUZ06sZjq+0Iiz9/WBL8yd511YZujFS91FgVMw==" - }, - "UnityEngine.Modules": { - "type": "Direct", - "requested": "[6000.0.50, )", - "resolved": "6000.0.50", - "contentHash": "hEFz1r4NGzeYXPY2yPh+/btcnwVB6EnEeRjDMCVNi19PD7VvRMvJeu46x1dNH9jYkT0B/ZXuyMBYiuI7YlRnKw==" - }, - "BepInEx.BaseLib": { - "type": "Transitive", - "resolved": "5.4.20", - "contentHash": "0bXgYxbCEN2Ixp3kiFEhyw+RASeFQeg/ww+lbMt7if6XMeVS60eg6epNsMA8Jbx57dmNOzNevkKKw8mP8SUMqw==" - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Mono.Cecil": { - "type": "Transitive", - "resolved": "0.11.4", - "contentHash": "IC1h5g0NeJGHIUgzM1P82ld57knhP0IcQfrYITDPXlNpMYGUrsG5TxuaWTjaeqDNQMBDNZkB8L0rBnwsY6JHuQ==" - }, - "MonoMod.RuntimeDetour": { - "type": "Transitive", - "resolved": "22.1.29.1", - "contentHash": "CksRFEGCPs8VGKe6pc3zsOPd2Jik+FMlmx5HDxWVTxz9JlAmWDk1+0PCwAVw4iGDuDZmIUjZDjhSACasyw9y/Q==", - "dependencies": { - "Mono.Cecil": "0.11.4", - "MonoMod.Utils": "22.1.29.1", - "System.Collections.NonGeneric": "4.3.0", - "System.ComponentModel.TypeConverter": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.7.0", - "System.Reflection.Emit.Lightweight": "4.7.0", - "System.Reflection.TypeExtensions": "4.7.0" - } - }, - "MonoMod.Utils": { - "type": "Transitive", - "resolved": "22.1.29.1", - "contentHash": "qKyM/JXIA3hr2BpE5LTtYfZvIFlcLB62pkORARRv0bXKSk1n23Nl3oAdYymQMjNfrhn/jCPZRqKDFftrYa1K2Q==", - "dependencies": { - "Mono.Cecil": "0.11.4", - "System.Collections.NonGeneric": "4.3.0", - "System.ComponentModel.TypeConverter": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.7.0", - "System.Reflection.Emit.Lightweight": "4.7.0", - "System.Reflection.TypeExtensions": "4.7.0" - } - }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.NonGeneric": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Collections.Specialized": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", - "dependencies": { - "System.Collections.NonGeneric": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.ComponentModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.ComponentModel.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==", - "dependencies": { - "System.ComponentModel": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.ComponentModel.TypeConverter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Collections.NonGeneric": "4.3.0", - "System.Collections.Specialized": "4.3.0", - "System.ComponentModel": "4.3.0", - "System.ComponentModel.Primitives": "4.3.0", - "System.Globalization": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==" - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "AucBYo3DSI0IDxdUjKksBcQJXPHyoPyrCXYURW1WDsLI4M65Ar/goSHjdnHOAY9MiYDNKqDlIgaYm+zL2hA1KA==" - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "a4OLB4IITxAXJeV74MDx49Oq2+PsF6Sml54XAFv+2RyWwtDBcabzoxiiJRhdhx+gaohLh4hEGCLQyBozXoQPqA==" - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - } - } - } -} \ No newline at end of file diff --git a/AssetHelperMenu/AssetHelperMenu.csproj b/AssetHelperMenu/AssetHelperMenu.csproj index 79b58fb..faee3c0 100644 --- a/AssetHelperMenu/AssetHelperMenu.csproj +++ b/AssetHelperMenu/AssetHelperMenu.csproj @@ -14,10 +14,6 @@ $(NoWarn);MSB3270 - True - - True - $(MSBuildProjectDirectory)=/ @@ -33,7 +29,7 @@ - + diff --git a/AssetHelperMenu/AssetHelperMenuPlugin.cs b/AssetHelperMenu/AssetHelperMenuPlugin.cs index c4e2738..bb768a2 100644 --- a/AssetHelperMenu/AssetHelperMenuPlugin.cs +++ b/AssetHelperMenu/AssetHelperMenuPlugin.cs @@ -8,6 +8,7 @@ using BepInEx.Configuration; using USceneManager = UnityEngine.SceneManagement.SceneManager; using Silksong.AssetHelper.Core; +using Silksong.ModMenu.Models; namespace AssetHelperMenu; @@ -64,8 +65,13 @@ public AbstractMenuScreen BuildCustomMenu() DumpGameObjectPaths(USceneManager.GetSceneAt(i).name); } }); - - // TODO - add a button to dump a scene by name - blocked by ModMenu needing a text entry field + + TextInput sceneEntryField = new TextInput("Scene", TextModels.ForStrings(), "Choose a scene to dump"); + sceneSubpageBuilder.Add(sceneEntryField); + sceneSubpageBuilder.AddButton("Dump selected scene", () => + { + DumpGameObjectPaths(sceneEntryField.Model.Value); + }); PaginatedMenuScreen sceneSubpage = sceneSubpageBuilder.Build(); diff --git a/AssetHelperMenu/packages.lock.json b/AssetHelperMenu/packages.lock.json deleted file mode 100644 index bb1b2a9..0000000 --- a/AssetHelperMenu/packages.lock.json +++ /dev/null @@ -1,445 +0,0 @@ -{ - "version": 1, - "dependencies": { - ".NETStandard,Version=v2.1": { - "BepInEx.Analyzers": { - "type": "Direct", - "requested": "[1.0.8, )", - "resolved": "1.0.8", - "contentHash": "xrfNmunsPhBx+vStTxLonq/aHkRrDH77c9tG/x3m5eejrKe5B0nf7cJPRRt6x330sGI0bLaPTtygdeHUgvI3wQ==" - }, - "BepInEx.Core": { - "type": "Direct", - "requested": "[5.4.21, )", - "resolved": "5.4.21", - "contentHash": "NMUPlbHTTfJ+qIQCI90uIvjuUQ4wnwt4cpRsK3ItBh1DhsWFzAHXNiZjBxZkPysljEKQ2iu89sxMTga4bxBXVQ==", - "dependencies": { - "BepInEx.BaseLib": "5.4.20", - "HarmonyX": "2.7.0" - } - }, - "Hamunii.BepInEx.AutoPlugin": { - "type": "Direct", - "requested": "[2.1.0, )", - "resolved": "2.1.0", - "contentHash": "EuH0DoeBkpEiLYwCVZrlrQiNoqYi7U42+MCGpVim22erghvYYjPosEQTCHMsmyyBjLO/0NuLCu4I9aO/EkjVRQ==" - }, - "HarmonyX": { - "type": "Direct", - "requested": "[2.9.0, )", - "resolved": "2.9.0", - "contentHash": "vB03a0lJJefzSnPP8VJSzyTtgSx81dnxU2olfirHTBnbXY8Uc2BMoxCODPK1nzg4C53lQR0NOi+bp9170sYVFA==", - "dependencies": { - "MonoMod.RuntimeDetour": "22.1.29.1", - "System.Reflection.Emit": "4.7.0" - } - }, - "Microsoft.Unity.Analyzers": { - "type": "Direct", - "requested": "[1.26.0, )", - "resolved": "1.26.0", - "contentHash": "lU4QRpGxzwBGkDJs86Q18CwhiqigssQrItJFr+c1Ijra35Y/uTwV/bRiSIYcXSaWl9RsXCi6dqG4xVAXb9sCtQ==" - }, - "Silksong.GameLibs": { - "type": "Direct", - "requested": "[*-*, )", - "resolved": "1.2.0-silksong1.0.29315", - "contentHash": "MwcUC0FMUn7aCwYHWgQegOBxNGgNBDie9nfg0GeodqbnfrHOjUZ06sZjq+0Iiz9/WBL8yd511YZujFS91FgVMw==" - }, - "Silksong.ModMenu": { - "type": "Direct", - "requested": "[0.4.3, )", - "resolved": "0.4.3", - "contentHash": "uRI5SS6NIX3ivv/eLFLD9elYdLEfg6s6yHX/SY2N7ps4Ijdt28KMMlRyNI09Z0oXRULRIUSl02nasFUxEB+RPg==", - "dependencies": { - "BepInEx.Core": "5.4.21", - "HarmonyX": "2.9.0", - "MonoDetour": "0.7.5", - "MonoDetour.HookGen": "0.7.3", - "Silksong.UnityHelper": "1.0.1", - "UnityEngine.Modules": "6000.0.50" - } - }, - "UnityEngine.Modules": { - "type": "Direct", - "requested": "[6000.0.50, )", - "resolved": "6000.0.50", - "contentHash": "hEFz1r4NGzeYXPY2yPh+/btcnwVB6EnEeRjDMCVNi19PD7VvRMvJeu46x1dNH9jYkT0B/ZXuyMBYiuI7YlRnKw==" - }, - "AssetHelperLib": { - "type": "Transitive", - "resolved": "0.11.0", - "contentHash": "1YIHnp3S+gBLa1NzFTtSmMFA+zT7D0mCgCPqPhVa4pv5CKpJQgSiZPeiRxzMDvDMK0sdGa6O6/Uuh1pPIrBYlw==" - }, - "AssetsTools.NET": { - "type": "Transitive", - "resolved": "3.0.4", - "contentHash": "NqYy5UIucwKwdkpamFTi8lr53hD+V8qQmbHzztQj1v4Pq1G4TjNlpkUWOhktTLBqBm4aPRaAkpFgB+3TDEuomw==" - }, - "BepInEx.BaseLib": { - "type": "Transitive", - "resolved": "5.4.20", - "contentHash": "0bXgYxbCEN2Ixp3kiFEhyw+RASeFQeg/ww+lbMt7if6XMeVS60eg6epNsMA8Jbx57dmNOzNevkKKw8mP8SUMqw==" - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Mono.Cecil": { - "type": "Transitive", - "resolved": "0.11.4", - "contentHash": "IC1h5g0NeJGHIUgzM1P82ld57knhP0IcQfrYITDPXlNpMYGUrsG5TxuaWTjaeqDNQMBDNZkB8L0rBnwsY6JHuQ==" - }, - "MonoDetour": { - "type": "Transitive", - "resolved": "0.7.10", - "contentHash": "st/61d+jjzIN/TfxU8gwTiD9NWhG36yiIRdo3ANcV1bpQhOr/i2WYUJ3j/Omi00+yX+kfwzMu43ygDufELWw/g==", - "dependencies": { - "MonoMod.RuntimeDetour": "21.12.13.1" - } - }, - "MonoDetour.HookGen": { - "type": "Transitive", - "resolved": "0.7.3", - "contentHash": "82nNksQf2HHpoTt0QhWY+uWCkA9gYE4H+7fbBI2IOa+TVe4In838/XogX/5Lx6EhXXF58qV4XWvwFxmmdg/YQw==", - "dependencies": { - "MonoDetour": "0.5.0" - } - }, - "MonoMod.RuntimeDetour": { - "type": "Transitive", - "resolved": "22.1.29.1", - "contentHash": "CksRFEGCPs8VGKe6pc3zsOPd2Jik+FMlmx5HDxWVTxz9JlAmWDk1+0PCwAVw4iGDuDZmIUjZDjhSACasyw9y/Q==", - "dependencies": { - "Mono.Cecil": "0.11.4", - "MonoMod.Utils": "22.1.29.1", - "System.Collections.NonGeneric": "4.3.0", - "System.ComponentModel.TypeConverter": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.7.0", - "System.Reflection.Emit.Lightweight": "4.7.0", - "System.Reflection.TypeExtensions": "4.7.0" - } - }, - "MonoMod.Utils": { - "type": "Transitive", - "resolved": "22.1.29.1", - "contentHash": "qKyM/JXIA3hr2BpE5LTtYfZvIFlcLB62pkORARRv0bXKSk1n23Nl3oAdYymQMjNfrhn/jCPZRqKDFftrYa1K2Q==", - "dependencies": { - "Mono.Cecil": "0.11.4", - "System.Collections.NonGeneric": "4.3.0", - "System.ComponentModel.TypeConverter": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.7.0", - "System.Reflection.Emit.Lightweight": "4.7.0", - "System.Reflection.TypeExtensions": "4.7.0" - } - }, - "Silksong.UnityHelper": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "e9pIwaIKjEjtV+gzvTXKiI3i/0qxSdCgOV6sMGlm6U4f2HeSSIgmhi0DVLJ7qU2LaAUTpLqQEt9q5duPUouK0g==", - "dependencies": { - "BepInEx.Core": "5.4.21", - "HarmonyX": "2.9.0", - "UnityEngine.Modules": "6000.0.50" - } - }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.NonGeneric": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Collections.Specialized": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", - "dependencies": { - "System.Collections.NonGeneric": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.ComponentModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.ComponentModel.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==", - "dependencies": { - "System.ComponentModel": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.ComponentModel.TypeConverter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Collections.NonGeneric": "4.3.0", - "System.Collections.Specialized": "4.3.0", - "System.ComponentModel": "4.3.0", - "System.ComponentModel.Primitives": "4.3.0", - "System.Globalization": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==" - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "AucBYo3DSI0IDxdUjKksBcQJXPHyoPyrCXYURW1WDsLI4M65Ar/goSHjdnHOAY9MiYDNKqDlIgaYm+zL2hA1KA==" - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "a4OLB4IITxAXJeV74MDx49Oq2+PsF6Sml54XAFv+2RyWwtDBcabzoxiiJRhdhx+gaohLh4hEGCLQyBozXoQPqA==" - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "Silksong.AssetHelper": { - "type": "Project", - "dependencies": { - "AssetHelperLib": "[0.11.0, )", - "AssetsTools.NET": "[3.0.4, )", - "BepInEx.Core": "[5.4.21, )", - "HarmonyX": "[2.9.0, )", - "MonoDetour": "[*, )", - "UnityEngine.Modules": "[6000.0.50, )" - } - } - } - } -} \ No newline at end of file diff --git a/AssetHelperTesting/AssetHelperTesting.csproj b/AssetHelperTesting/AssetHelperTesting.csproj new file mode 100644 index 0000000..5a420d1 --- /dev/null +++ b/AssetHelperTesting/AssetHelperTesting.csproj @@ -0,0 +1,76 @@ + + + + + AssetHelperTesting + netstandard2.1 + latest + enable + True + recommended + + $(NoWarn);MSB3270 + + + $(MSBuildProjectDirectory)=/ + + false + + + + $(SilksongFolder)\Hollow Knight Silksong_Data\Managed + true + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AssetHelperTesting/AssetHelperTestingPlugin.cs b/AssetHelperTesting/AssetHelperTestingPlugin.cs new file mode 100644 index 0000000..71b4a89 --- /dev/null +++ b/AssetHelperTesting/AssetHelperTestingPlugin.cs @@ -0,0 +1,35 @@ +using AssetHelperTesting.Tests; +using BepInEx; +using BepInEx.Logging; +using Silksong.AssetHelper.Dev; +using Silksong.AssetHelper.Plugin; +using UnityEngine; + +namespace AssetHelperTesting +{ + // TODO - adjust the plugin guid as needed + [BepInAutoPlugin(id: "org.silksong-modding.assethelpertesting")] + public partial class AssetHelperTestingPlugin : BaseUnityPlugin + { + internal static ManualLogSource InstanceLogger { get; private set; } + + private void Awake() + { + InstanceLogger = Logger; + + PrepareTests(); + + AssetRequestAPI.InvokeAfterBundleCreation( + () => DebugTools.DumpAllAddressableAssets(AssetRequestAPI.SceneAssetLocator!, "scene_locator.json") + ); + + Logger.LogInfo($"Plugin {Name} ({Id}) has loaded!"); + } + + // Contributors should freely modify this method + private void PrepareTests() + { + SquirrmTest.Prepare(); + } + } +} diff --git a/AssetHelperTesting/Events.cs b/AssetHelperTesting/Events.cs new file mode 100644 index 0000000..b2f59b4 --- /dev/null +++ b/AssetHelperTesting/Events.cs @@ -0,0 +1,35 @@ +using System; + +namespace AssetHelperTesting; + +internal static class Events +{ + private static Action? _onHeroStart; + + public static event Action? OnHeroStart + { + add + { + EnsureHooked(); + _onHeroStart += value; + } + remove + { + _onHeroStart -= value; + } + } + + private static bool _hooked; + + private static void EnsureHooked() + { + if (_hooked) return; + Md.HeroController.Start.Postfix(InvokeSubscribers); + _hooked = true; + } + + private static void InvokeSubscribers(HeroController self) + { + _onHeroStart?.Invoke(); + } +} diff --git a/AssetHelperTesting/JsonHelper.cs b/AssetHelperTesting/JsonHelper.cs new file mode 100644 index 0000000..20d2225 --- /dev/null +++ b/AssetHelperTesting/JsonHelper.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; + +namespace AssetHelperTesting; + +internal static class JsonHelper +{ + public static bool TryLoadEmbeddedJson(string filename, [NotNullWhen(true)] out T? parsed) + { + parsed = default; + Assembly assembly = typeof(JsonHelper).Assembly; + + string resourceName = $"AssetHelperTesting.Resources.{filename}.json"; + + try + { + using Stream? stream = assembly.GetManifestResourceStream(resourceName); + if (stream == null) return false; + + using StreamReader reader = new(stream); + string jsonText = reader.ReadToEnd(); + parsed = JsonConvert.DeserializeObject(jsonText); + return parsed != null; + } + catch + { + return false; + } + } +} diff --git a/AssetHelperTesting/MonoDetourImports.cs b/AssetHelperTesting/MonoDetourImports.cs new file mode 100644 index 0000000..017f834 --- /dev/null +++ b/AssetHelperTesting/MonoDetourImports.cs @@ -0,0 +1,3 @@ +using MonoDetour.HookGen; + +[assembly: MonoDetourTargets(typeof(HeroController))] diff --git a/AssetHelperTesting/README.md b/AssetHelperTesting/README.md new file mode 100644 index 0000000..add95dc --- /dev/null +++ b/AssetHelperTesting/README.md @@ -0,0 +1,10 @@ +# AssetHelperTesting + +Testing code for AssetHelper. + +## Contributing + +Any tests that should be persisted should be created as a separate class in the tests folder. + +Contributors should feel free to modify the code in AssetHelperTestingPlugin.PrepareTests as they +see fit. Any code specific to a test should persist in a file in the Tests subfolder. diff --git a/AssetHelperTesting/Resources/nonscenegroup.json b/AssetHelperTesting/Resources/nonscenegroup.json new file mode 100644 index 0000000..dace21b --- /dev/null +++ b/AssetHelperTesting/Resources/nonscenegroup.json @@ -0,0 +1,922 @@ +[ + { + "BundleName": "audiocuesdynamic_assets_areaabyss", + "AssetName": "Assets/Audio/AtmosCues/Abyss Cocoon.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaabyss", + "AssetName": "Assets/Audio/MusicCues/Abyss Ascent.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaabyss", + "AssetName": "Assets/Audio/MusicCues/Abyss.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaabyss", + "AssetName": "Assets/Audio/MusicCues/Enemy Battle Abyss.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaabyss", + "AssetName": "Assets/Audio/MusicCues/LastDivePrologue.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaabyss", + "AssetName": "Assets/Audio/MusicCues/LostLace.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaabyss", + "AssetName": "Assets/Audio/MusicCues/LostLace2.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaabyssareadocks", + "AssetName": "Assets/Audio/AtmosCues/Abyss.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaant", + "AssetName": "Assets/Audio/AtmosCues/Hunters March.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaant", + "AssetName": "Assets/Audio/MusicCues/Hunter Queen Carmelita.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaant", + "AssetName": "Assets/Audio/MusicCues/Hunters Trail.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaant", + "AssetName": "Assets/Audio/Voices/Enemies_Silksong/bone hunter child/ant_child_death.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaant", + "AssetName": "Assets/Audio/Voices/Enemies_Silksong/bone hunter child/ant_child_sing.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaant", + "AssetName": "Assets/Audio/Voices/Enemies_Silksong/bone hunter child/ant_child_yelp.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaantareaboneareacoralareamossareawilds", + "AssetName": "Assets/Audio/MusicCues/RipAndShred.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaaqueduct", + "AssetName": "Assets/Audio/AtmosCues/Pharloom Chasm.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaaqueduct", + "AssetName": "Assets/Audio/MusicCues/Aqueducts.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaaqueduct", + "AssetName": "Assets/Audio/MusicCues/Pinstress.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaarborium", + "AssetName": "Assets/Audio/AtmosCues/Arborium.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaarborium", + "AssetName": "Assets/Audio/MusicCues/Memorium.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaarboriumareahangareaunderstoreareaward", + "AssetName": "Assets/Audio/AtmosCues/Halls PreWake.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaarboriumareahangareaunderstoreareaward", + "AssetName": "Assets/Audio/AtmosCues/Halls.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabell", + "AssetName": "Assets/Audio/MusicCues/BellBattle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabell", + "AssetName": "Assets/Audio/MusicCues/Bell_Surrounds.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabell", + "AssetName": "Assets/Audio/MusicCues/Belltown Act3.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabell", + "AssetName": "Assets/Audio/MusicCues/Belltown.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabell", + "AssetName": "Assets/Audio/MusicCues/Belltown_Cursed.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellareabellway", + "AssetName": "Assets/Audio/AtmosCues/BellArea.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellareabone", + "AssetName": "Assets/Audio/MusicCues/Shrine.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellareacoralareashellwoodareaward", + "AssetName": "Assets/Audio/MusicCues/Creepy Boss Main.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellareashellwoodareaward", + "AssetName": "Assets/Audio/MusicCues/Creepy Boss Ambient.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellareaslab", + "AssetName": "Assets/Audio/MusicCues/Spinner.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellareaslab", + "AssetName": "Assets/Audio/MusicCues/SpinnerRage.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellshrineareaboneareawilds", + "AssetName": "Assets/Audio/AtmosCues/Boneforest.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellwayareadocksareasongareawilds", + "AssetName": "Assets/Audio/AtmosCues/Deep Docks.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellwayareapeak", + "AssetName": "Assets/Audio/MusicCues/Peak.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabellwayareapeakareaslab", + "AssetName": "Assets/Audio/AtmosCues/Mountain.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabone", + "AssetName": "Assets/Audio/MusicCues/Boneforest.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabone", + "AssetName": "Assets/Audio/MusicCues/Bonetown.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaboneareadocks", + "AssetName": "Assets/Audio/MusicCues/SongGolem.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areabonearealibrary", + "AssetName": "Assets/Audio/MusicCues/Vaults.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaboneareashellwoodareaslabareaward", + "AssetName": "Assets/Audio/MusicCues/Enemy Battle Small.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaclover", + "AssetName": "Assets/Audio/AtmosCues/Cloverland.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaclover", + "AssetName": "Assets/Audio/MusicCues/CloverDancers.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaclover", + "AssetName": "Assets/Audio/MusicCues/Cloverland.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacloverareacoralareamemoryareaslab", + "AssetName": "Assets/Audio/MusicCues/Memory.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacloverareatutorialareaweaver", + "AssetName": "Assets/Audio/AtmosCues/Moss Cave.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacog", + "AssetName": "Assets/Audio/AtmosCues/Cogwork Core.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacog", + "AssetName": "Assets/Audio/MusicCues/Cogwork Core.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacogareacradleareasong", + "AssetName": "Assets/Audio/AtmosCues/Citadel Ruined Pipes.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoral", + "AssetName": "Assets/Audio/AtmosCues/Blasted Steps.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoral", + "AssetName": "Assets/Audio/MusicCues/Coral King.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoral", + "AssetName": "Assets/Audio/MusicCues/Coral Ruins.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoral", + "AssetName": "Assets/Audio/MusicCues/Coral_Gorge.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoral", + "AssetName": "Assets/Audio/MusicCues/Coral_River.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoral", + "AssetName": "Assets/Audio/MusicCues/FinalJudge.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoralarealibraryareasong", + "AssetName": "Assets/Audio/MusicCues/Ambience Citadel Surrounds.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoralareamemory", + "AssetName": "Assets/Audio/AtmosCues/Coral Tower.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoralareamemory", + "AssetName": "Assets/Audio/MusicCues/Coral Tower Ambient.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoralareamemory", + "AssetName": "Assets/Audio/MusicCues/Coral Tower Battle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacoralareamemoryareaslabareatutorial", + "AssetName": "Assets/Audio/AtmosCues/Memory.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacradle", + "AssetName": "Assets/Audio/MusicCues/Cradle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacradle", + "AssetName": "Assets/Audio/MusicCues/Silk Boss A.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacradle", + "AssetName": "Assets/Audio/MusicCues/Silk Boss Ambient A.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacradle", + "AssetName": "Assets/Audio/MusicCues/Silk Boss Ambient B.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacradle", + "AssetName": "Assets/Audio/MusicCues/Silk Boss B.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacradle", + "AssetName": "Assets/Audio/MusicCues/SurfaceAscent.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacradleareasong", + "AssetName": "Assets/Audio/AtmosCues/Cradle.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacradleareasurface", + "AssetName": "Assets/Audio/AtmosCues/Surface.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacrawl", + "AssetName": "Assets/Audio/AtmosCues/Crawl.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areacrawl", + "AssetName": "Assets/Audio/MusicCues/Crawl.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areadocks", + "AssetName": "Assets/Audio/AtmosCues/SurfaceInterior.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areadocks", + "AssetName": "Assets/Audio/MusicCues/Deep Docks.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areadocksareawilds", + "AssetName": "Assets/Audio/MusicCues/Deep Deep Docks.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areadust", + "AssetName": "Assets/Audio/AtmosCues/Dustpens.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areadust", + "AssetName": "Assets/Audio/MusicCues/Dustpens.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areadust", + "AssetName": "Assets/Audio/MusicCues/MistMaze.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areadustareaorgan", + "AssetName": "Assets/Audio/AtmosCues/Organ.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areagreymoor", + "AssetName": "Assets/Audio/AtmosCues/Greymoor.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areagreymoor", + "AssetName": "Assets/Audio/MusicCues/Greymoor.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areahang", + "AssetName": "Assets/Audio/AtmosCues/Hanging Garden.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areahang", + "AssetName": "Assets/Audio/MusicCues/CitadelHang.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areahangarealibrary", + "AssetName": "Assets/Audio/MusicCues/Grand Forum Battle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_arealibrary", + "AssetName": "Assets/Audio/AtmosCues/Citadel Undercave.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_arealibrary", + "AssetName": "Assets/Audio/AtmosCues/Library Act3.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_arealibrary", + "AssetName": "Assets/Audio/AtmosCues/Library.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areamemoryareatutorial", + "AssetName": "Assets/Audio/MusicCues/Red Memory.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areamoss", + "AssetName": "Assets/Audio/AtmosCues/Bonebottom Chasm.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areamoss", + "AssetName": "Assets/Audio/MusicCues/MossCave Act3.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaorgan", + "AssetName": "Assets/Audio/MusicCues/MistMaze_Organ.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaorgan", + "AssetName": "Assets/Audio/MusicCues/Phantom.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areashellwood", + "AssetName": "Assets/Audio/AtmosCues/Shellwood Underlake.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areashellwood", + "AssetName": "Assets/Audio/AtmosCues/Shellwood.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areashellwood", + "AssetName": "Assets/Audio/MusicCues/FlowerBattle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areashellwood", + "AssetName": "Assets/Audio/MusicCues/Seth Battle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areashellwood", + "AssetName": "Assets/Audio/MusicCues/Shellwood.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaslab", + "AssetName": "Assets/Audio/AtmosCues/Slab.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaslab", + "AssetName": "Assets/Audio/MusicCues/Cloak Battle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaslab", + "AssetName": "Assets/Audio/MusicCues/FirstWeaver.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaslab", + "AssetName": "Assets/Audio/MusicCues/Slab.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areasong", + "AssetName": "Assets/Audio/AtmosCues/Citadel Main Act 3.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areasong", + "AssetName": "Assets/Audio/AtmosCues/Citadel Main Unwoken.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areasong", + "AssetName": "Assets/Audio/AtmosCues/Citadel Main.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areasong", + "AssetName": "Assets/Audio/MusicCues/CitadelHalls Act3.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areasong", + "AssetName": "Assets/Audio/MusicCues/CitadelHalls.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areasong", + "AssetName": "Assets/Audio/MusicCues/Enclave.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areasong", + "AssetName": "Assets/Audio/MusicCues/LaceDefeated.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaswamp", + "AssetName": "Assets/Audio/MusicCues/Shadow.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaswampareaweaverareawilds", + "AssetName": "Assets/Audio/MusicCues/Weaverlands.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areatutorial", + "AssetName": "Assets/Audio/AtmosCues/None.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areatutorial", + "AssetName": "Assets/Audio/MusicCues/MossCave.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areatutorial", + "AssetName": "Assets/Audio/MusicCues/Mosstown.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaunderstore", + "AssetName": "Assets/Audio/AtmosCues/Understore.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaunderstore", + "AssetName": "Assets/Audio/MusicCues/Understore.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areaward", + "AssetName": "Assets/Audio/MusicCues/Ward.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areawilds", + "AssetName": "Assets/Audio/AtmosCues/Wilds.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areawilds", + "AssetName": "Assets/Audio/MusicCues/Wilds.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_areawisp", + "AssetName": "Assets/Audio/MusicCues/Wisp.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_battle", + "AssetName": "Assets/Audio/MusicCues/Enemy Battle Grind.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_battle", + "AssetName": "Assets/Audio/MusicCues/Enemy Battle Mid.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_blackthread", + "AssetName": "Assets/Audio/MusicCues/Abyss Tension.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_boss", + "AssetName": "Assets/Audio/MusicCues/Boss Strive.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_boss", + "AssetName": "Assets/Audio/MusicCues/SmallBattle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_candle", + "AssetName": "Assets/Audio/MusicCues/Chapel.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_laceboss", + "AssetName": "Assets/Audio/MusicCues/LaceBattle.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_menu", + "AssetName": "Assets/Audio/AtmosCues/MiscWind.asset", + "Type": "AtmosCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_menu", + "AssetName": "Assets/Audio/MusicCues/Title.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_shared", + "AssetName": "Assets/Audio/MusicCues/None.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_trobbio", + "AssetName": "Assets/Audio/MusicCues/TormentedTrobbio.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "audiocuesdynamic_assets_trobbio", + "AssetName": "Assets/Audio/MusicCues/Trobbio.asset", + "Type": "MusicCue, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "maps_assets_all", + "AssetName": "Assets/Prefabs/UI/Map/Game_Map_Hornet.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "prompts_assets_all", + "AssetName": "Assets/Prefabs/UI/Hornet UI/Ancestral_Art_Get_Prompt.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areabellway", + "AssetName": "Assets/Prefabs/UI/Fast Travel Map.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areabellway", + "AssetName": "Assets/Prefabs/Enemies/Projectiles/Shot Giant Centipede.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areabellway", + "AssetName": "Assets/Prefabs/Hornet Bosses/Giant Centipede Bomb.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "shopui_assets_all", + "AssetName": "Assets/Prefabs/UI/Shop/Shop Menu.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaclover", + "AssetName": "Assets/Prefabs/Hornet Enemies/Aspid Hatchling.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areadust", + "AssetName": "Assets/Prefabs/Hornet Enemies/Grove Pilgrim Fly.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areadust", + "AssetName": "Assets/Prefabs/Hornet Enemies/Caltrop.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areadust", + "AssetName": "Assets/Prefabs/Hornet Enemies/Caltrop Ball.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areadust", + "AssetName": "Assets/Prefabs/Hornet Enemies/Chef Maggot Blob.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areacloverareamoss", + "AssetName": "Assets/Prefabs/Hornet Enemies/SilkAcid BurstCloud.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areamoss", + "AssetName": "Assets/Prefabs/Enemies/Fungus 1 + 2/Grass Ball.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areamoss", + "AssetName": "Assets/Prefabs/Hornet Enemies/Aspid Collector Glob.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areacrawl", + "AssetName": "Assets/Prefabs/Hornet Enemies/Aspid Bullet.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areabone", + "AssetName": "Assets/Prefabs/Enemies/Projectiles/Rock Roller Bomb.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_shared", + "AssetName": "Assets/Prefabs/Hornet NPCs/Bellbeast Child.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areadocks", + "AssetName": "Assets/Prefabs/Enemies/Projectiles/DF Bomb Rock.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areawilds", + "AssetName": "Assets/Prefabs/Hornet Enemies/Spine Floater Spine.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_lifeblood", + "AssetName": "Assets/Prefabs/Hornet Enemies/Lifeblood Projectile.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_lifeblood", + "AssetName": "Assets/Prefabs/Items/Health Flyer.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaant", + "AssetName": "Assets/Prefabs/Hornet Enemies/Hunter Child Sickle.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaant", + "AssetName": "Assets/Prefabs/Hornet Enemies/Bone Hunter Javelin.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaantareamemory", + "AssetName": "Assets/Prefabs/Hornet Enemies/Bone Hunter Chief Javelin.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areagreymoor", + "AssetName": "Assets/Prefabs/Hornet Enemies/Centipede Farmer Projectile.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areagreymoor", + "AssetName": "Assets/Prefabs/Hornet Enemies/Crow.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areawisp", + "AssetName": "Assets/Prefabs/Hornet Enemies/Wisp Fireball.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areasong", + "AssetName": "Assets/Prefabs/Enemies/Fungus 1 + 2/Throwing Bell.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areashellwood", + "AssetName": "Assets/Prefabs/Hornet Enemies/Shellwood Gnat.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaarborium", + "AssetName": "Assets/Prefabs/Hornet Enemies/Lightning Bola Ball Enemy.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areacoralareamemory", + "AssetName": "Assets/Prefabs/Hornet Enemies/Coral Bubble.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_arealibrary", + "AssetName": "Assets/Prefabs/Enemies/Fungus 1 + 2/Lightbearer Globe Projectile.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_arealibrary", + "AssetName": "Assets/Prefabs/Hornet Bosses/Tormented Trobbio Tornado.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_arealibrary", + "AssetName": "Assets/Prefabs/Hornet Bosses/Trobbio Cross Bomb.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areasong", + "AssetName": "Assets/Prefabs/Hornet Enemies/Song Pilgrim 03.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_trobbio", + "AssetName": "Assets/Prefabs/Hornet Bosses/Trobbio Tornado.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_trobbio", + "AssetName": "Assets/Prefabs/Hornet Bosses/Trobbio Bomb.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areahang", + "AssetName": "Assets/Prefabs/Hornet Enemies/rune bomb small.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areahang", + "AssetName": "Assets/Prefabs/Hornet Enemies/rune slam.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areahangareasong", + "AssetName": "Assets/Prefabs/Hornet Enemies/Song Handmaiden.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaslab", + "AssetName": "Assets/Prefabs/Hornet Enemies/Slab Fly Glob.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaslab", + "AssetName": "Assets/Prefabs/Heroes/Tools/First Weaver Bomb Blast.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_shared", + "AssetName": "Assets/Prefabs/Effects/hero_maggoted_effect.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_shared", + "AssetName": "Assets/Prefabs/Enemies/Generic Attacks/Gas Explosion Recycle M.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_shared", + "AssetName": "Assets/Prefabs/Effects/Particle System/Knight Particles_follow.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaswamp", + "AssetName": "Assets/Prefabs/Hornet Enemies/Swamp Shaman Fireball.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areadustmaze", + "AssetName": "Assets/Prefabs/Hornet Enemies/Silkfly Mistmaze.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaaqueduct", + "AssetName": "Assets/Prefabs/Hornet Enemies/Skinny Mosquito Bullet.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaabyss", + "AssetName": "Assets/Prefabs/Hornet Enemies/Gloomfly.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaabyss", + "AssetName": "Assets/Prefabs/Enemies/Abyss Attacks/Abyss Bullet.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_areaabyss", + "AssetName": "Assets/Prefabs/Enemies/Abyss Attacks/Abyss Vomit Glob.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "localpoolprefabs_assets_laceboss", + "AssetName": "Assets/Prefabs/Hornet Bosses/Lost Lace/Lost Lace Summon Bullet.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "globalpoolprefabs_assets_all", + "AssetName": "Assets/Prefabs/Silk Possession Obj.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + }, + { + "BundleName": "herodynamic_assets_all", + "AssetName": "Assets/Prefabs/Heroes/Hornet Cocoon Corpse.prefab", + "Type": "UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" + } +] \ No newline at end of file diff --git a/AssetHelperTesting/Resources/scenegroup1.json b/AssetHelperTesting/Resources/scenegroup1.json new file mode 100644 index 0000000..ce8ebd7 --- /dev/null +++ b/AssetHelperTesting/Resources/scenegroup1.json @@ -0,0 +1,882 @@ +{ + "arborium_09": [ + "_Managers", + "_SceneManager", + "TileMap", + "MossBone Crawler (1)", + "MossBone Crawler Fat" + ], + "tut_02": [ + "bone_plat_01", + "bone_plat_02", + "Tiny Dragonfly (3)", + "green_grass_tri (6)/Green Grass A" + ], + "bonetown_boss": [ + "Boss Scene/Boulders Battle", + "Boss Scene/Skull King" + ], + "slab_16b": [ + "Broodmother Scene Control/Broodmother Scene/Battle Scene Broodmother/Spawner Flies", + "Broodmother Scene Control/Broodmother Scene/Battle Scene Broodmother/Spawner Flies/Slab Fly Small Fresh", + "Broodmother Scene Control/Broodmother Scene/Battle Scene Broodmother/Wave 4/Slab Fly Broodmother" + ], + "cog_dancers_boss": [ + "Dancer Control/Death Chunks 1" + ], + "slab_10b": [ + "Boss Scene/Pin Projectiles", + "Boss Scene/Loose Pins", + "Boss Scene/Blasts", + "Boss Scene/First Weaver" + ], + "shellwood_22": [ + "Boss Scene/Pt Shield Trail" + ], + "library_13": [ + "Grand Stage Scene/Boss Scene Trobbio/Flare Glitter", + "Grand Stage Scene/Boss Scene Trobbio/Floor Tiles", + "Grand Stage Scene/Boss Scene Trobbio/Trapdoor Bursts", + "Grand Stage Scene/Boss Scene TormentedTrobbio/Flare Glitter", + "Grand Stage Scene/Boss Scene TormentedTrobbio/Floor Tiles", + "Grand Stage Scene/Boss Scene TormentedTrobbio/Trapdoor Bursts", + "Grand Stage Scene/Boss Scene Trobbio/Trobbio", + "Grand Stage Scene/Boss Scene TormentedTrobbio/Tormented Trobbio" + ], + "coral_36": [ + "Judge Children Sing Trigger", + "Judge Children Trigger", + "Judge Child (1)" + ], + "shellwood_18": [ + "Boss Scene Parent/Boss Scene/Spikes" + ], + "memory_coral_tower": [ + "Boss Scene/Long Spear", + "Boss Scene/Air Spear", + "Boss Scene/Uppercut Spear", + "Boss Scene/Roar Spikes", + "Boss Scene/Cross Spears", + "Boss Scene/Cross Followup Spears", + "Boss Scene/Shoot Spikes", + "Battle Scenes/Battle Scene Chamber 2/Wave 9/Coral Conch Driller", + "Enemy Activator Groups/Enemy Activator Low/Enemy Folder/Coral Goomba M (2)", + "Enemy Activator Groups/Enemy Activator Low/Enemy Folder/Coral Goomba L", + "Battle Scenes/Battle Scene Chamber 3/Wave 5 - fish1/Coral Swimmer Fat (1)", + "Battle Scenes/Battle Scene Chamber 3/Wave 5 - fish1/Coral Poke Swimmer", + "Battle Scenes/Battle Scene Chamber 2/Wave 7b - Fish/Coral Spike Swimmer (1)", + "Battle Scenes/Battle Scene Chamber 2/Wave 2/Coral Warrior (1)", + "Battle Scenes/Battle Scene Chamber 4/Wave 3/Coral Bubble Brute", + "Battle Scenes/Battle Scene Chamber 2/Wave 10/Coral Brawler (1)", + "Battle Scenes/Battle Scene Chamber 2/Wave 1/Coral Hunter", + "Enemy Activator Groups/Enemy Activator Low/Enemy Folder/Coral Swimmer Small", + "Battle Scenes/Battle Scene Chamber 3/Wave 15b - double jellyfish/Coral Big Jellyfish", + "Battle Scenes/Battle Scene Chamber 1/Wave 5/Coral Flyer", + "Battle Scenes/Battle Scene Chamber 3/Wave 2b/Coral Flyer Throw", + "Battle Scenes/Battle Scene Chamber 2/Wave 10/Coral Brawler (1)/Stomp Spire L", + "Boss Scene/Roar Spikes/Spike Holder 1/Coral Spike", + "Boss Scene/Coral King", + "Fish/Pt Exit" + ], + "memory_ant_queen": [ + "Boss Scene/Grind Spikes R", + "Boss Scene/Grind Spikes L", + "Boss Scene/Battle Scene/Wave 4/Bone Hunter Fly Chief", + "Boss Scene/Hunter Queen Boss" + ], + "bone_east_08": [ + "Boss Scene/Lava Plats", + "Boss Scene/Lava Rocks", + "Boss Scene/Pre Activation Floor/Song Golem Floor (8)" + ], + "cog_04": [ + "Spike Cog collider Round/Cog Damager", + "cog_rail_hazard (9)/cog", + "Spike_cog_set_core (5)/Spike Cog 2", + "Spike_cog_set_core (5)/Spike Cog 3 (1)", + "cog_lever", + "Black Thread States Thread Only Variant/Normal World/Song Automaton 01", + "Black Thread States Thread Only Variant/Black Thread World/Group (1)/Song Automaton 02", + "Song Automaton Goomba", + "cog_plat_float_tiny (1)", + "cog_plat_float (2)" + ], + "under_06": [ + "junk_chute_ manual (1)", + "Grind Plat Control/Understore Grind Plat (1)", + "understore_junk_pipe" + ], + "bone_east_09": [ + "Lava Box", + "lava_set/LavaBase (1)" + ], + "abyss_07": [ + "Abyss Tendril Hero Damager", + "Abyss Tendrils (16)" + ], + "coral_11": [ + "Sand Centipede Hero Damager", + "Sand Centipede Attacker (10)" + ], + "bone_east_03": [ + "Coal Region", + "Black Thread States Thread Only Variant/Normal World/Bone Goomba", + "Black Thread States Thread Only Variant/Normal World/Bone Goomba Large", + "bone_goomba_skull_break", + "bone_goomba_skull_break_large)", + "Black Thread States Thread Only Variant/Normal World/Hunting PreScene/Bone Circler", + "Black Thread States Thread Only Variant/Black Thread World/Bone Circler Vicious (1)", + "Black Thread States Thread Only Variant/Normal World/Bone_Boulder", + "bone_plat_02 (2)", + "bone_plat_03 (2)", + "bone_plat_03 (6)", + "Dock Worker", + "lava_rocks_top_glower" + ], + "cradle_03": [ + "cradle_spike_plat (10)", + "Boss Scene/Rubble Fields/Rubble Field M", + "cradle_plat (7)", + "cradle_spike_plat (10)/art/Cradle__0004_moving_plat (9)" + ], + "coral_29": [ + "Zap Hazard Parent", + "Zap Worm Lightning (2)", + "Boss Scene/Zap Clusters/Cluster 1/Mega Jelly Zap", + "coral_zap_mounds_shortest (10)" + ], + "belltown": [ + "Town States/Spinner Defeated/Quest Board Pivot/Quest_Board", + "hanging_bell_house 2/plat_float_07", + "Hornet House States/Full/plat_float_07 (1)", + "Black Thread States Thread Only Variant/Normal World/shop_sign", + "Pin Pilgrim", + "Town States/Spinner Defeated/Bagpipers Not Here/Belltown Greeter Act3", + "BlurPlane" + ], + "arborium_04": [ + "MossBone Fly (1)", + "Enemy Respawner/Source Folder/Bone Thumper" + ], + "tut_03": [ + "Black Thread States/Normal World/Battle Scene/Wave 1/Mossbone Mother" + ], + "mosstown_01": [ + "Black Thread States Thread Only Variant/Black Thread World/Aspid Collector", + "Black Thread States Thread Only Variant/Normal World/Pilgrim Moss Spitter", + "Pilgrim 03 (1)", + "Pilgrim 01", + "Bone Lever" + ], + "greymoor_13": [ + "Pilgrim 03/Drop Dust", + "Black Thread States Thread Only Variant/Normal World/Pilgrim BellThrower", + "Black Thread States Thread Only Variant/Black Thread World/Pilgrim Bellthrower Fly" + ], + "bone_east_14b": [ + "Pilgrim 04", + "Pilgrim 02 (1)" + ], + "coral_32": [ + "Black Thread States/Black Thread World/Black_Thread_Core/Enemy Group/Pilgrim Fly", + "Black Thread States/Black Thread World/Black_Thread_Core/Enemy Group/Pilgrim Hiker", + "shell_plat_hang_bell (4)", + "Black Thread States/Normal World/Coral Judge (3)", + "fossil_judge_break_leanRight", + "blown_sand_tiled_set", + "Coral Conch Shooter (1)" + ], + "bonegrave": [ + "Pilgrim Groups/Group 1/Act3 Pilgrim 05", + "Pilgrim Groups/Group 2/Pilgrim StaffWielder", + "Pilgrim Groups/Rosary Pilgrim Scene/Rosary Pilgrim", + "Pilgrim Groups/Rosary Pilgrim Scene/Geo Small Persistent (4)" + ], + "mosstown_02": [ + "Pilgrim Trap Wire", + "traps_left/Pilgrim Trap Spike", + "Black Thread States Thread Only Variant/Normal World/Thick Silk Vines" + ], + "bonetown": [ + "Black Thread States/Normal World/Bonetown Resident", + "Black Thread States/Normal World/RestBench Control/Bench Getting Repaired/Fixer Pilgrim Bench Repair", + "Black Thread States/Normal World/fixer_constructs/fixer_statue/Shell Shard Fossil Big", + "rosary_shrine_small", + "Black Thread States/Black Thread World/Thief Scene/Rosary Thief Group/Rosary Thief" + ], + "weave_05b": [ + "Bone Goomba Bounce Fly (11)" + ], + "ant_19": [ + "Bone Crawler (2)", + "Enemy Break Cage (9)/Enemy/Bone Flyer", + "Boss Control/Boss Scene/Bone Flyer Giant", + "Updraft Region (1)" + ], + "arborium_03": [ + "Bone Roller", + "Flower Drifter (3)", + "Flower Drifter (4)/pollen_flare_attack", + "Bloom Shooter", + "hanging_gardens_plat_float_metal_small (3)", + "Arborium Plat Mid", + "Arborium Keeper" + ], + "bone_east_lavachallenge": [ + "Bone Spitter", + "bone_plat_01_crumble_small (2)", + "bone_plat_01_crumble (2)", + "bone_plat_02_crumble (1)", + "bone_plat_crumble_tall (4)", + "Heart Piece (1)" + ], + "bone_06": [ + "Rock Roller Scene/Rock Roller", + "Shell Fossil Mimic AppearVariant" + ], + "chapel_wanderer": [ + "Battle Scene/Gates/Battle Gate Bone" + ], + "bone_east_01": [ + "Black Thread States Thread Only Variant/Normal World/Dock Flyer", + "dock_plat_float_01 (1)", + "dock_plat_float_01 (9)" + ], + "dock_02": [ + "Tar Slug", + "Dock Bomber", + "Shield Dockworker Spawn/Shield Dockworker (2)" + ], + "dock_11": [ + "Tar Slug Huge (1)" + ], + "dock_02b": [ + "Dock Charger", + "lava_crumble_plat (5)" + ], + "bone_east_15": [ + "Song_Gate_small (3)", + "Song_lever_side", + "fung_plat_float_06", + "Fields Flock Flyer", + "Fields Goomba", + "Fields Flyer", + "bell_bench", + "bell_bench/RestBench", + "bell_bench/Enviro Region Simple" + ], + "under_19": [ + "Lava_Waterfall Set (4)", + "lava_crumble_plat", + "Understore Automaton", + "Understore Automaton EX (9)", + "Pilgrim Staff Understore", + "Pilgrim 03 Understore (1)" + ], + "dock_09": [ + "Boss Scene/Dock Guard Slasher", + "Boss Scene/Dock Guard Thrower" + ], + "room_forge": [ + "_NPCs/Forge Daughter" + ], + "bone_east_14": [ + "bone_plat_03", + "Explode Floor Scene/DropBomb Rock (2)", + "Spine Floater (9)", + "explode_wall (4)" + ], + "bone_east_24": [ + "Bone Hopper Group/Bone Hopper Simple", + "Bone Hopper Group/Bone Hopper Giant", + "Ant Trapper Quest Scene (3)/Tracking Scene/Trapper Barb Trap Landmine" + ], + "bone_east_10_church": [ + "Rhino Scene/Rhino", + "Black Thread States Thread Only Variant/Normal World/Flea Rescue Sleeping" + ], + "bone_east_08_boss_golem": [ + "Boss Scene" + ], + "crawl_04": [ + "Little Crabs/Crabs/Small Crab", + "Roof Crab" + ], + "crawl_01": [ + "Crypt Worms/GameObject/Crypt Worm (9)", + "Bone Worm Nests/Worm Pool/Bone Worm (2)" + ], + "crawl_03": [ + "Area_States/Infected/Bone Worm BlueBlood (1)", + "Area_States/Infected/Bone Worm BlueTurret", + "PUSTULE_States/Active/pustule_set_small (1)/PUSTULE", + "bone_plat_02 (4)" + ], + "crawl_10": [ + "Area_States/Infected/Blue Assistant" + ], + "crawl_09": [ + "Area_States/Infected/Health Cocoon" + ], + "crawl_05": [ + "Group/bone_plat_01 (3)", + "Crest_shrine_corner_plat (2)" + ], + "ant_04": [ + "Black Thread States Thread Only Variant/Normal World/Bone Hunter Tiny", + "Black Thread States Thread Only Variant/Normal World/Bone Hunter Buzzer", + "Black Thread States Thread Only Variant/Normal World/Bone Hunter Child", + "Black Thread States Thread Only Variant/Normal World/Bone Hunter", + "White Palace Fly", + "Hunter Sickle Trap", + "Hunter Trap Plate", + "Silkcatcher Plant" + ], + "ant_21": [ + "Enemy Control/Ant Merchant Killed/Big Guard Dead/Bone Hunter Fly", + "Enemy Control/Normal/Bone Hunter Throw", + "ant_rosary_string", + "ant_rosary_string_medium (1)", + "ant_rosary_string_large" + ], + "ant_09": [ + "Fields Harpoon Ring Pole" + ], + "greymoor_06": [ + "Farmer Scissors", + "Mite", + "Greymoor_windmill_cog (1)/GameObject/dustpen_trap_shine0000" + ], + "greymoor_05": [ + "Scene Control/Farmer Enemies/Roosting Enemies/Farmer Catcher (2)", + "Scene Control/Farmer Enemies/Farmer Centipede (1)" + ], + "greymoor_16": [ + "Gnat Giant" + ], + "greymoor_03": [ + "Mitefly (1)", + "Weathervane States/Post Quest/Scarecraw", + "greymoor_balloon_small (2)", + "greymoor_balloon_mid (1)", + "greymoor_balloon_large", + "Black Thread States Thread Only Variant/Normal World/Strut Structure/Tilt Plat", + "break_grey_lamp_dual_twist (1)" + ], + "greymoor_15b": [ + "Crowman", + "Crowman Dagger (1)" + ], + "room_crowcourt_02": [ + "Battle Scene/Wave 2/Crowman Juror Tiny", + "Battle Scene/Wave 1/Crowman Juror", + "Battle Scene/Wave 1/Crowman Dagger Juror", + "Battle Scene/Wave 6/Crawfather", + "Battle Scene/Wave 6/Crawfather/Chains/Crawfather Attack Chain" + ], + "greymoor_05_boss": [ + "Vampire Gnat Boss Scene/Vampire Gnat" + ], + "greymoor_07": [ + "grey_lever_gate (1)", + "Greymoor_Rain_Tiled_Set" + ], + "dust_05": [ + "Dustroach", + "Roachkeeper" + ], + "dust_02": [ + "Roachfeeder Short", + "Black Thread States Thread Only Variant/Normal World/Roachfeeder Tall", + "greymoor_flip_bridge (1)" + ], + "dust_chef": [ + "Battle Parent/Kitchen Pipe Gong/kitchen_string_offset/kitchen_string", + "Battle Parent/Battle Scene/Wave 1/Roachkeeper Chef Tiny", + "Battle Parent/Battle Scene/Wave 2/Roachkeeper Chef (1)" + ], + "dust_11": [ + "Steel Soul States/Regular/NPC Control/Large Cocoon 1" + ], + "sprintmaster_cave": [ + "Race Group/Tracks/Track 2/Sprintmaster Bounce Pod (4)" + ], + "wisp_02": [ + "Wisp Bounce Pod", + "Wisp Farmers/Wisp Flame Lantern", + "Wisp Farmers/Farmer Wisp" + ], + "belltown_basement_03": [ + "Bell Goomba", + "Bell Fly", + "rosary_cache_bell_ground" + ], + "belltown_04": [ + "Drop Bell (1)" + ], + "shellwood_01": [ + "shellwood_plat_float_thin", + "shellwood_plat_float_wide", + "Black Thread States/Normal World/Pilgrim Fisher Enemy (1)", + "Shellwood Goomba", + "Shellwood Goomba Flyer (1)", + "Black Thread States/Black Thread World/Shakra Guard Scene/Scene Folder/Mapper StandGuard NPC" + ], + "arborium_05": [ + "Shellwood Bounce Bloom", + "Pond Skater", + "Bloom Puncher" + ], + "shellwood_10": [ + "weaver_harp_sign", + "Flower Drifter", + "pollen_particles (1)", + "Ability Scene (1)/Shrine Weaver Ability" + ], + "shellwood_02": [ + "Shellwood Wasp", + "Stick Insect", + "Stick Insect Charger" + ], + "shellwood_26": [ + "Black Thread States/Normal World/Stick Insect Flyer (1)" + ], + "belltown_room_shellwood": [ + "shell_hang_rope" + ], + "coral_34": [ + "Harpoon Ring Pinstress Rope (4)" + ], + "song_20b": [ + "sc_plat_hang_bell (5)", + "Dial Door Bridge" + ], + "coral_judge_arena": [ + "Boss Scene/Last Judge" + ], + "coral_37": [ + "Room_States/Steel/Steel Sentinel" + ], + "coral_24": [ + "Coral Spike Goomba", + "Coral Conch Shooter Heavy (1)", + "Coral Conch Stabber (1)", + "coral_crust_tree (5)/Coral Crust Tree Activator", + "coral_crust_tree (5)/Interactive Activate Parent/Branch 1/Coral Crust Tree Spike Red", + "coral_crust_tree (10)/Interactive Activate Parent/Branch 1/Coral Crust Tree Spike Grey", + "coral_crust_tree (5)/Interactive Activate Parent/Branch 1/Coral Crust Tree Plat Small Grey", + "coral_crust_tree (7)/Interactive Activate Parent/Branch 1/Coral Crust Tree Plat Small Red", + "coral_crust_tree (5)/Interactive Activate Parent/Branch 1/Coral Crust Tree Plat Mid Red", + "Coral_plat_float_green_medium (1)" + ], + "arborium_06": [ + "Coral Goomba Large (1)", + "Coral Crust Wall Small", + "Coral Crust Wall Mid (1)", + "Coral Crust Wall Tall (3)" + ], + "coral_39": [ + "Coral Warrior Grey" + ], + "coral_tower_01": [ + "Coral_Warrior_break" + ], + "arborium_08": [ + "Giant Flea Scene/Giant Flea" + ], + "arborium_02": [ + "ant_tiny_white_bug_swarm" + ], + "under_03": [ + "fan_hazard" + ], + "cog_09": [ + "puzzle cylinders/Cog_Choir_Cylinder/Anim Offset/Cog 1/puzzle cog lever" + ], + "cog_06": [ + "Song Automaton Shield" + ], + "cog_05": [ + "Battle Scene/Wave 2/Song Automaton Fly Spike" + ], + "cog_07": [ + "Black Thread States/Normal World/Song Automaton Fly (3)", + "Black Thread States/Normal World/Repairable Scene/Song Automaton Ball (1)" + ], + "under_17": [ + "Architect Scene/Chair/pillar E/pillar D/pillar C/pillar B/pillar A/seat/Architect NPC" + ], + "under_19b": [ + "Understore Thrower" + ], + "under_10": [ + "Battle Scene/Wave 1/Understore Small", + "Battle Scene/Wave 2/Understore Poker", + "Battle Scene/Wave 6/Understore Heavy (1)" + ], + "slab_15": [ + "Mite Heavy (1)", + "Slab Prisoner Leaper New", + "Slab Prisoner Fly New" + ], + "hang_10": [ + "Understore Mite Giant", + "Citadel Bat" + ], + "arborium_11": [ + "Merchant Quest Parent/Quest Active/Battle Scene/Wave 1/Citadel Bat Large" + ], + "under_07": [ + "steam_vent_short (3)", + "Battle Scene/Gates/steam_vent_short (2)" + ], + "under_05": [ + "cog_05_shortcut/before/blocking cogs/Spike Cog 3", + "cog_05_shortcut/before/blocking cogs/Spike Cog 2", + "dock_metal_grate_floor_set (1)" + ], + "under_03d": [ + "Black Thread States/Normal World/Understore Large Worker" + ], + "library_04": [ + "Acolyte Control/Song Scholar Acolyte", + "Black Thread States/Normal World/Lightbearer (3)", + "Black Thread States/Normal World/Scrollkeeper", + "Scholar", + "library_lamp_stand (1)", + "library_lamp_wall (2)" + ], + "song_11": [ + "Black Thread States Thread Only Variant/Normal World/Pilgrim 01 Song", + "Pilgrim 02 Song", + "Pilgrim 04 Song (2)", + "Pilgrim Stomper Song", + "metronome_plat (11)", + "sc_plat_float_mid (5)" + ], + "song_17": [ + "March Group Control/March Group R/Song Pilgrim 01", + "Garmond Fight Scene/Garmond Fighter" + ], + "hang_04_boss": [ + "Battle Scene/Wave 3/Pilgrim 03 Song", + "Battle Scene/Wave 8 - Heavy Sentry/Song Heavy Sentry", + "Battle Scene/Wave 5 - Song Admins/Song Administrator", + "Battle Scene/Wave 13 - Maestro/Song Pilgrim Maestro", + "Battle Scene/Wave 1/Song Reed" + ], + "hang_07": [ + "Black Thread States/Normal World/Unscaler/Song Reed Grand (1)" + ], + "song_25": [ + "Black Thread States/Normal World/Song Reed Grand (2)/grand_reed_spell_sphere", + "Song Knight Control/Song Knight Present/Song Knight BattleEncounter" + ], + "song_09": [ + "Hornet_pressure_plate_small_persistent", + "Citadel Switch Gate" + ], + "coral_10": [ + "Hornet_pressure_plate/Plate", + "Song Gate Entrance Right", + "Seth Stand NPC", + "Black Thread States/Normal World/Hornet_way_pole_harp (9)" + ], + "song_enclave": [ + "sc_plat_float_fat", + "Black Thread States/Normal World/Enclave States/States/Level 1/Enclave Simple NPC Tall", + "Black Thread States/Normal World/Enclave States/States/Level 1/Enclave Caretaker", + "Black Thread States/Normal World/Enclave States/States/Level 2/Sherma Enclave NPC", + "Black Thread States/Black Thread World/Enclave Act 3/Sherma Caretaker" + ], + "song_12": [ + "Black Thread States/Normal World/sc_plat_float_fat (1)" + ], + "song_01": [ + "sc_plat_float_tall" + ], + "cog_dancers": [ + "Black Thread States/Normal World/harpoon_ring_gate" + ], + "ward_09": [ + "Sherma Rescue Scene/Activation Folder/Battle Scene/Wave 1/Song Pilgrim 02" + ], + "ward_03": [ + "Song Creeper (2)", + "brk_barrel_03_opencoal" + ], + "ward_02": [ + "Boss Scene Parent/Respawn Scene/Husks/Slasher 1", + "Boss Scene Parent/Respawn Scene/Husks/Slammer 1", + "Boss Scene Parent/Silk Heart" + ], + "bone_11b": [ + "Silk Spool" + ], + "slab_05": [ + "Slab Fly Small", + "spike_trap_slab_jail/pressure_plate", + "Jail Gate Door (2)" + ], + "slab_04": [ + "Slab Fly Mid (2)" + ], + "slab_22": [ + "Slab Fly Large", + "slab_jail_lever" + ], + "bone_east_04c": [ + "Scene Control/Slab Jailer Scene/Slab Fly Large Cage" + ], + "slab_21": [ + "slab_spike_ball", + "trap_spinning_blade_S_bend_slab_jail (4)/prop_blade" + ], + "peak_06": [ + "Crystal Drifter", + "Crystal Drifter Giant", + "Float Crystal (13)" + ], + "peak_05": [ + "Peaks Drifter", + "chair_lift_ring/Harpoon Ring Citadel", + "weaver_heat_lamp (2)/Lamp", + "coal_lantern_jail_wall_mount/string_cap", + "Slide Surface (2)", + "peak_storm_set_mid_strength" + ], + "hang_08": [ + "Harpoon Ring VerticalRide" + ], + "cog_08": [ + "Harpoon Ring Rail Slider" + ], + "bellway_peak_02": [ + "Snowflake Chunk (82)", + "weaver lamp_roof heat large" + ], + "peak_mask_maker": [ + "Peak Mask Maker" + ], + "peak_08b": [ + "DJ Get Sequence/Fayforn Ground Sit NPC" + ], + "shadow_02": [ + "Bloat Roach", + "Black Thread States Thread Only Variant/Normal World/Swamp Goomba", + "plank_plat (4)", + "Swamp Bounce Pod", + "moss_crumble_plat", + "Shakra Trail Quest Parent/Active/Tracking Trail (2)/Silhouettes Parent/Silhoutte", + "Mapper/Mapper_ambient_rings/rings/mapper extra rings/mapper rings/Mapper_Ring_world (3)" + ], + "shadow_04": [ + "Swamp Mosquito (3)", + "GameObject/Surface Water Region" + ], + "hang_09": [ + "Soft Waterfall Region", + "coral_river_chunk/particle_barrel_splash", + "coral_river_chunk/river_top/Base", + "coral_river_chunk/waterfall/Base" + ], + "shadow_12": [ + "Swamp Muckman All Control/Swamp Muckman (4)", + "Swamp Muckman All Control/Swamp Muckman Tall Control/Activation Folder/Swamp Muckman Tall" + ], + "shadow_26": [ + "Swamp Drifter", + "gloom_lift_destroy/gloom_lift_set/gloom_plat_lift destroy" + ], + "shadow_18": [ + "Battle Scene/Wave 6 - Boss/Swamp Shaman", + "maggot_sack_break (1)", + "Battle Scene/stake_trap_swing_repeater", + "Battle Scene/Gates/Battle Gate Swamp" + ], + "shadow_10": [ + "Swamp Stake Shooter Folder (1)/Swamp Stake Shooter", + "Spike Ball Folder/stake_trap_swing" + ], + "shadow_27": [ + "Breakable Hang Sack 2" + ], + "dust_maze_01": [ + "Wraith", + "Mist Maze Controller/Trap Sets/Trap Set/Dust Trap Spike Plate", + "Mist Maze Controller/Trap Sets/Trap Set/Dust Trap Spike Dropper", + "Mist Maze Controller/Trap Sets/Trap Set/Mite Trap" + ], + "organ_01": [ + "Boss Scene/Phantom", + "Spike (7)", + "Organ_outer__0012_balcony_side_plat", + "GameObject (58)/metal_bridge (2)", + "organ_lift_broken_drop/lift_bottom_broken" + ], + "aqueduct_03": [ + "Swamp Mosquito Skinny", + "Swamp Barnacle (1)", + "Swamp Ductsucker", + "waterways_particles (1)", + "Breakable Wall" + ], + "aqueduct_05_caravan": [ + "Caravan_States/Fleatopia/Caravan Lech/Caravan Lech Wounded" + ], + "aqueduct_04": [ + "Swamp Barnacle Slab Fly" + ], + "abyss_05": [ + "Abyss Crawler (2)", + "Abyss Crawler Large (1)", + "abyss_plat_mid", + "abyss_plat_wide", + "Abyss Bounce Pod" + ], + "abyss_02b": [ + "Gloom Beast" + ], + "memory_red": [ + "Scenery Groups/End Scenery/White Palace Fly Red Memory (1)", + "Scenery Groups/Deepnest Scenery/Control Lever", + "Scenery Groups/Entry Scenery/memory_ground_plat (6)", + "thread_memory_region/web_particles (1)", + "Scenery Groups/Entry Scenery/red_memory_silk_pod0007 (15)", + "Scenery Groups/Hive Scenery/Hive_Break_01", + "Scenery Groups/Deepnest Scenery/plat_float_07", + "Scenery Groups/Deepnest Scenery/deepnest_platform_05", + "Scenery Groups/Deepnest Scenery/deepnest_platform_03", + "Scenery Groups/Deepnest Scenery/deepnest_platform_04", + "Scenery Groups/Deepnest Scenery/deepnest_platform_01", + "Scenery Groups/Hive Scenery/hive_plat_04 (1)", + "Scenery Groups/Hive Scenery/hive_plat_02", + "Scenery Groups/Hive Scenery/hive_plat_01" + ], + "clover_02c": [ + "water_components_moss_short 1/caustic_small_000 (13)", + "grove_pod (1)/Clover Bounce Pod Activator", + "grove_pod (1)/Interactive Parent/Clover Bounce Pod (3)", + "Memory Orb Group/Clover_Statue_Break Orb", + "Grass Goomba", + "Lilypad Plat/Lilypad Fly" + ], + "tut_04": [ + "States/Outro Scene/Snail_Shell_Small", + "States/Outro Scene/Snail_Shell_Mid", + "States/Outro Scene/Snail_Shell_Large" + ], + "song_04": [ + "Black Thread States/Normal World/Scene States/Green Prince Stand Song_04" + ], + "clover_04b": [ + "Lilypad Trap Setter/Lilypad Plat (1)", + "Battle Scene/Wave 3/Grasshopper Child (1)", + "Battle Scene/Return Scene/Grasshopper Slasher", + "Grasshopper Fly", + "break_grey_lamp_harp" + ], + "clover_06": [ + "Cloverstag (2)", + "Group/Clover_Silk_Pod" + ], + "clover_21": [ + "Group/clover_gate_outer_0000_1 (53)", + "Group/clover_gate_outer_0000_1 (54)", + "clover___0019_roof2_plat (5)" + ], + "clover_05c": [ + "Hornet_pressure_plate_small_persistent", + "Clover Gate (1)", + "Hornet Dragonfly" + ], + "clover_18": [ + "Dragonfly Large" + ], + "aqueduct_05_festival": [ + "Caravan_States/Flea_Games_Start_effect/confetti_burst (1)", + "Flea Games Counter", + "Caravan_States/Flea Festival/Flea Game - Juggling/Flea Games Host NPC" + ], + "cradle_destroyed_challenge_01": [ + "Cradle Challenge Pea Break", + "Centipede Trap Control/Centipede Trap", + "Spike Lazy Flyer" + ], + "abandoned_town": [ + "plat_float_06", + "Surface Scuttler", + "collid" + ], + "cradle_destroyed_challenge_02": [ + "Blade Spider", + "Blade Spider Hang" + ], + "bone_19": [ + "Bone Chest", + "Breakable Wall" + ], + "song_03": [ + "Chest Scene/Chest" + ], + "dock_06_church": [ + "Black Thread States Thread Only Variant/Normal World/City Shard Chest" + ], + "under_08": [ + "Understore Toll Bench (2)" + ], + "belltown_room_spare": [ + "furnishings/bed/RestBench" + ], + "hang_06_bank": [ + "rosary_cannon/Art/Rosary Cannon Scene/rosary_string_machine" + ], + "hang_06b": [ + "new_scene/Reflection_surface" + ], + "ant_17": [ + "Gilly" + ], + "library_09": [ + "Black Thread States/Normal World/Scene Control/Garmond Scene/Garmond Fighter" + ], + "coral_33": [ + "Black Thread States/Black Thread World/Garmond Scenes/Garmond Black Threaded Scene/Garmond Black Threaded Fighter" + ], + "hang_01": [ + "Thread Spinner" + ], + "weave_04": [ + "top set/Song_Gate_set (2)", + "Weaver Servitor (2)" + ], + "peak_04d": [ + "Weaver Servitor Large" + ], + "weave_12": [ + "weaver_lift_power_chamber/switches/Lever_Left" + ], + "greymoor_08_mapper": [ + "Mapper Call Pole", + "Mapper Spar NPC" + ], + "hang_17b": [ + "Boss Scene - To Additive Load/Song Knight" + ], + "bone_steel_servant": [ + "Steel Servant Scene/Battle Scene/Wave 1/Abyss Mass" + ], + "song_19_entrance": [ + "Black Thread States/Black Thread World/black_thread_strand" + ], + "greymoor_20c": [ + "Crest Get Shrine" + ], + "song_15": [ + "Black Thread States/Black Thread World/Black_Thread_Core_Citadel" + ], + "bone_01c": [ + "bell_bench/frame/small_bell Parent", + "bell_bench/frame/Bell Main Parent" + ] +} \ No newline at end of file diff --git a/AssetHelperTesting/Resources/scenegroup2.json b/AssetHelperTesting/Resources/scenegroup2.json new file mode 100644 index 0000000..2e3903f --- /dev/null +++ b/AssetHelperTesting/Resources/scenegroup2.json @@ -0,0 +1,188 @@ +{ + "memory_coral_tower": [ + "Group (4)/Pt Fish Shiny (11)", + "Group (4)/Pt Fish Fat (12)", + "Group (4)/Pt Fish Finny (12)", + "Group (4)/Pt Fish Shiny (13)", + "Group (4)/Pt Fish Finny (13)", + "Group (4)/Pt Fish Fat (10)", + "Fish/Pt Main/BG (2)/Pt Fish Shiny (3)", + "Fish/Pt Main/BG (2)/Pt Fish Finny (3)", + "Fish/Pt Main/BG (2)/Pt Fish Fat (3)", + "Fish/Pt Main/BG (2)/Pt Fish Shiny (1)", + "Fish/Pt Main/BG (2)/Pt Fish Finny (1)", + "Fish/Pt Main/BG (2)/Pt Fish Fat (1)", + "Fish/Pt Main/BG (2)/Pt Fish Fat", + "Fish/Pt Main/BG (2)/Pt Fish Finny", + "Fish/Pt Main/BG (2)/Pt Fish Shiny", + "Fish/Pt Main/BG (2)/Pt Fish Finny (2)", + "Fish/Pt Main/BG (2)/Pt Fish Shiny (2)", + "Fish/Pt Main/BG (2)/Pt Fish Fat (4)", + "Fish/Pt Exit/BG (2)/Pt Fish Finny", + "Fish/Pt Exit/BG (2)/Pt Fish Finny (1)", + "Fish/Pt Exit/BG (2)/Pt Fish Shiny", + "Fish/Pt Exit/BG (2)/Pt Fish Shiny (1)", + "Fish/Pt Exit/BG (2)/Pt Fish Shiny (2)", + "Fish/Pt Exit/BG (2)/Pt Fish Shiny (3)", + "Fish/Pt Exit/BG (2)/Pt Fish Finny (2)", + "Fish/Pt Exit/BG (2)/Pt Fish Finny (5)", + "Fish/Pt Exit/BG (2)/Pt Fish Shiny (5)", + "Fish/Pt Exit/FG (1)/Pt Fish Fat", + "Fish/Pt Exit/FG (1)/Pt Fish Fat (1)", + "Fish/Pt Exit/FG (1)/Pt Fish Finny", + "Fish/Pt Exit/FG (1)/Pt Fish Finny (1)", + "Fish/Pt Exit/FG (1)/Pt Fish Shiny", + "Fish/Pt Exit/FG (1)/Pt Fish Shiny (1)", + "Fish/Pt Exit/FG (1)/Pt Fish Finny (2)", + "Fish/Pt Exit/FG (1)/Pt Fish Fat (3)", + "Fish/Pt Exit/FG (1)/Pt Fish Shiny (2)", + "Fish/Pt Entry/FG/Pt Fish Fat", + "Fish/Pt Entry/FG/Pt Fish Fat (1)", + "Fish/Pt Entry/FG/Pt Fish Finny", + "Fish/Pt Entry/FG/Pt Fish Finny (1)", + "Fish/Pt Entry/FG/Pt Fish Shiny", + "Fish/Pt Entry/FG/Pt Fish Shiny (1)", + "Fish/Pt Entry/FG/Pt Fish Finny (2)", + "Fish/Pt Entry/FG/Pt Fish Fat (3)", + "Fish/Pt Entry/FG/Pt Fish Shiny (2)", + "Fish/Pt Entry/BG/Pt Fish Fat", + "Fish/Pt Entry/BG/Pt Fish Fat (1)", + "Fish/Pt Entry/BG/Pt Fish Finny", + "Fish/Pt Entry/BG/Pt Fish Finny (1)", + "Fish/Pt Entry/BG/Pt Fish Shiny", + "Fish/Pt Entry/BG/Pt Fish Shiny (1)", + "Fish/Pt Entry/BG/Pt Fish Fat (3)", + "Fish/Pt Entry/BG/Pt Fish Finny (2)", + "Fish/Pt Entry/BG/Pt Fish Finny (3)", + "Fish/Pt Entry/BG/Pt Fish Shiny (2)", + "Fish/Pt Entry/BG/Pt Fish Shiny (3)", + "Fish/Pt Entry/BG (1)/Pt Fish Finny", + "Fish/Pt Entry/BG (1)/Pt Fish Finny (1)", + "Fish/Pt Entry/BG (1)/Pt Fish Shiny", + "Fish/Pt Entry/BG (1)/Pt Fish Shiny (1)", + "Fish/Pt Entry/BG (1)/Pt Fish Shiny (2)", + "Fish/Pt Entry/BG (1)/Pt Fish Shiny (3)", + "Fish/Pt Entry/BG (1)/Pt Fish Finny (2)", + "Fish/Pt Entry/BG (1)/Pt Fish Finny (5)", + "Fish/Pt Entry/BG (1)/Pt Fish Shiny (5)", + "Group (38)/Coral_lamp_hang_single (1)", + "Group (38)/Coral_lamp_hang_double (1)", + "Group (38)/Coral_lamp_hang_double", + "Group (38)/Coral_lamp_hang_single", + "Group (41)/Coral_lamp_hang_single (4)", + "Group (17)/Coral_lamp_hang_double", + "Group (8)/white_palace_light_beam (25)", + "Group (8)/white_palace_light_beam (26)", + "Group (8)/white_palace_light_beam (28)", + "Coral_BG_set (1)/white_palace_light_beam", + "Coral_BG_set (1)/white_palace_light_beam (22)", + "Coral_BG_set (1)/white_palace_light_beam (4)", + "Coral_BG_set (1)/white_palace_light_beam (1)", + "Coral_BG_set (1)/white_palace_light_beam (6)", + "Coral_BG_set (1)/white_palace_light_beam (9)", + "Coral_BG_set (1)/white_palace_light_beam (10)", + "Coral_BG_set (1)/white_palace_light_beam (11)", + "Coral_BG_set (1)/white_palace_light_beam (12)", + "Coral_BG_set (1)/white_palace_light_beam (15)", + "Coral_BG_set (1)/white_palace_light_beam (20)", + "Coral_BG_set (1)/white_palace_light_beam (23)", + "Coral_BG_set (1)/white_palace_light_beam (24)", + "Coral_BG_set (1)/white_palace_light_beam (14)", + "Group (38)/pillar_piece (2)", + "Group (38)/pillar_piece", + "Group (17)/pillar_piece (3)", + "Group (8)/temp fog (32)", + "Coral_BG_set (1)/temp fog (1)", + "Coral_BG_set (1)/temp fog (2)", + "Coral_BG_set (1)/temp fog (3)", + "Coral_BG_set (1)/temp fog (5)", + "Coral_BG_set (1)/temp fog (59)", + "Coral_BG_set (1)/temp fog (7)", + "Coral_BG_set (1)/temp fog (8)", + "Coral_BG_set (1)/temp fog (9)", + "Coral_BG_set (1)/temp fog (10)", + "Coral_BG_set (1)/temp fog (11)", + "Coral_BG_set (1)/temp fog (12)", + "Coral_BG_set (1)/temp fog (13)", + "Coral_BG_set (1)/temp fog (14)", + "Coral_BG_set (1)/temp fog (60)", + "Coral_BG_set (1)/temp fog (61)", + "Coral_BG_set (1)/temp fog (15)", + "Coral_BG_set (1)/temp fog (16)", + "Coral_BG_set (1)/temp fog (17)", + "Coral_BG_set (1)/temp fog (18)", + "Coral_BG_set (1)/temp fog (21)", + "Coral_BG_set (1)/temp fog (22)", + "Coral_BG_set (1)/temp fog (23)", + "Coral_BG_set (1)/temp fog (24)", + "Coral_BG_set (1)/temp fog (26)", + "Coral_BG_set (1)/temp fog (64)", + "Coral_BG_set (1)/temp fog (70)", + "Coral_BG_set (1)/temp fog (65)", + "Coral_BG_set (1)/temp fog (27)", + "Coral_BG_set (1)/temp fog (28)", + "Coral_BG_set (1)/temp fog (30)", + "Coral_BG_set (1)/temp fog (63)", + "Coral_BG_set (1)/temp fog (25)", + "Coral_BG_set (1)/temp fog (29)", + "Coral_BG_set (1)/temp fog (52)", + "Coral_BG_set (1)/temp fog (32)", + "Coral_BG_set (1)/temp fog (66)", + "Coral_BG_set (1)/temp fog (67)", + "Coral_BG_set (1)/temp fog (33)", + "Coral_BG_set (1)/temp fog (34)", + "Coral_BG_set (1)/temp fog (35)", + "Coral_BG_set (1)/temp fog (36)", + "Coral_BG_set (1)/temp fog (37)", + "Coral_BG_set (1)/temp fog (38)", + "Coral_BG_set (1)/temp fog (39)", + "Coral_BG_set (1)/temp fog (40)", + "Coral_BG_set (1)/temp fog (41)", + "Coral_BG_set (1)/temp fog (42)", + "Coral_BG_set (1)/temp fog (43)", + "Coral_BG_set (1)/temp fog (44)", + "Coral_BG_set (1)/temp fog (45)", + "Coral_BG_set (1)/temp fog (46)", + "Coral_BG_set (1)/temp fog (62)", + "Coral_BG_set (1)/temp fog (49)", + "Coral_BG_set (1)/temp fog (50)", + "Coral_BG_set (1)/temp fog (51)", + "Coral_BG_set (1)/temp fog (31)", + "Coral_BG_set (1)/temp fog (68)", + "Coral_BG_set (1)/temp fog (69)", + "Coral_BG_set (1)/temp fog (53)", + "Coral_BG_set (1)/temp fog (54)", + "Coral_BG_set (1)/temp fog (55)", + "Coral_BG_set (1)/temp fog (56)", + "Coral_BG_set (1)/temp fog (57)", + "Coral_BG_set (1)/temp fog (58)", + "Boss Scene/coral_king_final_throne/initial_darkeners/temp fog (69)", + "Boss Scene/coral_king_final_throne/fogger/temp fog (52)", + "Group (4)/temp fog (63)", + "Group/waterfall caustic", + "waterfall caustic (1)/waterfall caustic (3)", + "Top (33)", + "Top (29)", + "Top (32)", + "waterfall_base_large (1)/Fungus_Steam (2)", + "waterfall_base_large (1)/particle_barrel_splash (4)", + "waterfall_base_large (1)/particle_barrel_splash (5)", + "Group (6)/Audio Waterfall Small" + ], + "hang_02": [ + "coral_river_chunk/river_top", + "Surface Water Region", + "water_components_short_simple_white/StillWater", + "water_components_short_simple_white/waterways_water_components/acid_water_top", + "Surface Water Region (1)", + "coral_river_chunk/corner" + ], + "hang_03": [ + "coral_river_chunk/waterfall" + ], + "coral_26": [ + "Coral_Spikes_pin (10)/Coral_extras_01_0005_s3", + "Coral_Spikes_pin (12)/Coral_extras_01_0004_s4 (3)", + "Spike Collider Barb (2)" + ] +} \ No newline at end of file diff --git a/AssetHelperTesting/Tests/DependentParentTest.cs b/AssetHelperTesting/Tests/DependentParentTest.cs new file mode 100644 index 0000000..fb9da00 --- /dev/null +++ b/AssetHelperTesting/Tests/DependentParentTest.cs @@ -0,0 +1,113 @@ +using HutongGames.PlayMaker; +using HutongGames.PlayMaker.Actions; +using Silksong.AssetHelper.ManagedAssets; +using Silksong.FsmUtil; +using UnityEngine; + +namespace AssetHelperTesting.Tests; + +/// +/// Test spawning an asset which depends on an ancestor. +/// +public class DependentParentTest : MonoBehaviour +{ + public KeyCode SpawnHotkey { get; set; } + + public static void Prepare(KeyCode spawnHotkey = KeyCode.H) + { + GameObject go = new("MossMother Spawner"); + DontDestroyOnLoad(go); + DependentParentTest component = go.AddComponent(); + component.SpawnHotkey = spawnHotkey; + } + + private ManagedAsset _asset; + + void Awake() + { + _asset = ManagedAsset.FromSceneAsset( + sceneName: "Tut_03", + objPath: "Black Thread States/Normal World/Battle Scene/Wave 1/Mossbone Mother"); + + Md.HeroController.Start.Postfix(DoLoad); + } + + private void DoLoad(HeroController self) + { + _asset.Load(); + } + + // Code lifted from https://github.com/cometcake575/Architect-Silksong/blob/main/Behaviour/Fixers/EnemyFixers.cs + public static void FixMossMother(GameObject obj) + { + PlayMakerFSM fsm = obj.LocateMyFSM("Control"); + + // Variables to align + FsmVariables vars = fsm.FsmVariables; + FsmFloat centreX = vars.FindFsmFloat("Centre X"); + FsmFloat leftX = vars.FindFsmFloat("Left X"); + FsmFloat rightX = vars.FindFsmFloat("Right X"); + FsmFloat swoopY = vars.FindFsmFloat("Swoop Height"); + FsmFloat maxY = vars.FindFsmFloat("Max Height"); + + // Align based on self + fsm.GetState("Init")!.InsertMethod(() => Realign(obj.transform.position), 0); + + // Wake + obj.GetComponent().enabled = true; + obj.transform.GetChild(0).gameObject.SetActive(false); + fsm.GetState("Dormant")!.InsertMethod(() => + { + if (obj.GetComponent().isDead) return; + fsm.SetState("Roar"); + }, 0); + + // Disable stun + fsm.GetState("Roar")!.GetAction(9)!.stunHero = false; + + // Align based on player + fsm.GetState("Idle")!.InsertMethod(() => Realign(HeroController.instance.transform.position), 0); + + // Fix stuck + fsm.GetState("Slam RePos")!.InsertMethod(() => fsm.SendEvent("FINISHED"), 0); + + // Swoop follow alignment + FsmState swoop = fsm.GetState("Swoop")!; + swoop.DisableAction(1); + swoop.DisableAction(2); + swoop.DisableAction(3); + + // Disable music + FsmState roarEnd = fsm.GetState("Roar End")!; + roarEnd.DisableAction(3); + roarEnd.DisableAction(4); + + // Disable death bool + fsm.GetState("End")!.DisableAction(4); + + return; + + void Realign(Vector2 source) + { + centreX.value = source.x; + leftX.value = source.x - 10.5f; + rightX.value = source.x + 10.5f; + swoopY.value = source.y; + maxY.value = source.y + 6; + } + } + + void Update() + { + if (Input.GetKeyDown(SpawnHotkey)) + { + _asset.EnsureLoaded(); + GameObject mossMom = _asset.InstantiateAsset(); + + mossMom.transform.position = HeroController.instance.transform.position + new Vector3(3, 3, 0); + FixMossMother(mossMom); + + mossMom.SetActive(true); + } + } +} diff --git a/AssetHelperTesting/Tests/EnemySpawn.cs b/AssetHelperTesting/Tests/EnemySpawn.cs new file mode 100644 index 0000000..7b62e26 --- /dev/null +++ b/AssetHelperTesting/Tests/EnemySpawn.cs @@ -0,0 +1,62 @@ +using AssetHelperTesting; +using Silksong.AssetHelper.ManagedAssets; +using System; +using UnityEngine; + +/// +/// Test that requests an enemy asset, loads when entering game, spawns when hotkey pressed +/// +public class EnemySpawn : MonoBehaviour +{ + public KeyCode SpawnHotkey { get; set; } + + public static void Prepare(KeyCode spawnHotkey = KeyCode.H) + { + GameObject go = new("Alita Spawner"); + DontDestroyOnLoad(go); + EnemySpawn component = go.AddComponent(); + component.SpawnHotkey = spawnHotkey; + } + + private ManagedAsset _asset; + + void Awake() + { + _asset = ManagedAsset.FromSceneAsset( + sceneName: "Memory_Coral_Tower", + objPath: "Battle Scenes/Battle Scene Chamber 2/Wave 1/Coral Hunter"); + + Events.OnHeroStart += () => _asset.Load(); + } + + // Code lifted from https://github.com/cometcake575/Architect-Silksong/blob/main/Behaviour/Fixers/EnemyFixers.cs + public static void FixAlita(GameObject obj) + { + PlayMakerFSM fsm = obj.LocateMyFSM("Control"); + fsm.FsmVariables.FindFsmBool("Spear Spawner").Value = false; + + float ground = obj.transform.GetPositionY(); + fsm.FsmVariables.FindFsmFloat("Tele Air Y Max").Value = ground + 8; + fsm.FsmVariables.FindFsmFloat("Tele Air Y Min").Value = ground + 2; + fsm.FsmVariables.FindFsmFloat("Tele Ground Y").Value = ground; + + fsm.FsmVariables.FindFsmFloat("Tele X Max").Value = obj.transform.GetPositionX() + 11; + fsm.FsmVariables.FindFsmFloat("Tele X Min").Value = obj.transform.GetPositionX() - 11; + + fsm.FsmVariables.FindFsmGameObject("Aiming Cursor").Value = new GameObject(obj.name + " Aim Cursor"); + } + + void Update() + { + if (Input.GetKeyDown(SpawnHotkey)) + { + _asset.EnsureLoaded(); + GameObject alita = _asset.InstantiateAsset(); + + alita.transform.position = HeroController.instance.transform.position + new Vector3(3, 0, 0); + FixAlita(alita); + + alita.SetActive(true); + } + } +} diff --git a/AssetHelperTesting/Tests/LargeRequest.cs b/AssetHelperTesting/Tests/LargeRequest.cs new file mode 100644 index 0000000..532f902 --- /dev/null +++ b/AssetHelperTesting/Tests/LargeRequest.cs @@ -0,0 +1,74 @@ +using Silksong.AssetHelper.Dev; +using Silksong.AssetHelper.Plugin; +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Windows.Speech; + +namespace AssetHelperTesting.Tests; + +/// +/// Test that makes a large request (taken from existing AssetHelper clients) +/// but does nothing with the assets. +/// +public class LargeRequest : MonoBehaviour +{ + // SceneReq1, NonSceneReq are Architect; SceneReq2 is Pharlooms Glory + public static void Prepare(bool sceneReq1 = true, bool sceneReq2 = true, bool nonSceneReq = true) + { + GameObject go = new("Large Request Owner"); + DontDestroyOnLoad(go); + LargeRequest component = go.AddComponent(); + + MakeRequests(sceneReq1, sceneReq2, nonSceneReq); + AssetRequestAPI.InvokeAfterBundleCreation(() => DebugTools.SerializeAssetRequest()); + } + + private static void MakeRequests(bool sceneReq1, bool sceneReq2, bool nonSceneReq) + { + if (sceneReq1) + { + RequestSceneAssets("scenegroup1"); + } + if (sceneReq2) + { + RequestSceneAssets("scenegroup2"); + } + if (nonSceneReq) + { + RequestNonSceneAssets("nonscenegroup"); + } + } + + private static void RequestSceneAssets(string filename) + { + if (!JsonHelper.TryLoadEmbeddedJson(filename, out Dictionary>? parsed)) + { + AssetHelperTestingPlugin.InstanceLogger.LogWarning($"Failed to parse scene assets for {filename}"); + return; + } + + foreach ((string scene, List assets) in parsed) + { + AssetRequestAPI.RequestSceneAssets(scene, assets); + } + } + + private record RequestedNonSceneAsset(string BundleName, string AssetName, Type Type); + + private static void RequestNonSceneAssets(string filename) + { + if (!JsonHelper.TryLoadEmbeddedJson( + filename, + out List? parsed)) + { + AssetHelperTestingPlugin.InstanceLogger.LogWarning($"Failed to parse non-scene assets for {filename}"); + return; + } + + foreach ((string bundleName, string assetName, Type assetType) in parsed) + { + AssetRequestAPI.RequestNonSceneAsset(bundleName, assetName, assetType); + } + } +} diff --git a/AssetHelperTesting/Tests/MultiGoSpawn.cs b/AssetHelperTesting/Tests/MultiGoSpawn.cs new file mode 100644 index 0000000..1d16c62 --- /dev/null +++ b/AssetHelperTesting/Tests/MultiGoSpawn.cs @@ -0,0 +1,45 @@ +using Silksong.AssetHelper.ManagedAssets; +using Silksong.AssetHelper.Plugin; +using Silksong.UnityHelper.Extensions; +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace AssetHelperTesting.Tests; + +/// +/// Test associated with multiple game objects having the same name. +/// +public class MultiGoSpawn : MonoBehaviour +{ + public KeyCode RootHotkey { get; set; } + + private static ManagedAssetList _groupAsset; + + public static void Prepare(KeyCode rootHotkey = KeyCode.H) + { + GameObject go = new("Group Spawner"); + DontDestroyOnLoad(go); + MultiGoSpawn component = go.AddComponent(); + component.RootHotkey = rootHotkey; + } + + void Awake() + { + _groupAsset = ManagedAssetList.FromSceneAsset(sceneName: "Weave_08", objPath: "Group"); + Events.OnHeroStart += () => _groupAsset.Load(); + } + + void Update() + { + if (!Input.GetKeyDown(RootHotkey)) return; + + _groupAsset.EnsureLoaded(); + + GameObject spawnedGroup = _groupAsset.InstantiateAsset(go => go.FindChild("Inspect Region (1)") != null); + spawnedGroup.transform.position = HeroController.instance.transform.position + new Vector3(5, 0, 0); + spawnedGroup.SetActive(true); + } +} diff --git a/AssetHelperTesting/Tests/SpawnRequestedChild.cs b/AssetHelperTesting/Tests/SpawnRequestedChild.cs new file mode 100644 index 0000000..ad03f92 --- /dev/null +++ b/AssetHelperTesting/Tests/SpawnRequestedChild.cs @@ -0,0 +1,41 @@ +using Silksong.AssetHelper.ManagedAssets; +using Silksong.AssetHelper.Plugin; +using UnityEngine; + +namespace AssetHelperTesting.Tests; + +/// +/// Test spawning a child game object whose ancestor was requested. +/// +public class SpawnRequestedChild : MonoBehaviour +{ + public KeyCode SpawnHotkey { get; set; } + + private static ManagedAsset _asset; + + public static void Prepare(KeyCode spawnHotkey = KeyCode.H) + { + GameObject go = new("Group Spawner"); + DontDestroyOnLoad(go); + SpawnRequestedChild component = go.AddComponent(); + component.SpawnHotkey = spawnHotkey; + } + + void Awake() + { + _asset = ManagedAsset.FromSceneAsset(sceneName: "Memory_Coral_Tower", objPath: "Fish/Pt Exit/BG (2)/Pt Fish Shiny (5)"); + AssetRequestAPI.RequestSceneAsset(sceneName: "Memory_Coral_Tower", assetPath: "Fish/Pt Exit"); + Events.OnHeroStart += () => _asset.Load(); + } + + void Update() + { + if (!Input.GetKeyDown(SpawnHotkey)) return; + + _asset.EnsureLoaded(); + + GameObject spawnedAsset = _asset.InstantiateAsset(); + spawnedAsset.transform.position = HeroController.instance.transform.position + new Vector3(5, 5, 0); + spawnedAsset.SetActive(true); + } +} diff --git a/AssetHelperTesting/Tests/SquirrmTest.cs b/AssetHelperTesting/Tests/SquirrmTest.cs new file mode 100644 index 0000000..5f4fea4 --- /dev/null +++ b/AssetHelperTesting/Tests/SquirrmTest.cs @@ -0,0 +1,46 @@ +using Silksong.AssetHelper.ManagedAssets; +using UnityEngine; + +namespace AssetHelperTesting.Tests; + +/// +/// Loading and instantiating this asset causes concerning warning logs to be emitted - this test +/// is here to support investigation into this issue. +/// +internal class SquirrmTest : MonoBehaviour +{ + public KeyCode LoadHotkey { get; set; } + public KeyCode InstantiateHotkey { get; set; } + + public static void Prepare(KeyCode loadHotkey = KeyCode.G, KeyCode instantiateHotkey = KeyCode.H) + { + GameObject go = new("Squirrm loader"); + DontDestroyOnLoad(go); + SquirrmTest component = go.AddComponent(); + component.LoadHotkey = loadHotkey; + component.InstantiateHotkey = instantiateHotkey; + } + + private ManagedAsset _asset; + + void Awake() + { + _asset = ManagedAsset.FromSceneAsset( + sceneName: "Coral_36", + objPath: "Judge Child (1)"); + } + + void Update() + { + if (Input.GetKeyDown(LoadHotkey)) + { + _asset.Load(); + _asset.Handle.Completed += _ => AssetHelperTestingPlugin.InstanceLogger.LogInfo($"Squirrm loaded"); + } + + if (Input.GetKeyDown(InstantiateHotkey)) + { + GameObject go = _asset.InstantiateAsset(); + } + } +} diff --git a/docs/articles/load.md b/docs/articles/load.md index ef49dc9..4f2625e 100644 --- a/docs/articles/load.md +++ b/docs/articles/load.md @@ -26,7 +26,7 @@ with `yield return managedAsset.Load();` executed before using the asset. If the function loading the asset is not the one using the asset but the asset is expected to be loaded , for instance if the asset is being loaded on enter game, then it may be sensible to use the -@"Silksong.AssetHelper.ManagedAssets.ManagedAssetExtensions.EnsureLoaded``1(Silksong.AssetHelper.ManagedAssets.ManagedAsset{``0})" +@"Silksong.AssetHelper.ManagedAssets.ManagedAssetExtensions.EnsureLoaded``1(Silksong.AssetHelper.ManagedAssets.ManagedAssetBase{``0})" method to ensure the asset is loaded before using it. ## Unloading assets