From 784b1adb60fbe23ccf40288e8e38d5d93933709f Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 11 Jul 2024 21:12:07 -0400 Subject: [PATCH 01/14] it doesnt work --- .../Builder/Props/ProjectionBuilder.cs | 20 +++--- NewHorizons/Utility/Files/ImageUtilities.cs | 5 ++ .../Files/SlideReelAsyncImageLoader.cs | 64 ++++++++++++++++--- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index a8e42c0f..2d25948d 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -513,7 +513,7 @@ namespace NewHorizons.Builder.Props { NHLogger.LogVerbose($"The atlas cache for slide reel containing [{slides.FirstOrDefault(x => !string.IsNullOrEmpty(x.imagePath))?.imagePath}] is {atlasKey}"); // Load the atlas texture used to draw onto the physical slide reel object - atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png"))); + atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png"), false)); } for (int i = 0; i < slides.Length; i++) @@ -527,11 +527,12 @@ namespace NewHorizons.Builder.Props if (useInvertedCache && cacheExists) { // Load the inverted images used when displaying slide reels to a screen - invertedImageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/inverted_blank_slide_reel.png"))); + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/inverted_blank_slide_reel.png"), false)); } else { - imageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png"))); + // Used to then make cached stuff + imageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png"), useInvertedCache)); } } else @@ -539,11 +540,11 @@ namespace NewHorizons.Builder.Props if (useInvertedCache && cacheExists) { // Load the inverted images used when displaying slide reels to a screen - invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath), false)); } else { - imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); + imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath), useInvertedCache)); } } @@ -556,16 +557,16 @@ namespace NewHorizons.Builder.Props { if (useAtlasCache) { - atlasImageLoader.Start(false); + atlasImageLoader.Start(false, false); } // When using the inverted cache we never need the regular images if (useInvertedCache) { - invertedImageLoader.Start(true); + invertedImageLoader.Start(true, false); } else { - imageLoader.Start(true); + imageLoader.Start(true, false); } return (invertedImageLoader, atlasImageLoader, imageLoader); @@ -573,7 +574,8 @@ namespace NewHorizons.Builder.Props else { // Will be slow and create the cache if needed - imageLoader.Start(true); + // Will run sequentially to ensure we don't run out of memory + imageLoader.Start(true, true); return (null, null, imageLoader); } diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index c6ef49da..5d0c4d2f 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -72,6 +72,11 @@ namespace NewHorizons.Utility.Files { var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, filename); var key = GetKey(path); + DeleteTexture(key, texture); + } + + public static void DeleteTexture(string key, Texture2D texture) + { if (_textureCache.ContainsKey(key)) { if (_textureCache[key] == texture) diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs index 58b9db92..8897c927 100644 --- a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -15,7 +15,7 @@ namespace NewHorizons.Utility.Files; /// public class SlideReelAsyncImageLoader { - public List<(int index, string path)> PathsToLoad { get; private set; } = new(); + public List<(int index, string path, bool deleteImmediately)> PathsToLoad { get; private set; } = new(); public class ImageLoadedEvent : UnityEvent { } public ImageLoadedEvent imageLoadedEvent = new(); @@ -31,7 +31,7 @@ public class SlideReelAsyncImageLoader private bool _started; private bool _clamp; - public void Start(bool clamp) + public void Start(bool clamp, bool sequential) { if (_started) return; @@ -46,7 +46,7 @@ public class SlideReelAsyncImageLoader NHLogger.LogVerbose("Loading new slide reel"); imageLoadedEvent.AddListener(OnImageLoaded); - SingletonSlideReelAsyncImageLoader.Instance.Load(this); + SingletonSlideReelAsyncImageLoader.Instance.Load(this, sequential); } private void OnImageLoaded(Texture texture, int index, string originalPath) @@ -60,7 +60,17 @@ public class SlideReelAsyncImageLoader } } - private IEnumerator DownloadTexture(string url, int index) + private IEnumerator DownloadTextures() + { + foreach (var (index, path, deleteImmediately) in PathsToLoad) + { + NHLogger.LogVerbose($"Loaded slide reel {index} of {PathsToLoad.Count}"); + + yield return DownloadTexture(path, index, deleteImmediately); + } + } + + private IEnumerator DownloadTexture(string url, int index, bool deleteImmediately) { var key = ImageUtilities.GetKey(url); if (ImageUtilities.CheckCachedTexture(key, out var existingTexture)) @@ -107,6 +117,12 @@ public class SlideReelAsyncImageLoader var time = DateTime.Now; imageLoadedEvent?.Invoke(texture, index, url); + + // For when its an image loaded only to create a new cacheable image + if (deleteImmediately) + { + ImageUtilities.DeleteTexture(key, texture); + } NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms"); } } @@ -115,6 +131,10 @@ public class SlideReelAsyncImageLoader { public static SingletonSlideReelAsyncImageLoader Instance { get; private set; } + private Queue _loaders = new(); + + private bool _isLoading; + public void Awake() { Instance = this; @@ -124,20 +144,48 @@ public class SlideReelAsyncImageLoader private void OnSceneUnloaded(Scene _) { StopAllCoroutines(); + _loaders.Clear(); + _isLoading = false; } - public void Load(SlideReelAsyncImageLoader loader) + public void Load(SlideReelAsyncImageLoader loader, bool sequential) { // Delay at least one frame to let things subscribe to the event before it fires Delay.FireOnNextUpdate(() => { - foreach (var (index, path) in loader.PathsToLoad) + if (sequential) { - NHLogger.LogVerbose($"Loaded slide reel {index} of {loader.PathsToLoad.Count}"); + // Sequential + _loaders.Enqueue(loader); + if (!_isLoading) + { + StartCoroutine(LoadAllSequential()); + } + } + else + { + foreach (var (index, path, deleteImmediately) in loader.PathsToLoad) + { + NHLogger.LogVerbose($"Loaded slide reel {index} of {loader.PathsToLoad.Count}"); - StartCoroutine(loader.DownloadTexture(path, index)); + StartCoroutine(loader.DownloadTexture(path, index, deleteImmediately)); + } } }); } + + private IEnumerator LoadAllSequential() + { + NHLogger.Log("Loading slide reels"); + _isLoading = true; + while (_loaders.Count > 0) + { + var loader = _loaders.Dequeue(); + yield return loader.DownloadTextures(); + NHLogger.Log($"Finished a slide reel, {_loaders.Count} left"); + } + _isLoading = false; + NHLogger.Log("Done loading slide reels"); + } } } \ No newline at end of file From 63ae94a9a3c1e53b58a6759f178cb6798c22d3d3 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Sun, 14 Jul 2024 16:43:14 -0400 Subject: [PATCH 02/14] Update manifest.json --- NewHorizons/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 14962bcf..399bc919 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -9,5 +9,5 @@ "dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], "conflicts": [ "PacificEngine.OW_CommonResources" ], "pathsToPreserve": [ "planets", "systems", "translations" ], - "donateLink": "https://www.patreon.com/ownh" + "donateLink": "https://www.patreon.com/xen42" } From 18f35df554c4eabad5f5ccef93ba577bf3855312 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Sun, 14 Jul 2024 18:47:50 -0400 Subject: [PATCH 03/14] Sequential loading works, is under new option, also redid default config to have tooltips and stuff --- .../Builder/Props/ProjectionBuilder.cs | 25 ++++++++--- NewHorizons/Main.cs | 40 +++++++++-------- .../Files/SlideReelAsyncImageLoader.cs | 40 +++++++++++------ NewHorizons/default-config.json | 43 ++++++++++++++++--- 4 files changed, 103 insertions(+), 45 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 2d25948d..597ac9a8 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -45,6 +45,8 @@ namespace NewHorizons.Builder.Props private static bool _isInit; + public static bool CacheExists(IModBehaviour mod) => Directory.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER)); + internal static void InitPrefabs() { if (_isInit) return; @@ -151,6 +153,9 @@ namespace NewHorizons.Builder.Props var (invImageLoader, atlasImageLoader, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, true); + // If the cache doesn't exist it will be created here, slide reels only use the base image loader for cache creation so delete the images after to free memory + imageLoader.deleteTexturesWhenDone = !CacheExists(mod); + var key = GetUniqueSlideReelID(mod, info.slides); if (invImageLoader != null && atlasImageLoader != null) @@ -220,7 +225,6 @@ namespace NewHorizons.Builder.Props }); } - // Else when you put them down you can't pick them back up slideReelObj.GetComponent()._physicsRemoved = false; @@ -361,6 +365,9 @@ namespace NewHorizons.Builder.Props var time = DateTime.Now; slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath); NHLogger.LogVerbose($"Slide reel invert time {(DateTime.Now - time).TotalMilliseconds}ms"); + + // Autoprojector only uses the inverted images so the original can be destroyed now + ImageUtilities.DeleteTexture(ImageUtilities.GetKey(originalPath), tex); }); } @@ -505,7 +512,7 @@ namespace NewHorizons.Builder.Props var atlasKey = GetUniqueSlideReelID(mod, slides); - var cacheExists = Directory.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER)); + var cacheExists = CacheExists(mod); NHLogger.Log($"Does cache exist for slide reels? {cacheExists}"); @@ -513,7 +520,7 @@ namespace NewHorizons.Builder.Props { NHLogger.LogVerbose($"The atlas cache for slide reel containing [{slides.FirstOrDefault(x => !string.IsNullOrEmpty(x.imagePath))?.imagePath}] is {atlasKey}"); // Load the atlas texture used to draw onto the physical slide reel object - atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png"), false)); + atlasImageLoader.PathsToLoad.Add((0, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ATLAS_SLIDE_CACHE_FOLDER, $"{atlasKey}.png"))); } for (int i = 0; i < slides.Length; i++) @@ -527,12 +534,12 @@ namespace NewHorizons.Builder.Props if (useInvertedCache && cacheExists) { // Load the inverted images used when displaying slide reels to a screen - invertedImageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/inverted_blank_slide_reel.png"), false)); + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/inverted_blank_slide_reel.png"))); } else { // Used to then make cached stuff - imageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png"), useInvertedCache)); + imageLoader.PathsToLoad.Add((i, Path.Combine(Instance.ModHelper.Manifest.ModFolderPath, "Assets/textures/blank_slide_reel.png"))); } } else @@ -540,11 +547,11 @@ namespace NewHorizons.Builder.Props if (useInvertedCache && cacheExists) { // Load the inverted images used when displaying slide reels to a screen - invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath), false)); + invertedImageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, INVERTED_SLIDE_CACHE_FOLDER, slideInfo.imagePath))); } else { - imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath), useInvertedCache)); + imageLoader.PathsToLoad.Add((i, Path.Combine(mod.ModHelper.Manifest.ModFolderPath, slideInfo.imagePath))); } } @@ -555,6 +562,8 @@ namespace NewHorizons.Builder.Props if (cacheExists) { + NHLogger.Log("Loading slide reels from cache"); + if (useAtlasCache) { atlasImageLoader.Start(false, false); @@ -573,6 +582,8 @@ namespace NewHorizons.Builder.Props } else { + NHLogger.Log("Generating slide reel cache"); + // Will be slow and create the cache if needed // Will run sequentially to ensure we don't run out of memory imageLoader.Start(true, true); diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 4df680c9..85c26823 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -46,9 +46,10 @@ namespace NewHorizons // Settings public static bool Debug { get; private set; } public static bool VerboseLogs { get; private set; } - private static bool _useCustomTitleScreen; + public static bool SequentialPreCaching { get; private set; } + public static bool CustomTitleScreen { get; private set; } + public static string DefaultSystemOverride { get; private set; } private static bool _wasConfigured = false; - private static string _defaultSystemOverride; public static Dictionary SystemDict = new(); public static Dictionary> BodyDict = new(); @@ -59,7 +60,7 @@ namespace NewHorizons public static bool IsSystemReady { get; private set; } - public string DefaultStarSystem => SystemDict.ContainsKey(_defaultSystemOverride) ? _defaultSystemOverride : _defaultStarSystem; + public string DefaultStarSystem => SystemDict.ContainsKey(DefaultSystemOverride) ? DefaultSystemOverride : _defaultStarSystem; public string CurrentStarSystem { get @@ -130,8 +131,9 @@ namespace NewHorizons var currentScene = SceneManager.GetActiveScene().name; - Debug = config.GetSettingsValue("Debug"); - VerboseLogs = config.GetSettingsValue("Verbose Logs"); + Debug = config.GetSettingsValue(nameof(Debug)); + VerboseLogs = config.GetSettingsValue(nameof(VerboseLogs)); + SequentialPreCaching = config.GetSettingsValue(nameof(SequentialPreCaching)); if (currentScene == "SolarSystem") { @@ -143,19 +145,19 @@ namespace NewHorizons else if (Debug) NHLogger.UpdateLogLevel(NHLogger.LogType.Log); else NHLogger.UpdateLogLevel(NHLogger.LogType.Error); - var oldDefaultSystemOverride = _defaultSystemOverride; - _defaultSystemOverride = config.GetSettingsValue("Default System Override"); - if (oldDefaultSystemOverride != _defaultSystemOverride) + var oldDefaultSystemOverride = DefaultSystemOverride; + DefaultSystemOverride = config.GetSettingsValue(nameof(DefaultSystemOverride)); + if (oldDefaultSystemOverride != DefaultSystemOverride) { ResetCurrentStarSystem(); - NHLogger.Log($"Changed default star system override to {_defaultSystemOverride}"); + NHLogger.Log($"Changed default star system override to {DefaultSystemOverride}"); } - var wasUsingCustomTitleScreen = _useCustomTitleScreen; - _useCustomTitleScreen = config.GetSettingsValue("Custom title screen"); + var wasUsingCustomTitleScreen = CustomTitleScreen; + CustomTitleScreen = config.GetSettingsValue(nameof(CustomTitleScreen)); // Reload the title screen if this was updated on it // Don't reload if we haven't configured yet (called on game start) - if (wasUsingCustomTitleScreen != _useCustomTitleScreen && SceneManager.GetActiveScene().name == "TitleScreen" && _wasConfigured) + if (wasUsingCustomTitleScreen != CustomTitleScreen && SceneManager.GetActiveScene().name == "TitleScreen" && _wasConfigured) { NHLogger.LogVerbose("Reloading"); SceneManager.LoadScene("TitleScreen", LoadSceneMode.Single); @@ -428,7 +430,7 @@ namespace NewHorizons IsChangingStarSystem = false; - if (isTitleScreen && _useCustomTitleScreen) + if (isTitleScreen && CustomTitleScreen) { try { @@ -1037,16 +1039,16 @@ namespace NewHorizons private void ResetCurrentStarSystem() { - if (SystemDict.ContainsKey(_defaultSystemOverride)) + if (SystemDict.ContainsKey(DefaultSystemOverride)) { - CurrentStarSystem = _defaultSystemOverride; + CurrentStarSystem = DefaultSystemOverride; // #738 - Sometimes the override will not support spawning regularly, so always warp in if possible - if (SystemDict[_defaultSystemOverride].Config.Vessel?.spawnOnVessel == true) + if (SystemDict[DefaultSystemOverride].Config.Vessel?.spawnOnVessel == true) { IsWarpingFromVessel = true; } - else if (BodyDict.TryGetValue(_defaultSystemOverride, out var bodies) && bodies.Any(x => x.Config?.Spawn?.shipSpawn != null)) + else if (BodyDict.TryGetValue(DefaultSystemOverride, out var bodies) && bodies.Any(x => x.Config?.Spawn?.shipSpawn != null)) { IsWarpingFromShip = true; } @@ -1058,9 +1060,9 @@ namespace NewHorizons else { // Ignore first load because it doesn't even know what systems we have - if (!_firstLoad && !string.IsNullOrEmpty(_defaultSystemOverride)) + if (!_firstLoad && !string.IsNullOrEmpty(DefaultSystemOverride)) { - NHLogger.LogError($"The given default system override {_defaultSystemOverride} is invalid - no system exists with that name"); + NHLogger.LogError($"The given default system override {DefaultSystemOverride} is invalid - no system exists with that name"); } CurrentStarSystem = _defaultStarSystem; diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs index 8897c927..46b15ec9 100644 --- a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -3,6 +3,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Security.Policy; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; @@ -15,7 +16,9 @@ namespace NewHorizons.Utility.Files; /// public class SlideReelAsyncImageLoader { - public List<(int index, string path, bool deleteImmediately)> PathsToLoad { get; private set; } = new(); + public List<(int index, string path)> PathsToLoad { get; private set; } = new(); + + private Dictionary _loadedTextures = new(); public class ImageLoadedEvent : UnityEvent { } public ImageLoadedEvent imageLoadedEvent = new(); @@ -23,6 +26,8 @@ public class SlideReelAsyncImageLoader public bool FinishedLoading { get; private set; } private int _loadedCount = 0; + public bool deleteTexturesWhenDone; + // TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, // and make use of that for at least for projector stuff (require player to be in the same sector as the slides // for them to start loading, and unload when the player leaves) @@ -53,24 +58,40 @@ public class SlideReelAsyncImageLoader { _loadedCount++; + var key = ImageUtilities.GetKey(originalPath); + _loadedTextures[key] = texture as Texture2D; + if (_loadedCount >= PathsToLoad.Count) { NHLogger.LogVerbose($"Finished loading all textures for a slide reel (one was {PathsToLoad.FirstOrDefault()}"); FinishedLoading = true; + + if (deleteTexturesWhenDone) + { + DeleteLoadedImages(); + } + } + } + + private void DeleteLoadedImages() + { + foreach (var (key, texture) in _loadedTextures) + { + ImageUtilities.DeleteTexture(key, texture); } } private IEnumerator DownloadTextures() { - foreach (var (index, path, deleteImmediately) in PathsToLoad) + foreach (var (index, path) in PathsToLoad) { NHLogger.LogVerbose($"Loaded slide reel {index} of {PathsToLoad.Count}"); - yield return DownloadTexture(path, index, deleteImmediately); + yield return DownloadTexture(path, index); } } - private IEnumerator DownloadTexture(string url, int index, bool deleteImmediately) + private IEnumerator DownloadTexture(string url, int index) { var key = ImageUtilities.GetKey(url); if (ImageUtilities.CheckCachedTexture(key, out var existingTexture)) @@ -118,11 +139,6 @@ public class SlideReelAsyncImageLoader var time = DateTime.Now; imageLoadedEvent?.Invoke(texture, index, url); - // For when its an image loaded only to create a new cacheable image - if (deleteImmediately) - { - ImageUtilities.DeleteTexture(key, texture); - } NHLogger.LogVerbose($"Slide reel event took: {(DateTime.Now - time).TotalMilliseconds}ms"); } } @@ -153,7 +169,7 @@ public class SlideReelAsyncImageLoader // Delay at least one frame to let things subscribe to the event before it fires Delay.FireOnNextUpdate(() => { - if (sequential) + if (sequential && Main.SequentialPreCaching) { // Sequential _loaders.Enqueue(loader); @@ -164,11 +180,11 @@ public class SlideReelAsyncImageLoader } else { - foreach (var (index, path, deleteImmediately) in loader.PathsToLoad) + foreach (var (index, path) in loader.PathsToLoad) { NHLogger.LogVerbose($"Loaded slide reel {index} of {loader.PathsToLoad.Count}"); - StartCoroutine(loader.DownloadTexture(path, index, deleteImmediately)); + StartCoroutine(loader.DownloadTexture(path, index)); } } }); diff --git a/NewHorizons/default-config.json b/NewHorizons/default-config.json index 146eb9fe..e4b87a61 100644 --- a/NewHorizons/default-config.json +++ b/NewHorizons/default-config.json @@ -1,9 +1,38 @@ { - "enabled": true, - "settings": { - "Debug": false, - "Custom title screen": true, - "Default System Override": "", - "Verbose Logs": false - } + "enabled": true, + "settings": { + "CustomTitleScreen": { + "title": "Custom Title Screen", + "type": "toggle", + "value": true, + "tooltip": "Displays planets from installed mods on the title screen." + }, + "DebugSeparator": { + "type": "separator" + }, + "Debug": { + "title": "Debug", + "type": "toggle", + "value": false, + "tooltip": "Enables the debug raycast, visible quantum object colliders, and debug options menu." + }, + "VerboseLogs": { + "title": "Verbose Logs", + "type": "toggle", + "value": false, + "tooltip": "Makes logs much more detailed. Useful when debugging." + }, + "SequentialPreCaching": { + "title": "Sequential Pre-caching", + "type": "toggle", + "value": false, + "tooltip": "This is a debug option intended for mod creators. Prevents running out of memory when computing slide reel caches, but is slower and will cause in-game lag." + }, + "DefaultSystemOverride": { + "title": "Default System Override", + "type": "text", + "value": "", + "tooltip": "Forces the player to spawn in this system after death. Must match the unique name of a system, eg, xen.NewHorizonsExamples" + } + } } \ No newline at end of file From 63cb8e6f5d88eafff2cd1a08e688b48c619b7799 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Sun, 14 Jul 2024 20:48:56 -0400 Subject: [PATCH 04/14] Use deleteTexturesWhenDone on autoprojectors, add comments to make it clearer what this does --- NewHorizons/Builder/Props/ProjectionBuilder.cs | 7 ++++--- NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Builder/Props/ProjectionBuilder.cs b/NewHorizons/Builder/Props/ProjectionBuilder.cs index 597ac9a8..7a55d891 100644 --- a/NewHorizons/Builder/Props/ProjectionBuilder.cs +++ b/NewHorizons/Builder/Props/ProjectionBuilder.cs @@ -349,6 +349,10 @@ namespace NewHorizons.Builder.Props slideCollection.streamingAssetIdentifier = string.Empty; // NREs if null var (invImageLoader, _, imageLoader) = StartAsyncLoader(mod, info.slides, ref slideCollection, true, false); + + // Autoprojector only uses the inverted images so the original can be destroyed if they are loaded (when creating the cached inverted images) + imageLoader.deleteTexturesWhenDone = true; + if (invImageLoader != null) { // Loaded directly from cache @@ -365,9 +369,6 @@ namespace NewHorizons.Builder.Props var time = DateTime.Now; slideCollection.slides[index]._image = ImageUtilities.InvertSlideReel(mod, tex, originalPath); NHLogger.LogVerbose($"Slide reel invert time {(DateTime.Now - time).TotalMilliseconds}ms"); - - // Autoprojector only uses the inverted images so the original can be destroyed now - ImageUtilities.DeleteTexture(ImageUtilities.GetKey(originalPath), tex); }); } diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs index 46b15ec9..51123183 100644 --- a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -26,7 +26,13 @@ public class SlideReelAsyncImageLoader public bool FinishedLoading { get; private set; } private int _loadedCount = 0; - public bool deleteTexturesWhenDone; + /// + /// If we are loading images where: + /// 1) The slide reel cache does not exist + /// 2) The loader would only use the images to make the cache + /// Then we want to delete them immediately after loading, in order to save memory + /// + public bool deleteTexturesWhenDone = false; // TODO: set up an optional “StartLoading” and “StartUnloading” condition on AsyncTextureLoader, // and make use of that for at least for projector stuff (require player to be in the same sector as the slides From a564afcf0c5a31e80460223dd1f8d694b858f8c1 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Sun, 14 Jul 2024 21:04:31 -0400 Subject: [PATCH 05/14] Ensure inverted slide reels have the same key regardless of if they're from the cache folder or not --- NewHorizons/Utility/Files/ImageUtilities.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/NewHorizons/Utility/Files/ImageUtilities.cs b/NewHorizons/Utility/Files/ImageUtilities.cs index 5d0c4d2f..3f9d8497 100644 --- a/NewHorizons/Utility/Files/ImageUtilities.cs +++ b/NewHorizons/Utility/Files/ImageUtilities.cs @@ -108,6 +108,16 @@ namespace NewHorizons.Utility.Files public static Texture2D InvertSlideReel(IModBehaviour mod, Texture2D texture, string originalPath) { var key = $"{texture.name} > invert"; + var cachedPath = ""; + + // If we're going to end up caching the texture we must make sure it will end up using the same key + // Not sure why we check if the originalPath is null but it did that before so + if (!string.IsNullOrEmpty(originalPath)) + { + cachedPath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.INVERTED_SLIDE_CACHE_FOLDER, originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, "")); + key = GetKey(cachedPath); + } + if (_textureCache.TryGetValue(key, out var existingTexture)) return (Texture2D)existingTexture; var pixels = texture.GetPixels(); @@ -143,12 +153,11 @@ namespace NewHorizons.Utility.Files // Since doing this is expensive we cache the results to the disk // Preloading cached values is done in ProjectionBuilder - if (!string.IsNullOrEmpty(originalPath)) + if (!string.IsNullOrEmpty(cachedPath)) { - var path = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, ProjectionBuilder.INVERTED_SLIDE_CACHE_FOLDER, originalPath.Replace(mod.ModHelper.Manifest.ModFolderPath, "")); - NHLogger.LogVerbose($"Caching inverted image to {path}"); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - File.WriteAllBytes(path, newTexture.EncodeToPNG()); + NHLogger.LogVerbose($"Caching inverted image to {cachedPath}"); + Directory.CreateDirectory(Path.GetDirectoryName(cachedPath)); + File.WriteAllBytes(cachedPath, newTexture.EncodeToPNG()); } return newTexture; From a8c4641743bd5053814bc45223c5864fd3864094 Mon Sep 17 00:00:00 2001 From: Magnus Date: Sun, 4 Aug 2024 13:32:15 -0700 Subject: [PATCH 06/14] Fix documentation error and add log for future debuging --- NewHorizons/NewHorizonsApi.cs | 8 +++++--- docs/src/content/docs/guides/extending-configs.md | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 6a165fa2..dea9f8eb 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -143,9 +143,11 @@ namespace NewHorizons public object QueryBody(Type outType, string bodyName, string jsonPath) { var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == bodyName); - return planet == null - ? null - : QueryJson(outType, Path.Combine(planet.Mod.ModHelper.Manifest.ModFolderPath, planet.RelativePath), jsonPath); + if (planet == null){ + NHLogger.LogError($"Could not find planet with body name {bodyName}.") + return null; + } + return QueryJson(outType, Path.Combine(planet.Mod.ModHelper.Manifest.ModFolderPath, planet.RelativePath), jsonPath); } public T QueryBody(string bodyName, string jsonPath) diff --git a/docs/src/content/docs/guides/extending-configs.md b/docs/src/content/docs/guides/extending-configs.md index 0d3ee903..63b39fc5 100644 --- a/docs/src/content/docs/guides/extending-configs.md +++ b/docs/src/content/docs/guides/extending-configs.md @@ -49,7 +49,7 @@ Then, use the `QueryBody` method: var api = ModHelper.Interactions.TryGetModApi("xen.NewHorizons"); api.GetBodyLoadedEvent().AddListener((name) => { ModHelper.Console.WriteLine($"Body: {name} Loaded!"); - var data = api.QueryBody("$.extras.myCoolExtensionData", name); + var data = api.QueryBody(name, "$.extras.myCoolExtensionData"); // Makes sure the module is not null if (data != null) { ModHelper.Console.WriteLine($"myCoolExtensionProperty for {name} is {data.myCoolExtensionProperty}!"); From 1fa4664b348eaf2bb1006e87ea74d3d202e8aeed Mon Sep 17 00:00:00 2001 From: Magnus Date: Mon, 5 Aug 2024 12:13:51 -0700 Subject: [PATCH 07/14] Added missing semicolon --- NewHorizons/NewHorizonsApi.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index dea9f8eb..40362fb2 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -144,7 +144,7 @@ namespace NewHorizons { var planet = Main.BodyDict[Main.Instance.CurrentStarSystem].Find((b) => b.Config.name == bodyName); if (planet == null){ - NHLogger.LogError($"Could not find planet with body name {bodyName}.") + NHLogger.LogError($"Could not find planet with body name {bodyName}."); return null; } return QueryJson(outType, Path.Combine(planet.Mod.ModHelper.Manifest.ModFolderPath, planet.RelativePath), jsonPath); @@ -163,6 +163,7 @@ namespace NewHorizons public object QuerySystem(Type outType, string jsonPath) { var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; + NHLogger.Log("System Relative Path: " + system.RelativePath); return system == null ? null : QueryJson(outType, Path.Combine(system.Mod.ModHelper.Manifest.ModFolderPath, system.RelativePath), jsonPath); From 5fae85f209584453e38fd8e611e33f05169e0591 Mon Sep 17 00:00:00 2001 From: Magnus Date: Mon, 5 Aug 2024 12:14:20 -0700 Subject: [PATCH 08/14] Set the SolarSystem's relative path if changed by a mod. --- NewHorizons/Main.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 4df680c9..5a5060d0 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -695,6 +695,9 @@ namespace NewHorizons if (SystemDict[starSystemName].Config.GlobalMusic == null && SystemDict[starSystemName].Config.Skybox == null) SystemDict[starSystemName].Mod = mod; SystemDict[starSystemName].Config.Merge(starSystemConfig); + // If a mod contains a change to the default system, set the relative path. + // Warning: If multiple systems make changes to the default system, only the relativePath will be set to the last mod loaded. + SystemDict[starSystemName].RelativePath = relativePath; } else { From 8614d082ffcb4b496b5acc1bb4c23dc6b08b662c Mon Sep 17 00:00:00 2001 From: xen-42 Date: Mon, 5 Aug 2024 15:15:34 -0400 Subject: [PATCH 09/14] Update Main.cs --- NewHorizons/Main.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 4df680c9..5b71202e 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -694,6 +694,8 @@ namespace NewHorizons { if (SystemDict[starSystemName].Config.GlobalMusic == null && SystemDict[starSystemName].Config.Skybox == null) SystemDict[starSystemName].Mod = mod; + if (SystemDict[starSystemName].Config.extras == null) + SystemDict[starSystemName].RelativePath = relativePath; SystemDict[starSystemName].Config.Merge(starSystemConfig); } else From a6037db5628b9176c0c36ea479e3608e01561a2b Mon Sep 17 00:00:00 2001 From: xen-42 Date: Mon, 5 Aug 2024 15:35:37 -0400 Subject: [PATCH 10/14] Only change relative path for base system, else log a warning --- NewHorizons/Main.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 79606f29..b80cf1e3 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -694,14 +694,26 @@ namespace NewHorizons if (SystemDict.ContainsKey(starSystemName)) { + // Both changing the Mod and RelativePath are weird and will likely cause incompat issues if two mods both affected the same system + // It's mostly just to fix up the config compared to the default one NH makes for the base StarSystem + if (SystemDict[starSystemName].Config.GlobalMusic == null && SystemDict[starSystemName].Config.Skybox == null) + { SystemDict[starSystemName].Mod = mod; - if (SystemDict[starSystemName].Config.extras == null) - SystemDict[starSystemName].RelativePath = relativePath; - SystemDict[starSystemName].Config.Merge(starSystemConfig); + } + // If a mod contains a change to the default system, set the relative path. // Warning: If multiple systems make changes to the default system, only the relativePath will be set to the last mod loaded. - SystemDict[starSystemName].RelativePath = relativePath; + if (string.IsNullOrEmpty(SystemDict[starSystemName].RelativePath)) + { + SystemDict[starSystemName].RelativePath = relativePath; + } + else + { + NHLogger.LogWarning($"Two (or more) mods are making system changes to {starSystemName} which may result in errors"); + } + + SystemDict[starSystemName].Config.Merge(starSystemConfig); } else { From b82cf5aa75b2076a1e8cfadddce9bd78494f03c9 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Wed, 7 Aug 2024 14:21:28 -0400 Subject: [PATCH 11/14] Fix NH breaking ship hud marker with visible stranger installed --- NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs | 6 +++--- NewHorizons/manifest.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs b/NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs index c99ae6fe..df51f4bb 100644 --- a/NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs +++ b/NewHorizons/Patches/HUDPatches/ShipHUDMarkerPatches.cs @@ -14,11 +14,11 @@ namespace NewHorizons.Patches.HUDPatches bool insideEYE = Locator.GetEyeStateManager() != null && Locator.GetEyeStateManager().IsInsideTheEye(); bool insideQM = __instance._quantumMoon != null && (__instance._quantumMoon.IsPlayerInside() || __instance._quantumMoon.IsShipInside()); bool insideRW = Locator.GetRingWorldController() != null && Locator.GetRingWorldController().isPlayerInside; - bool insideIP = Locator.GetCloakFieldController() != null && Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak; - bool insideCloak = CloakSectorController.isPlayerInside == CloakSectorController.isShipInside; + bool insideIPMatches = Locator.GetCloakFieldController() == null || Locator.GetCloakFieldController().isPlayerInsideCloak == Locator.GetCloakFieldController().isShipInsideCloak; + bool insideCloakMatches = CloakSectorController.isPlayerInside == CloakSectorController.isShipInside; bool sameInterference = InterferenceHandler.IsPlayerSameAsShip(); - __instance._isVisible = !insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && !__instance._shipDestroyed && !__instance._playerInShip && PlayerState.HasPlayerEnteredShip() && __instance._isWearingHelmet && insideIP && insideCloak && sameInterference; + __instance._isVisible = !insideEYE && !insideQM && !insideRW && !__instance._translatorEquipped && !__instance._inConversation && !__instance._shipDestroyed && !__instance._playerInShip && PlayerState.HasPlayerEnteredShip() && __instance._isWearingHelmet && insideIPMatches && insideCloakMatches && sameInterference; if (__instance._canvasMarker != null) __instance._canvasMarker.SetVisibility(__instance._isVisible); diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 399bc919..b9be6583 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -5,7 +5,7 @@ "name": "New Horizons", "uniqueName": "xen.NewHorizons", "version": "1.22.3", - "owmlVersion": "2.12.1", + "owmlVersion": "2.12.2", "dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], "conflicts": [ "PacificEngine.OW_CommonResources" ], "pathsToPreserve": [ "planets", "systems", "translations" ], From fb00001e36f9c89373e04a1c4b5b1b1887e0b655 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Wed, 7 Aug 2024 14:22:43 -0400 Subject: [PATCH 12/14] Remove debug log --- NewHorizons/NewHorizonsApi.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index 40362fb2..6c46ac3c 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -163,7 +163,6 @@ namespace NewHorizons public object QuerySystem(Type outType, string jsonPath) { var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; - NHLogger.Log("System Relative Path: " + system.RelativePath); return system == null ? null : QueryJson(outType, Path.Combine(system.Mod.ModHelper.Manifest.ModFolderPath, system.RelativePath), jsonPath); From c1c9f60ef943547e6476f2ccb93100e0a7fd45ba Mon Sep 17 00:00:00 2001 From: xen-42 Date: Wed, 7 Aug 2024 14:23:54 -0400 Subject: [PATCH 13/14] bumped the wrong version --- NewHorizons/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index b9be6583..cdf7a3c0 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -4,8 +4,8 @@ "author": "xen, Bwc9876, JohnCorby, MegaPiggy, Clay, Trifid, and friends", "name": "New Horizons", "uniqueName": "xen.NewHorizons", - "version": "1.22.3", - "owmlVersion": "2.12.2", + "version": "1.22.4", + "owmlVersion": "2.12.1", "dependencies": [ "JohnCorby.VanillaFix", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], "conflicts": [ "PacificEngine.OW_CommonResources" ], "pathsToPreserve": [ "planets", "systems", "translations" ], From 2b3b7e277eb080fa9033d5b8d0b3faf327a44284 Mon Sep 17 00:00:00 2001 From: xen-42 Date: Thu, 8 Aug 2024 11:50:21 -0400 Subject: [PATCH 14/14] Update SlideReelAsyncImageLoader.cs --- NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs index 51123183..7674ccdc 100644 --- a/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs +++ b/NewHorizons/Utility/Files/SlideReelAsyncImageLoader.cs @@ -3,7 +3,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Security.Policy; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking;