From 61102e25ebec01b9ab69b2b09d0dff8ca877c5aa Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Feb 2024 13:37:33 -0500 Subject: [PATCH 01/10] Fixes a bug where the ship wasn't removed from volumes when spawning --- NewHorizons/Handlers/PlayerSpawnHandler.cs | 14 ++++++++++++++ NewHorizons/manifest.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index 2b32cd691..4b665cbb4 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -77,6 +77,20 @@ public static void SpawnShip() } SpawnBody(ship.GetAttachedOWRigidbody(), SpawnPointBuilder.ShipSpawn, pos); + + // Bug affecting mods with massive stars (8600m+ radius) + // TH has an orbital radius of 8600m, meaning the base game ship spawn ends up inside the star + // This places the ship into the star's fluid volumes (destruction volume and atmosphere) + // When the ship is teleported out, it doesn't update it's detected fluid volumes and gets affected by drag forever + // Can fix by turning the volumes off and on again + foreach (var volume in ship.GetComponentInChildren()._activeVolumes) + { + if (volume.gameObject.activeInHierarchy) + { + volume.gameObject.SetActive(false); + volume.gameObject.SetActive(true); + } + } } } else if (Main.Instance.CurrentStarSystem != "SolarSystem" && !Main.Instance.IsWarpingFromShip) diff --git a/NewHorizons/manifest.json b/NewHorizons/manifest.json index 30dd04eda..c75c1b1c1 100644 --- a/NewHorizons/manifest.json +++ b/NewHorizons/manifest.json @@ -4,7 +4,7 @@ "author": "xen, Bwc9876, clay, MegaPiggy, John, Trifid, Hawkbar, Book", "name": "New Horizons", "uniqueName": "xen.NewHorizons", - "version": "1.18.3", + "version": "1.18.4", "owmlVersion": "2.9.8", "dependencies": [ "JohnCorby.VanillaFix", "_nebula.MenuFramework", "xen.CommonCameraUtility", "dgarro.CustomShipLogModes" ], "conflicts": [ "Raicuparta.QuantumSpaceBuddies", "PacificEngine.OW_CommonResources" ], From 6a2ef64a301ac79449ae47998057f99c6bf9f574 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Feb 2024 14:13:32 -0500 Subject: [PATCH 02/10] Probably fixes #748 resets landing pad sensor contact bodies --- NewHorizons/Handlers/PlayerSpawnHandler.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index 4b665cbb4..38dfea274 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -76,6 +76,13 @@ public static void SpawnShip() pos += SpawnPointBuilder.ShipSpawn.transform.TransformDirection(SpawnPointBuilder.ShipSpawnOffset); } + // #748 Before moving the ship, reset all its landing pad sensors + // Else they might think its still touching TH + foreach (var landingPadSensor in ship.GetComponentsInChildren()) + { + landingPadSensor._contactBody = null; + } + SpawnBody(ship.GetAttachedOWRigidbody(), SpawnPointBuilder.ShipSpawn, pos); // Bug affecting mods with massive stars (8600m+ radius) From 43e58bc987d28d9822f8ae628970e768a349ab35 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Feb 2024 14:15:53 -0500 Subject: [PATCH 03/10] More comments --- NewHorizons/Handlers/PlayerSpawnHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index 38dfea274..c3300c979 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -78,6 +78,7 @@ public static void SpawnShip() // #748 Before moving the ship, reset all its landing pad sensors // Else they might think its still touching TH + // Doing this before moving the ship so that if they start contacting in the new spawn point then that gets preserved foreach (var landingPadSensor in ship.GetComponentsInChildren()) { landingPadSensor._contactBody = null; @@ -90,6 +91,7 @@ public static void SpawnShip() // This places the ship into the star's fluid volumes (destruction volume and atmosphere) // When the ship is teleported out, it doesn't update it's detected fluid volumes and gets affected by drag forever // Can fix by turning the volumes off and on again + // Done after re-positioning else it'd just get re-added to the old volumes foreach (var volume in ship.GetComponentInChildren()._activeVolumes) { if (volume.gameObject.activeInHierarchy) From f56d97fcf4a45fc1187e9dba0f99b313d4fb4efe Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Feb 2024 14:52:23 -0500 Subject: [PATCH 04/10] #738 fix spawn override, #728 warning popup for dlc mods --- NewHorizons/Assets/translations/english.json | 3 ++- NewHorizons/Assets/translations/french.json | 3 ++- NewHorizons/External/NewHorizonBody.cs | 9 +++++++++ NewHorizons/Main.cs | 21 ++++++++++++++++++-- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Assets/translations/english.json b/NewHorizons/Assets/translations/english.json index 48df51eef..6eed88c37 100644 --- a/NewHorizons/Assets/translations/english.json +++ b/NewHorizons/Assets/translations/english.json @@ -22,7 +22,8 @@ "DEBUG_PLACE_TEXT": "Place Nomai Text", "DEBUG_UNDO": "Undo", "DEBUG_REDO": "Redo", - "Vessel": "Vessel" + "Vessel": "Vessel", + "DLC_REQUIRED": "WARNING\n\nYou have addons (like {0}) installed which require the DLC but it is not enabled.\n\nYour mods may not function as intended." }, "OtherDictionary": { "NOMAI_SHUTTLE_COMPUTER": "The shuttle]]> is currently resting at {0}]]>." diff --git a/NewHorizons/Assets/translations/french.json b/NewHorizons/Assets/translations/french.json index c268d4152..ed7d553bd 100644 --- a/NewHorizons/Assets/translations/french.json +++ b/NewHorizons/Assets/translations/french.json @@ -17,7 +17,8 @@ "RICH_PRESENCE_WARPING": "En route vers {0}.", "OUTDATED_VERSION_WARNING": "AVERTISSEMENT\n\nNew Horizons fonctionne seulement sur la version {0} ou plus récente. Vous êtes sur la version {1}.\n\nVeuillez mettre à jour votre jeu ou désinstaller NH.", "JSON_FAILED_TO_LOAD": "Fichier(s) invalide(s): {0}", - "Vessel": "Vaisseau" + "Vessel": "Vaisseau", + "DLC_REQUIRED": "AVERTISSEMENT\n\nVous avez installé des addons (par exemple, {0}) qui nécessitent le DLC mais il n'est pas activé.\n\nVos mods peuvent ne pas fonctionner." }, "AchievementTranslations": { "NH_EATEN_OUTSIDE_BRAMBLE": { diff --git a/NewHorizons/External/NewHorizonBody.cs b/NewHorizons/External/NewHorizonBody.cs index fa80de75e..590c5b910 100644 --- a/NewHorizons/External/NewHorizonBody.cs +++ b/NewHorizons/External/NewHorizonBody.cs @@ -26,6 +26,15 @@ public NewHorizonsBody(PlanetConfig config, IModBehaviour mod, string relativePa public GameObject Object; + public bool RequiresDLC() + { + var detailPaths = Config.Props.details.Select(x => x.path); + return Config.Cloak != null + || Config.Props?.rafts != null + || Config.Props?.slideShows != null + || detailPaths.Any(x => x.StartsWith("Ringworld") || x.StartsWith("Dreamworld")); + } + #region Cache public void LoadCache() { diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 5d438e9de..92509469a 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -99,6 +99,8 @@ public string CurrentStarSystem public bool PlayerSpawned { get; set; } public bool ForceClearCaches { get; set; } // for reloading configs + public bool FlagDLCRequired { get; set; } + public ShipWarpController ShipWarpController { get; private set; } // API events @@ -690,6 +692,14 @@ public void LoadConfigs(IModBehaviour mod) var relativeDirectory = file.Replace(folder, ""); var body = LoadConfig(mod, relativeDirectory); + // Only bother checking if they need the DLC if they don't have it + if (!HasDLC && !FlagDLCRequired && body.RequiresDLC()) + { + FlagDLCRequired = true; + var popupText = TranslationHandler.GetTranslation("DLC_REQUIRED", TranslationHandler.TextType.UI).Replace("{0}", mod.ModHelper.Manifest.Name); + MenuHandler.RegisterOneTimePopup(this, popupText, true); + } + if (body != null) { // Wanna track the spawn point of each system @@ -921,8 +931,15 @@ private void ResetCurrentStarSystem() { CurrentStarSystem = _defaultSystemOverride; - // Sometimes the override will not support spawning regularly, so always warp in - IsWarpingFromShip = true; + if (BodyDict.TryGetValue(_defaultSystemOverride, out var bodies) && bodies.Any(x => x.Config?.Spawn?.shipSpawn != null)) + { + // #738 - Sometimes the override will not support spawning regularly, so always warp in if possible + IsWarpingFromShip = true; + } + else + { + IsWarpingFromShip = false; + } } else { From 5b5fb0b827614454106758b3f4387b699995fbe5 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Feb 2024 15:36:30 -0500 Subject: [PATCH 05/10] Also refresh force volumes affecting ship --- NewHorizons/Handlers/PlayerSpawnHandler.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index c3300c979..45e691cd4 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -100,6 +100,16 @@ public static void SpawnShip() volume.gameObject.SetActive(true); } } + // Also applies to force volumes + foreach (var volume in ship.GetComponentInChildren()._activeVolumes) + { + if (volume.gameObject.activeInHierarchy) + { + volume.gameObject.SetActive(false); + volume.gameObject.SetActive(true); + } + } + // For some reason none of this seems to apply to the Player } } else if (Main.Instance.CurrentStarSystem != "SolarSystem" && !Main.Instance.IsWarpingFromShip) From 0528360cc0a60656ca2bcea9df0afcb8c0c1e7d4 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Feb 2024 15:47:05 -0500 Subject: [PATCH 06/10] Had to ToList to duplicate the arrays --- NewHorizons/Handlers/PlayerSpawnHandler.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index 45e691cd4..223806651 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -2,6 +2,7 @@ using NewHorizons.Utility; using NewHorizons.Utility.OWML; using System.Collections; +using System.Linq; using UnityEngine; namespace NewHorizons.Handlers @@ -92,7 +93,10 @@ public static void SpawnShip() // When the ship is teleported out, it doesn't update it's detected fluid volumes and gets affected by drag forever // Can fix by turning the volumes off and on again // Done after re-positioning else it'd just get re-added to the old volumes - foreach (var volume in ship.GetComponentInChildren()._activeVolumes) + + // .ToList is because it makes a copy of the array, else it errors: + // "InvalidOperationException: Collection was modified; enumeration operation may not execute." + foreach (var volume in ship.GetComponentInChildren()._activeVolumes.ToList()) { if (volume.gameObject.activeInHierarchy) { @@ -101,7 +105,7 @@ public static void SpawnShip() } } // Also applies to force volumes - foreach (var volume in ship.GetComponentInChildren()._activeVolumes) + foreach (var volume in ship.GetComponentInChildren()._activeVolumes.ToList()) { if (volume.gameObject.activeInHierarchy) { @@ -109,7 +113,8 @@ public static void SpawnShip() volume.gameObject.SetActive(true); } } - // For some reason none of this seems to apply to the Player + // For some reason none of this seems to apply to the Player. + // If somebody ever makes a sound volume thats somehow always applying to the player tho then itd probably be this } } else if (Main.Instance.CurrentStarSystem != "SolarSystem" && !Main.Instance.IsWarpingFromShip) From 1a56fe1ab25e1273433c7ae1897ff5973bbd14ad Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Feb 2024 16:09:50 -0500 Subject: [PATCH 07/10] Preloading bundles and setting subtitle path --- NewHorizons/External/Configs/AddonConfig.cs | 12 + NewHorizons/Handlers/SubtitlesHandler.cs | 12 +- NewHorizons/Main.cs | 16 +- NewHorizons/NewHorizonsApi.cs | 482 +++++++++--------- .../Utility/Files/AssetBundleUtilities.cs | 194 +++---- 5 files changed, 383 insertions(+), 333 deletions(-) diff --git a/NewHorizons/External/Configs/AddonConfig.cs b/NewHorizons/External/Configs/AddonConfig.cs index 54d8cf0e9..cfcc731ba 100644 --- a/NewHorizons/External/Configs/AddonConfig.cs +++ b/NewHorizons/External/Configs/AddonConfig.cs @@ -31,5 +31,17 @@ public class AddonConfig /// If popupMessage is set, should it repeat every time the game starts or only once /// public bool repeatPopup; + + /// + /// These asset bundles will be loaded on the title screen and stay loaded. Will improve initial load time at the cost of increased memory use. + /// The path is the relative directory of the asset bundle in the mod folder. + /// + public string[] preloadAssetBundles; + + /// + /// The path to the addons subtitle for the main menu. + /// Defaults to "subtitle.png". + /// + public string subtitlePath = "subtitle.png"; } } diff --git a/NewHorizons/Handlers/SubtitlesHandler.cs b/NewHorizons/Handlers/SubtitlesHandler.cs index fc4bfe94f..de283b544 100644 --- a/NewHorizons/Handlers/SubtitlesHandler.cs +++ b/NewHorizons/Handlers/SubtitlesHandler.cs @@ -61,9 +61,17 @@ public void Start() private void AddSubtitles() { - foreach (var mod in Main.MountedAddons.Where(mod => File.Exists($"{mod.ModHelper.Manifest.ModFolderPath}subtitle.png"))) + foreach (var mod in Main.MountedAddons) { - AddSubtitle(mod, "subtitle.png"); + if (Main.AddonConfigs.TryGetValue(mod, out var addonConfig) && File.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, addonConfig.subtitlePath))) + { + AddSubtitle(mod, addonConfig.subtitlePath); + } + // Else default to subtitle.png + else if (File.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, "subtitle.png"))) + { + AddSubtitle(mod, "subtitle.png"); + } } } diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index 92509469a..ee07514ff 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -50,9 +50,10 @@ public class Main : ModBehaviour private static bool _wasConfigured = false; private static string _defaultSystemOverride; - public static Dictionary SystemDict = new Dictionary(); - public static Dictionary> BodyDict = new Dictionary>(); - public static List MountedAddons = new List(); + public static Dictionary SystemDict = new(); + public static Dictionary> BodyDict = new(); + public static List MountedAddons = new(); + public static Dictionary AddonConfigs = new(); public static float SecondsElapsedInLoop = -1; @@ -747,6 +748,15 @@ private void LoadAddonManifest(string file, IModBehaviour mod) { MenuHandler.RegisterOneTimePopup(mod, TranslationHandler.GetTranslation(addonConfig.popupMessage, TranslationHandler.TextType.UI), addonConfig.repeatPopup); } + if (addonConfig.preloadAssetBundles != null) + { + foreach (var bundle in addonConfig.preloadAssetBundles) + { + AssetBundleUtilities.PreloadBundle(bundle, mod); + } + } + + AddonConfigs[mod] = addonConfig; } private void LoadTranslations(string folder, IModBehaviour mod) diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index e5507abc0..d6d78ed9b 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -26,316 +26,314 @@ using UnityEngine.Events; using static NewHorizons.External.Modules.ShipLogModule; -namespace NewHorizons -{ +namespace NewHorizons; - public class NewHorizonsApi : INewHorizons +public class NewHorizonsApi : INewHorizons +{ + [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] + public void Create(Dictionary config) { - [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] - public void Create(Dictionary config) - { - Create(config, null); - } + Create(config, null); + } - [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] - public void Create(Dictionary config, IModBehaviour mod) + [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] + public void Create(Dictionary config, IModBehaviour mod) + { + try { - try - { - var name = (string)config["Name"]; + var name = (string)config["Name"]; - NHLogger.LogWarning($"Recieved API request to create planet [{name}]"); + NHLogger.LogWarning($"Recieved API request to create planet [{name}]"); - if (name == null) return; + if (name == null) return; - var relativePath = $"temp/{name}.json"; - var fullPath = Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, relativePath); - if (!Directory.Exists(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp"))) - { - Directory.CreateDirectory(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp")); - } - JsonHelper.SaveJsonObject(fullPath, config); - var body = Main.Instance.LoadConfig(Main.Instance, relativePath); - File.Delete(fullPath); - - // Update it to point to their mod for textures and stuff - body.Mod = mod ?? Main.Instance; - - if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); - Main.BodyDict[body.Config.starSystem].Add(body); - } - catch (Exception ex) + var relativePath = $"temp/{name}.json"; + var fullPath = Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, relativePath); + if (!Directory.Exists(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp"))) { - NHLogger.LogError($"Error in Create API:\n{ex}"); + Directory.CreateDirectory(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp")); } - } + JsonHelper.SaveJsonObject(fullPath, config); + var body = Main.Instance.LoadConfig(Main.Instance, relativePath); + File.Delete(fullPath); - [Obsolete("SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignRadial) is deprecated, please use SpawnObject(IModBehaviour mod, GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignRadial) instead")] - public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, - float scale, bool alignRadial) - { - return SpawnObject(null, planet, sector, propToCopyPath, position, eulerAngles, scale, alignRadial); - } + // Update it to point to their mod for textures and stuff + body.Mod = mod ?? Main.Instance; - public void LoadConfigs(IModBehaviour mod) - { - Main.Instance.LoadConfigs(mod); + if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); + Main.BodyDict[body.Config.starSystem].Add(body); } - - public GameObject GetPlanet(string name) + catch (Exception ex) { - return Main.BodyDict.Values.SelectMany(x => x)?.ToList()?.FirstOrDefault(x => x.Config.name == name)?.Object; + NHLogger.LogError($"Error in Create API:\n{ex}"); } + } - public string GetCurrentStarSystem() => Main.Instance.CurrentStarSystem; - public UnityEvent GetChangeStarSystemEvent() => Main.Instance.OnChangeStarSystem; - public UnityEvent GetStarSystemLoadedEvent() => Main.Instance.OnStarSystemLoaded; - public UnityEvent GetBodyLoadedEvent() => Main.Instance.OnPlanetLoaded; + [Obsolete("SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignRadial) is deprecated, please use SpawnObject(IModBehaviour mod, GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignRadial) instead")] + public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, + float scale, bool alignRadial) + { + return SpawnObject(null, planet, sector, propToCopyPath, position, eulerAngles, scale, alignRadial); + } - public bool SetDefaultSystem(string name) - { - if (!Main.SystemDict.ContainsKey(name)) return false; + public void LoadConfigs(IModBehaviour mod) + { + Main.Instance.LoadConfigs(mod); + } - Main.Instance.SetDefaultSystem(name); - return true; - } + public GameObject GetPlanet(string name) + { + return Main.BodyDict.Values.SelectMany(x => x)?.ToList()?.FirstOrDefault(x => x.Config.name == name)?.Object; + } - public bool ChangeCurrentStarSystem(string name) - { - if (!Main.SystemDict.ContainsKey(name)) return false; + public string GetCurrentStarSystem() => Main.Instance.CurrentStarSystem; + public UnityEvent GetChangeStarSystemEvent() => Main.Instance.OnChangeStarSystem; + public UnityEvent GetStarSystemLoadedEvent() => Main.Instance.OnStarSystemLoaded; + public UnityEvent GetBodyLoadedEvent() => Main.Instance.OnPlanetLoaded; - Main.Instance.ChangeCurrentStarSystem(name); - return true; - } + public bool SetDefaultSystem(string name) + { + if (!Main.SystemDict.ContainsKey(name)) return false; - public string[] GetInstalledAddons() + Main.Instance.SetDefaultSystem(name); + return true; + } + + public bool ChangeCurrentStarSystem(string name) + { + if (!Main.SystemDict.ContainsKey(name)) return false; + + Main.Instance.ChangeCurrentStarSystem(name); + return true; + } + + public string[] GetInstalledAddons() + { + try { - try - { - return Main.MountedAddons.Select(x => x?.ModHelper?.Manifest?.UniqueName).ToArray(); - } - catch (Exception ex) - { - NHLogger.LogError($"Couldn't get installed addons:\n{ex}"); - return new string[] { }; - } + return Main.MountedAddons.Select(x => x?.ModHelper?.Manifest?.UniqueName).ToArray(); } - - private static object QueryJson(Type outType, string filePath, string jsonPath) + catch (Exception ex) { - if (filePath == "") return null; - try - { - var jsonText = File.ReadAllText(filePath); - var jsonData = JObject.Parse(jsonText); - return jsonData.SelectToken(jsonPath)?.ToObject(outType); - } - catch (FileNotFoundException) - { - return null; - } - catch (JsonException e) - { - NHLogger.LogError(e.ToString()); - return null; - } + NHLogger.LogError($"Couldn't get installed addons:\n{ex}"); + return new string[] { }; } + } - public object QueryBody(Type outType, string bodyName, string jsonPath) + private static object QueryJson(Type outType, string filePath, string jsonPath) + { + if (filePath == "") return null; + try { - 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); + var jsonText = File.ReadAllText(filePath); + var jsonData = JObject.Parse(jsonText); + return jsonData.SelectToken(jsonPath)?.ToObject(outType); } - - public T QueryBody(string bodyName, string jsonPath) + catch (FileNotFoundException) { - var data = QueryBody(typeof(T), bodyName, jsonPath); - if (data is T result) - { - return result; - } - return default; + return null; } - - public object QuerySystem(Type outType, string jsonPath) + catch (JsonException e) { - var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; - return system == null - ? null - : QueryJson(outType, Path.Combine(system.Mod.ModHelper.Manifest.ModFolderPath, system.RelativePath), jsonPath); + NHLogger.LogError(e.ToString()); + return null; } + } - public T QuerySystem(string jsonPath) + 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); + } + + public T QueryBody(string bodyName, string jsonPath) + { + var data = QueryBody(typeof(T), bodyName, jsonPath); + if (data is T result) { - var data = QuerySystem(typeof(T), jsonPath); - if (data is T result) - { - return result; - } - return default; + return result; } + return default; + } + + public object QuerySystem(Type outType, string jsonPath) + { + var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; + return system == null + ? null + : QueryJson(outType, Path.Combine(system.Mod.ModHelper.Manifest.ModFolderPath, system.RelativePath), jsonPath); + } - public GameObject SpawnObject(IModBehaviour mod, GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, - float scale, bool alignRadial) + public T QuerySystem(string jsonPath) + { + var data = QuerySystem(typeof(T), jsonPath); + if (data is T result) { - var prefab = SearchUtilities.Find(propToCopyPath); - var detailInfo = new DetailInfo() - { - position = position, - rotation = eulerAngles, - scale = scale, - alignRadial = alignRadial - }; - return DetailBuilder.Make(planet, sector, mod, prefab, detailInfo); + return result; } + return default; + } - public AudioSignal SpawnSignal(IModBehaviour mod, GameObject root, string audio, string name, string frequency, - float sourceRadius = 1f, float detectionRadius = 20f, float identificationRadius = 10f, bool insideCloak = false, - bool onlyAudibleToScope = true, string reveals = "") + public GameObject SpawnObject(IModBehaviour mod, GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, + float scale, bool alignRadial) + { + var prefab = SearchUtilities.Find(propToCopyPath); + var detailInfo = new DetailInfo() { - var info = new SignalInfo() - { - audio = audio, - detectionRadius = detectionRadius, - frequency = frequency, - identificationRadius = identificationRadius, - insideCloak = insideCloak, - name = name, - onlyAudibleToScope = onlyAudibleToScope, - position = Vector3.zero, - reveals = reveals, - sourceRadius = sourceRadius - }; - - return SignalBuilder.Make(root, null, info, mod).GetComponent(); - } + position = position, + rotation = eulerAngles, + scale = scale, + alignRadial = alignRadial + }; + return DetailBuilder.Make(planet, sector, mod, prefab, detailInfo); + } - public (CharacterDialogueTree, RemoteDialogueTrigger) SpawnDialogue(IModBehaviour mod, GameObject root, string xmlFile, float radius = 1f, - float range = 1f, string blockAfterPersistentCondition = null, float lookAtRadius = 1f, string pathToAnimController = null, - float remoteTriggerRadius = 0f) + public AudioSignal SpawnSignal(IModBehaviour mod, GameObject root, string audio, string name, string frequency, + float sourceRadius = 1f, float detectionRadius = 20f, float identificationRadius = 10f, bool insideCloak = false, + bool onlyAudibleToScope = true, string reveals = "") + { + var info = new SignalInfo() { - var info = new DialogueInfo() - { - blockAfterPersistentCondition = blockAfterPersistentCondition, - lookAtRadius = lookAtRadius, - pathToAnimController = pathToAnimController, - position = Vector3.zero, - radius = radius, - range = range, - xmlFile = xmlFile, - remoteTrigger = remoteTriggerRadius > 0f ? new RemoteTriggerInfo() - { - position = null, - radius = remoteTriggerRadius, - } : null, - }; - - return DialogueBuilder.Make(root, null, info, mod); - } + audio = audio, + detectionRadius = detectionRadius, + frequency = frequency, + identificationRadius = identificationRadius, + insideCloak = insideCloak, + name = name, + onlyAudibleToScope = onlyAudibleToScope, + position = Vector3.zero, + reveals = reveals, + sourceRadius = sourceRadius + }; + + return SignalBuilder.Make(root, null, info, mod).GetComponent(); + } - public void CreatePlanet(string config, IModBehaviour mod) + public (CharacterDialogueTree, RemoteDialogueTrigger) SpawnDialogue(IModBehaviour mod, GameObject root, string xmlFile, float radius = 1f, + float range = 1f, string blockAfterPersistentCondition = null, float lookAtRadius = 1f, string pathToAnimController = null, + float remoteTriggerRadius = 0f) + { + var info = new DialogueInfo() { - try + blockAfterPersistentCondition = blockAfterPersistentCondition, + lookAtRadius = lookAtRadius, + pathToAnimController = pathToAnimController, + position = Vector3.zero, + radius = radius, + range = range, + xmlFile = xmlFile, + remoteTrigger = remoteTriggerRadius > 0f ? new RemoteTriggerInfo() { - var planet = JsonConvert.DeserializeObject(config); - if (planet == null) - { - NHLogger.LogError($"Couldn't load planet via API. Is your Json formatted correctly? {config}"); - return; - } + position = null, + radius = remoteTriggerRadius, + } : null, + }; - var body = Main.Instance.RegisterPlanetConfig(planet, mod, null); + return DialogueBuilder.Make(root, null, info, mod); + } - if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); - Main.BodyDict[body.Config.starSystem].Add(body); - } - catch (Exception ex) + public void CreatePlanet(string config, IModBehaviour mod) + { + try + { + var planet = JsonConvert.DeserializeObject(config); + if (planet == null) { - NHLogger.LogError($"Error in CreatePlanet API:\n{ex}"); + NHLogger.LogError($"Couldn't load planet via API. Is your Json formatted correctly? {config}"); + return; } - } - public void DefineStarSystem(string name, string config, IModBehaviour mod) + var body = Main.Instance.RegisterPlanetConfig(planet, mod, null); + + if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); + Main.BodyDict[body.Config.starSystem].Add(body); + } + catch (Exception ex) { - var starSystemConfig = JsonConvert.DeserializeObject(config); - Main.Instance.LoadStarSystemConfig(name, starSystemConfig, null, mod); + NHLogger.LogError($"Error in CreatePlanet API:\n{ex}"); } + } + + public void DefineStarSystem(string name, string config, IModBehaviour mod) + { + var starSystemConfig = JsonConvert.DeserializeObject(config); + Main.Instance.LoadStarSystemConfig(name, starSystemConfig, null, mod); + } + + public (CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO) + { + var info = JsonConvert.DeserializeObject(dialogueInfo); + return DialogueBuilder.Make(planetGO, null, info, xml, textAssetID); + } - public (CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO) + public GameObject CreateNomaiText(string xml, string textInfo, GameObject planetGO) + { + var info = JsonConvert.DeserializeObject(textInfo); + return TranslatorTextBuilder.Make(planetGO, null, info, null, xml); + } + + public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary entryPositions, Dictionary curiousityColours) + { + // This method has to be called each time the ship log manager is created, i.e. each time a system loads so it will only ever be relevant to the current one. + var starSystem = Main.Instance.CurrentStarSystem; + + var body = new NewHorizonsBody(new PlanetConfig() { - var info = JsonConvert.DeserializeObject(dialogueInfo); - return DialogueBuilder.Make(planetGO, null, info, xml, textAssetID); - } + name = planetName, + starSystem = starSystem, + ShipLog = new ShipLogModule() + { + spriteFolder = imageFolder + } + }, mod); - public GameObject CreateNomaiText(string xml, string textInfo, GameObject planetGO) + if (!Main.BodyDict.ContainsKey(starSystem)) { - var info = JsonConvert.DeserializeObject(textInfo); - return TranslatorTextBuilder.Make(planetGO, null, info, null, xml); + Main.BodyDict.Add(starSystem, new List()); + Main.BodyDict[starSystem].Add(body); } - - public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary entryPositions, Dictionary curiousityColours) + else { - // This method has to be called each time the ship log manager is created, i.e. each time a system loads so it will only ever be relevant to the current one. - var starSystem = Main.Instance.CurrentStarSystem; - - var body = new NewHorizonsBody(new PlanetConfig() + var existingBody = Main.BodyDict[starSystem] + .FirstOrDefault(x => x.Config.name == planetName && x.Mod.ModHelper.Manifest.UniqueName == mod.ModHelper.Manifest.UniqueName); + if (existingBody != null) { - name = planetName, - starSystem = starSystem, - ShipLog = new ShipLogModule() - { - spriteFolder = imageFolder - } - }, mod); - - if (!Main.BodyDict.ContainsKey(starSystem)) - { - Main.BodyDict.Add(starSystem, new List()); - Main.BodyDict[starSystem].Add(body); + body = existingBody; } else { - var existingBody = Main.BodyDict[starSystem] - .FirstOrDefault(x => x.Config.name == planetName && x.Mod.ModHelper.Manifest.UniqueName == mod.ModHelper.Manifest.UniqueName); - if (existingBody != null) - { - body = existingBody; - } - else - { - Main.BodyDict[starSystem].Add(body); - } + Main.BodyDict[starSystem].Add(body); } + } - var system = new StarSystemConfig() - { - entryPositions = entryPositions? - .Select((pair) => new EntryPositionInfo() { id = pair.Key, position = pair.Value }) - .ToArray(), - curiosities = curiousityColours? - .Select((pair) => new CuriosityColorInfo() { id = pair.Key, color = MColor.FromColor(pair.Value.colour), highlightColor = MColor.FromColor(pair.Value.highlight) }) - .ToArray() - }; + var system = new StarSystemConfig() + { + entryPositions = entryPositions? + .Select((pair) => new EntryPositionInfo() { id = pair.Key, position = pair.Value }) + .ToArray(), + curiosities = curiousityColours? + .Select((pair) => new CuriosityColorInfo() { id = pair.Key, color = MColor.FromColor(pair.Value.colour), highlightColor = MColor.FromColor(pair.Value.highlight) }) + .ToArray() + }; - Main.Instance.LoadStarSystemConfig(starSystem, system, null, mod); + Main.Instance.LoadStarSystemConfig(starSystem, system, null, mod); - RumorModeBuilder.AddShipLogXML(GameObject.FindObjectOfType(), xml, body); - } + RumorModeBuilder.AddShipLogXML(GameObject.FindObjectOfType(), xml, body); + } - /// - /// Register your own builder that will act on the given GameObject by reading its raw json string - /// - /// - public void RegisterCustomBuilder(Action builder) => PlanetCreationHandler.CustomBuilders.Add(builder); + /// + /// Register your own builder that will act on the given GameObject by reading its raw json string + /// + /// + public void RegisterCustomBuilder(Action builder) => PlanetCreationHandler.CustomBuilders.Add(builder); - public string GetTranslationForShipLog(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.SHIPLOG); + public string GetTranslationForShipLog(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.SHIPLOG); - public string GetTranslationForDialogue(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.DIALOGUE); + public string GetTranslationForDialogue(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.DIALOGUE); - public string GetTranslationForUI(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.UI); + public string GetTranslationForUI(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.UI); - public string GetTranslationForOtherText(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.OTHER); - } + public string GetTranslationForOtherText(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.OTHER); } diff --git a/NewHorizons/Utility/Files/AssetBundleUtilities.cs b/NewHorizons/Utility/Files/AssetBundleUtilities.cs index 182826bab..83bf14610 100644 --- a/NewHorizons/Utility/Files/AssetBundleUtilities.cs +++ b/NewHorizons/Utility/Files/AssetBundleUtilities.cs @@ -3,123 +3,145 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using UnityEngine; -namespace NewHorizons.Utility.Files +namespace NewHorizons.Utility.Files; + +public static class AssetBundleUtilities { - public static class AssetBundleUtilities - { - public static Dictionary AssetBundles = new Dictionary(); + public static Dictionary AssetBundles = new Dictionary(); - public static void ClearCache() + public static void ClearCache() + { + foreach (var pair in AssetBundles) { - foreach (var pair in AssetBundles) + if (!pair.Value.keepLoaded) { - if (pair.Value == null) NHLogger.LogError($"The asset bundle for {pair.Key} was null when trying to unload"); - else pair.Value.Unload(true); + if (pair.Value.bundle == null) + { + NHLogger.LogError($"The asset bundle for {pair.Key} was null when trying to unload"); + } + else + { + pair.Value.bundle.Unload(true); + } } - AssetBundles.Clear(); + } + AssetBundles = AssetBundles.Where(x => x.Value.keepLoaded).ToDictionary(x => x.Key, x => x.Value); + } - public static T Load(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) where T : UnityEngine.Object + public static void PreloadBundle(string assetBundleRelativeDir, IModBehaviour mod) + { + string key = Path.GetFileName(assetBundleRelativeDir); + var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); + var request = AssetBundle.LoadFromFileAsync(completePath); + request.completed += _ => { - string key = Path.GetFileName(assetBundleRelativeDir); - T obj; + NHLogger.Log($"Finished loading async bundle {assetBundleRelativeDir}"); + AssetBundles[key] = (request.assetBundle, true); + }; + } - try - { - AssetBundle bundle; + public static T Load(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) where T : UnityEngine.Object + { + string key = Path.GetFileName(assetBundleRelativeDir); + T obj; - if (AssetBundles.ContainsKey(key)) - { - bundle = AssetBundles[key]; - } - else - { - var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); - bundle = AssetBundle.LoadFromFile(completePath); - if (bundle == null) - { - NHLogger.LogError($"Couldn't load AssetBundle at [{completePath}] for [{mod.ModHelper.Manifest.Name}]"); - return null; - } - - AssetBundles[key] = bundle; - } + try + { + AssetBundle bundle; - obj = bundle.LoadAsset(pathInBundle); + if (AssetBundles.ContainsKey(key)) + { + bundle = AssetBundles[key].bundle; } - catch (Exception e) + else { - NHLogger.LogError($"Couldn't load asset {pathInBundle} from AssetBundle {assetBundleRelativeDir}:\n{e}"); - return null; + var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); + bundle = AssetBundle.LoadFromFile(completePath); + if (bundle == null) + { + NHLogger.LogError($"Couldn't load AssetBundle at [{completePath}] for [{mod.ModHelper.Manifest.Name}]"); + return null; + } + + AssetBundles[key] = (bundle, false); } - return obj; + obj = bundle.LoadAsset(pathInBundle); } - - public static GameObject LoadPrefab(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) + catch (Exception e) { - var prefab = Load(assetBundleRelativeDir, pathInBundle, mod); + NHLogger.LogError($"Couldn't load asset {pathInBundle} from AssetBundle {assetBundleRelativeDir}:\n{e}"); + return null; + } - prefab.SetActive(false); + return obj; + } - ReplaceShaders(prefab); + public static GameObject LoadPrefab(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) + { + var prefab = Load(assetBundleRelativeDir, pathInBundle, mod); - return prefab; - } + prefab.SetActive(false); - public static void ReplaceShaders(GameObject prefab) + ReplaceShaders(prefab); + + return prefab; + } + + public static void ReplaceShaders(GameObject prefab) + { + foreach (var renderer in prefab.GetComponentsInChildren(true)) { - foreach (var renderer in prefab.GetComponentsInChildren(true)) + foreach (var material in renderer.sharedMaterials) { - foreach (var material in renderer.sharedMaterials) + if (material == null) continue; + + var replacementShader = Shader.Find(material.shader.name); + if (replacementShader == null) continue; + + // preserve override tag and render queue (for Standard shader) + // keywords and properties are already preserved + if (material.renderQueue != material.shader.renderQueue) + { + var renderType = material.GetTag("RenderType", false); + var renderQueue = material.renderQueue; + material.shader = replacementShader; + material.SetOverrideTag("RenderType", renderType); + material.renderQueue = renderQueue; + } + else { - if (material == null) continue; - - var replacementShader = Shader.Find(material.shader.name); - if (replacementShader == null) continue; - - // preserve override tag and render queue (for Standard shader) - // keywords and properties are already preserved - if (material.renderQueue != material.shader.renderQueue) - { - var renderType = material.GetTag("RenderType", false); - var renderQueue = material.renderQueue; - material.shader = replacementShader; - material.SetOverrideTag("RenderType", renderType); - material.renderQueue = renderQueue; - } - else - { - material.shader = replacementShader; - } + material.shader = replacementShader; } } + } - foreach (var trenderer in prefab.GetComponentsInChildren(true)) + foreach (var trenderer in prefab.GetComponentsInChildren(true)) + { + foreach (var material in trenderer.sharedMaterials) { - foreach (var material in trenderer.sharedMaterials) + if (material == null) continue; + + var replacementShader = Shader.Find(material.shader.name); + if (replacementShader == null) continue; + + // preserve override tag and render queue (for Standard shader) + // keywords and properties are already preserved + if (material.renderQueue != material.shader.renderQueue) + { + var renderType = material.GetTag("RenderType", false); + var renderQueue = material.renderQueue; + material.shader = replacementShader; + material.SetOverrideTag("RenderType", renderType); + material.renderQueue = renderQueue; + } + else { - if (material == null) continue; - - var replacementShader = Shader.Find(material.shader.name); - if (replacementShader == null) continue; - - // preserve override tag and render queue (for Standard shader) - // keywords and properties are already preserved - if (material.renderQueue != material.shader.renderQueue) - { - var renderType = material.GetTag("RenderType", false); - var renderQueue = material.renderQueue; - material.shader = replacementShader; - material.SetOverrideTag("RenderType", renderType); - material.renderQueue = renderQueue; - } - else - { - material.shader = replacementShader; - } + material.shader = replacementShader; } } } From 73b818d0b46f3a901c30c963d09d3822bfd83077 Mon Sep 17 00:00:00 2001 From: Ben C Date: Sat, 3 Feb 2024 21:11:25 +0000 Subject: [PATCH 08/10] Updated Schemas --- NewHorizons/Schemas/addon_manifest_schema.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NewHorizons/Schemas/addon_manifest_schema.json b/NewHorizons/Schemas/addon_manifest_schema.json index aada816a4..dec9022f4 100644 --- a/NewHorizons/Schemas/addon_manifest_schema.json +++ b/NewHorizons/Schemas/addon_manifest_schema.json @@ -27,6 +27,17 @@ "type": "boolean", "description": "If popupMessage is set, should it repeat every time the game starts or only once" }, + "preloadAssetBundles": { + "type": "array", + "description": "These asset bundles will be loaded on the title screen and stay loaded. Will improve initial load time at the cost of increased memory use.\nThe path is the relative directory of the asset bundle in the mod folder.", + "items": { + "type": "string" + } + }, + "subtitlePath": { + "type": "string", + "description": "The path to the addons subtitle for the main menu.\nDefaults to \"subtitle.png\"." + }, "$schema": { "type": "string", "description": "The schema to validate with" From f11099d62f48119ab5ead9b6257e822092bd44f8 Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 3 Feb 2024 16:17:20 -0500 Subject: [PATCH 09/10] Don't impulsively change namespace brackets --- NewHorizons/NewHorizonsApi.cs | 481 +++++++++--------- .../Utility/Files/AssetBundleUtilities.cs | 209 ++++---- 2 files changed, 346 insertions(+), 344 deletions(-) diff --git a/NewHorizons/NewHorizonsApi.cs b/NewHorizons/NewHorizonsApi.cs index d6d78ed9b..a96673d52 100644 --- a/NewHorizons/NewHorizonsApi.cs +++ b/NewHorizons/NewHorizonsApi.cs @@ -26,314 +26,315 @@ using UnityEngine.Events; using static NewHorizons.External.Modules.ShipLogModule; -namespace NewHorizons; - -public class NewHorizonsApi : INewHorizons +namespace NewHorizons { - [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] - public void Create(Dictionary config) + public class NewHorizonsApi : INewHorizons { - Create(config, null); - } + [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] + public void Create(Dictionary config) + { + Create(config, null); + } - [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] - public void Create(Dictionary config, IModBehaviour mod) - { - try + [Obsolete("Create(Dictionary config) is deprecated, please use LoadConfigs(IModBehaviour mod) instead")] + public void Create(Dictionary config, IModBehaviour mod) { - var name = (string)config["Name"]; + try + { + var name = (string)config["Name"]; - NHLogger.LogWarning($"Recieved API request to create planet [{name}]"); + NHLogger.LogWarning($"Recieved API request to create planet [{name}]"); - if (name == null) return; + if (name == null) return; - var relativePath = $"temp/{name}.json"; - var fullPath = Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, relativePath); - if (!Directory.Exists(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp"))) - { - Directory.CreateDirectory(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp")); - } - JsonHelper.SaveJsonObject(fullPath, config); - var body = Main.Instance.LoadConfig(Main.Instance, relativePath); - File.Delete(fullPath); + var relativePath = $"temp/{name}.json"; + var fullPath = Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, relativePath); + if (!Directory.Exists(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp"))) + { + Directory.CreateDirectory(Path.Combine(Main.Instance.ModHelper.Manifest.ModFolderPath, "temp")); + } + JsonHelper.SaveJsonObject(fullPath, config); + var body = Main.Instance.LoadConfig(Main.Instance, relativePath); + File.Delete(fullPath); - // Update it to point to their mod for textures and stuff - body.Mod = mod ?? Main.Instance; + // Update it to point to their mod for textures and stuff + body.Mod = mod ?? Main.Instance; - if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); - Main.BodyDict[body.Config.starSystem].Add(body); + if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); + Main.BodyDict[body.Config.starSystem].Add(body); + } + catch (Exception ex) + { + NHLogger.LogError($"Error in Create API:\n{ex}"); + } } - catch (Exception ex) + + [Obsolete("SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignRadial) is deprecated, please use SpawnObject(IModBehaviour mod, GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignRadial) instead")] + public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, + float scale, bool alignRadial) { - NHLogger.LogError($"Error in Create API:\n{ex}"); + return SpawnObject(null, planet, sector, propToCopyPath, position, eulerAngles, scale, alignRadial); } - } - [Obsolete("SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignRadial) is deprecated, please use SpawnObject(IModBehaviour mod, GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, float scale, bool alignRadial) instead")] - public GameObject SpawnObject(GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, - float scale, bool alignRadial) - { - return SpawnObject(null, planet, sector, propToCopyPath, position, eulerAngles, scale, alignRadial); - } - - public void LoadConfigs(IModBehaviour mod) - { - Main.Instance.LoadConfigs(mod); - } + public void LoadConfigs(IModBehaviour mod) + { + Main.Instance.LoadConfigs(mod); + } - public GameObject GetPlanet(string name) - { - return Main.BodyDict.Values.SelectMany(x => x)?.ToList()?.FirstOrDefault(x => x.Config.name == name)?.Object; - } + public GameObject GetPlanet(string name) + { + return Main.BodyDict.Values.SelectMany(x => x)?.ToList()?.FirstOrDefault(x => x.Config.name == name)?.Object; + } - public string GetCurrentStarSystem() => Main.Instance.CurrentStarSystem; - public UnityEvent GetChangeStarSystemEvent() => Main.Instance.OnChangeStarSystem; - public UnityEvent GetStarSystemLoadedEvent() => Main.Instance.OnStarSystemLoaded; - public UnityEvent GetBodyLoadedEvent() => Main.Instance.OnPlanetLoaded; + public string GetCurrentStarSystem() => Main.Instance.CurrentStarSystem; + public UnityEvent GetChangeStarSystemEvent() => Main.Instance.OnChangeStarSystem; + public UnityEvent GetStarSystemLoadedEvent() => Main.Instance.OnStarSystemLoaded; + public UnityEvent GetBodyLoadedEvent() => Main.Instance.OnPlanetLoaded; - public bool SetDefaultSystem(string name) - { - if (!Main.SystemDict.ContainsKey(name)) return false; + public bool SetDefaultSystem(string name) + { + if (!Main.SystemDict.ContainsKey(name)) return false; - Main.Instance.SetDefaultSystem(name); - return true; - } + Main.Instance.SetDefaultSystem(name); + return true; + } - public bool ChangeCurrentStarSystem(string name) - { - if (!Main.SystemDict.ContainsKey(name)) return false; + public bool ChangeCurrentStarSystem(string name) + { + if (!Main.SystemDict.ContainsKey(name)) return false; - Main.Instance.ChangeCurrentStarSystem(name); - return true; - } + Main.Instance.ChangeCurrentStarSystem(name); + return true; + } - public string[] GetInstalledAddons() - { - try + public string[] GetInstalledAddons() { - return Main.MountedAddons.Select(x => x?.ModHelper?.Manifest?.UniqueName).ToArray(); + try + { + return Main.MountedAddons.Select(x => x?.ModHelper?.Manifest?.UniqueName).ToArray(); + } + catch (Exception ex) + { + NHLogger.LogError($"Couldn't get installed addons:\n{ex}"); + return new string[] { }; + } } - catch (Exception ex) + + private static object QueryJson(Type outType, string filePath, string jsonPath) { - NHLogger.LogError($"Couldn't get installed addons:\n{ex}"); - return new string[] { }; + if (filePath == "") return null; + try + { + var jsonText = File.ReadAllText(filePath); + var jsonData = JObject.Parse(jsonText); + return jsonData.SelectToken(jsonPath)?.ToObject(outType); + } + catch (FileNotFoundException) + { + return null; + } + catch (JsonException e) + { + NHLogger.LogError(e.ToString()); + return null; + } } - } - private static object QueryJson(Type outType, string filePath, string jsonPath) - { - if (filePath == "") return null; - try + public object QueryBody(Type outType, string bodyName, string jsonPath) { - var jsonText = File.ReadAllText(filePath); - var jsonData = JObject.Parse(jsonText); - return jsonData.SelectToken(jsonPath)?.ToObject(outType); + 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); } - catch (FileNotFoundException) + + public T QueryBody(string bodyName, string jsonPath) { - return null; + var data = QueryBody(typeof(T), bodyName, jsonPath); + if (data is T result) + { + return result; + } + return default; } - catch (JsonException e) + + public object QuerySystem(Type outType, string jsonPath) { - NHLogger.LogError(e.ToString()); - return null; + var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; + return system == null + ? null + : QueryJson(outType, Path.Combine(system.Mod.ModHelper.Manifest.ModFolderPath, system.RelativePath), jsonPath); } - } - - 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); - } - public T QueryBody(string bodyName, string jsonPath) - { - var data = QueryBody(typeof(T), bodyName, jsonPath); - if (data is T result) + public T QuerySystem(string jsonPath) { - return result; + var data = QuerySystem(typeof(T), jsonPath); + if (data is T result) + { + return result; + } + return default; } - return default; - } - - public object QuerySystem(Type outType, string jsonPath) - { - var system = Main.SystemDict[Main.Instance.CurrentStarSystem]; - return system == null - ? null - : QueryJson(outType, Path.Combine(system.Mod.ModHelper.Manifest.ModFolderPath, system.RelativePath), jsonPath); - } - public T QuerySystem(string jsonPath) - { - var data = QuerySystem(typeof(T), jsonPath); - if (data is T result) + public GameObject SpawnObject(IModBehaviour mod, GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, + float scale, bool alignRadial) { - return result; + var prefab = SearchUtilities.Find(propToCopyPath); + var detailInfo = new DetailInfo() + { + position = position, + rotation = eulerAngles, + scale = scale, + alignRadial = alignRadial + }; + return DetailBuilder.Make(planet, sector, mod, prefab, detailInfo); } - return default; - } - public GameObject SpawnObject(IModBehaviour mod, GameObject planet, Sector sector, string propToCopyPath, Vector3 position, Vector3 eulerAngles, - float scale, bool alignRadial) - { - var prefab = SearchUtilities.Find(propToCopyPath); - var detailInfo = new DetailInfo() + public AudioSignal SpawnSignal(IModBehaviour mod, GameObject root, string audio, string name, string frequency, + float sourceRadius = 1f, float detectionRadius = 20f, float identificationRadius = 10f, bool insideCloak = false, + bool onlyAudibleToScope = true, string reveals = "") { - position = position, - rotation = eulerAngles, - scale = scale, - alignRadial = alignRadial - }; - return DetailBuilder.Make(planet, sector, mod, prefab, detailInfo); - } + var info = new SignalInfo() + { + audio = audio, + detectionRadius = detectionRadius, + frequency = frequency, + identificationRadius = identificationRadius, + insideCloak = insideCloak, + name = name, + onlyAudibleToScope = onlyAudibleToScope, + position = Vector3.zero, + reveals = reveals, + sourceRadius = sourceRadius + }; + + return SignalBuilder.Make(root, null, info, mod).GetComponent(); + } - public AudioSignal SpawnSignal(IModBehaviour mod, GameObject root, string audio, string name, string frequency, - float sourceRadius = 1f, float detectionRadius = 20f, float identificationRadius = 10f, bool insideCloak = false, - bool onlyAudibleToScope = true, string reveals = "") - { - var info = new SignalInfo() + public (CharacterDialogueTree, RemoteDialogueTrigger) SpawnDialogue(IModBehaviour mod, GameObject root, string xmlFile, float radius = 1f, + float range = 1f, string blockAfterPersistentCondition = null, float lookAtRadius = 1f, string pathToAnimController = null, + float remoteTriggerRadius = 0f) { - audio = audio, - detectionRadius = detectionRadius, - frequency = frequency, - identificationRadius = identificationRadius, - insideCloak = insideCloak, - name = name, - onlyAudibleToScope = onlyAudibleToScope, - position = Vector3.zero, - reveals = reveals, - sourceRadius = sourceRadius - }; - - return SignalBuilder.Make(root, null, info, mod).GetComponent(); - } + var info = new DialogueInfo() + { + blockAfterPersistentCondition = blockAfterPersistentCondition, + lookAtRadius = lookAtRadius, + pathToAnimController = pathToAnimController, + position = Vector3.zero, + radius = radius, + range = range, + xmlFile = xmlFile, + remoteTrigger = remoteTriggerRadius > 0f ? new RemoteTriggerInfo() + { + position = null, + radius = remoteTriggerRadius, + } : null, + }; + + return DialogueBuilder.Make(root, null, info, mod); + } - public (CharacterDialogueTree, RemoteDialogueTrigger) SpawnDialogue(IModBehaviour mod, GameObject root, string xmlFile, float radius = 1f, - float range = 1f, string blockAfterPersistentCondition = null, float lookAtRadius = 1f, string pathToAnimController = null, - float remoteTriggerRadius = 0f) - { - var info = new DialogueInfo() + public void CreatePlanet(string config, IModBehaviour mod) { - blockAfterPersistentCondition = blockAfterPersistentCondition, - lookAtRadius = lookAtRadius, - pathToAnimController = pathToAnimController, - position = Vector3.zero, - radius = radius, - range = range, - xmlFile = xmlFile, - remoteTrigger = remoteTriggerRadius > 0f ? new RemoteTriggerInfo() + try { - position = null, - radius = remoteTriggerRadius, - } : null, - }; + var planet = JsonConvert.DeserializeObject(config); + if (planet == null) + { + NHLogger.LogError($"Couldn't load planet via API. Is your Json formatted correctly? {config}"); + return; + } - return DialogueBuilder.Make(root, null, info, mod); - } + var body = Main.Instance.RegisterPlanetConfig(planet, mod, null); - public void CreatePlanet(string config, IModBehaviour mod) - { - try - { - var planet = JsonConvert.DeserializeObject(config); - if (planet == null) + if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); + Main.BodyDict[body.Config.starSystem].Add(body); + } + catch (Exception ex) { - NHLogger.LogError($"Couldn't load planet via API. Is your Json formatted correctly? {config}"); - return; + NHLogger.LogError($"Error in CreatePlanet API:\n{ex}"); } - - var body = Main.Instance.RegisterPlanetConfig(planet, mod, null); - - if (!Main.BodyDict.ContainsKey(body.Config.starSystem)) Main.BodyDict.Add(body.Config.starSystem, new List()); - Main.BodyDict[body.Config.starSystem].Add(body); } - catch (Exception ex) + + public void DefineStarSystem(string name, string config, IModBehaviour mod) { - NHLogger.LogError($"Error in CreatePlanet API:\n{ex}"); + var starSystemConfig = JsonConvert.DeserializeObject(config); + Main.Instance.LoadStarSystemConfig(name, starSystemConfig, null, mod); } - } - - public void DefineStarSystem(string name, string config, IModBehaviour mod) - { - var starSystemConfig = JsonConvert.DeserializeObject(config); - Main.Instance.LoadStarSystemConfig(name, starSystemConfig, null, mod); - } - - public (CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO) - { - var info = JsonConvert.DeserializeObject(dialogueInfo); - return DialogueBuilder.Make(planetGO, null, info, xml, textAssetID); - } - public GameObject CreateNomaiText(string xml, string textInfo, GameObject planetGO) - { - var info = JsonConvert.DeserializeObject(textInfo); - return TranslatorTextBuilder.Make(planetGO, null, info, null, xml); - } - - public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary entryPositions, Dictionary curiousityColours) - { - // This method has to be called each time the ship log manager is created, i.e. each time a system loads so it will only ever be relevant to the current one. - var starSystem = Main.Instance.CurrentStarSystem; - - var body = new NewHorizonsBody(new PlanetConfig() + public (CharacterDialogueTree, RemoteDialogueTrigger) CreateDialogueFromXML(string textAssetID, string xml, string dialogueInfo, GameObject planetGO) { - name = planetName, - starSystem = starSystem, - ShipLog = new ShipLogModule() - { - spriteFolder = imageFolder - } - }, mod); + var info = JsonConvert.DeserializeObject(dialogueInfo); + return DialogueBuilder.Make(planetGO, null, info, xml, textAssetID); + } - if (!Main.BodyDict.ContainsKey(starSystem)) + public GameObject CreateNomaiText(string xml, string textInfo, GameObject planetGO) { - Main.BodyDict.Add(starSystem, new List()); - Main.BodyDict[starSystem].Add(body); + var info = JsonConvert.DeserializeObject(textInfo); + return TranslatorTextBuilder.Make(planetGO, null, info, null, xml); } - else + + public void AddShipLogXML(IModBehaviour mod, XElement xml, string planetName, string imageFolder, Dictionary entryPositions, Dictionary curiousityColours) { - var existingBody = Main.BodyDict[starSystem] - .FirstOrDefault(x => x.Config.name == planetName && x.Mod.ModHelper.Manifest.UniqueName == mod.ModHelper.Manifest.UniqueName); - if (existingBody != null) + // This method has to be called each time the ship log manager is created, i.e. each time a system loads so it will only ever be relevant to the current one. + var starSystem = Main.Instance.CurrentStarSystem; + + var body = new NewHorizonsBody(new PlanetConfig() { - body = existingBody; + name = planetName, + starSystem = starSystem, + ShipLog = new ShipLogModule() + { + spriteFolder = imageFolder + } + }, mod); + + if (!Main.BodyDict.ContainsKey(starSystem)) + { + Main.BodyDict.Add(starSystem, new List()); + Main.BodyDict[starSystem].Add(body); } else { - Main.BodyDict[starSystem].Add(body); + var existingBody = Main.BodyDict[starSystem] + .FirstOrDefault(x => x.Config.name == planetName && x.Mod.ModHelper.Manifest.UniqueName == mod.ModHelper.Manifest.UniqueName); + if (existingBody != null) + { + body = existingBody; + } + else + { + Main.BodyDict[starSystem].Add(body); + } } - } - var system = new StarSystemConfig() - { - entryPositions = entryPositions? - .Select((pair) => new EntryPositionInfo() { id = pair.Key, position = pair.Value }) - .ToArray(), - curiosities = curiousityColours? - .Select((pair) => new CuriosityColorInfo() { id = pair.Key, color = MColor.FromColor(pair.Value.colour), highlightColor = MColor.FromColor(pair.Value.highlight) }) - .ToArray() - }; + var system = new StarSystemConfig() + { + entryPositions = entryPositions? + .Select((pair) => new EntryPositionInfo() { id = pair.Key, position = pair.Value }) + .ToArray(), + curiosities = curiousityColours? + .Select((pair) => new CuriosityColorInfo() { id = pair.Key, color = MColor.FromColor(pair.Value.colour), highlightColor = MColor.FromColor(pair.Value.highlight) }) + .ToArray() + }; - Main.Instance.LoadStarSystemConfig(starSystem, system, null, mod); + Main.Instance.LoadStarSystemConfig(starSystem, system, null, mod); - RumorModeBuilder.AddShipLogXML(GameObject.FindObjectOfType(), xml, body); - } + RumorModeBuilder.AddShipLogXML(GameObject.FindObjectOfType(), xml, body); + } - /// - /// Register your own builder that will act on the given GameObject by reading its raw json string - /// - /// - public void RegisterCustomBuilder(Action builder) => PlanetCreationHandler.CustomBuilders.Add(builder); + /// + /// Register your own builder that will act on the given GameObject by reading its raw json string + /// + /// + public void RegisterCustomBuilder(Action builder) => PlanetCreationHandler.CustomBuilders.Add(builder); - public string GetTranslationForShipLog(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.SHIPLOG); + public string GetTranslationForShipLog(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.SHIPLOG); - public string GetTranslationForDialogue(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.DIALOGUE); + public string GetTranslationForDialogue(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.DIALOGUE); - public string GetTranslationForUI(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.UI); + public string GetTranslationForUI(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.UI); - public string GetTranslationForOtherText(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.OTHER); + public string GetTranslationForOtherText(string text) => TranslationHandler.GetTranslation(text, TranslationHandler.TextType.OTHER); + } } diff --git a/NewHorizons/Utility/Files/AssetBundleUtilities.cs b/NewHorizons/Utility/Files/AssetBundleUtilities.cs index 83bf14610..72bb05c2f 100644 --- a/NewHorizons/Utility/Files/AssetBundleUtilities.cs +++ b/NewHorizons/Utility/Files/AssetBundleUtilities.cs @@ -6,144 +6,145 @@ using System.Linq; using UnityEngine; -namespace NewHorizons.Utility.Files; - -public static class AssetBundleUtilities +namespace NewHorizons.Utility.Files { - public static Dictionary AssetBundles = new Dictionary(); - - public static void ClearCache() + public static class AssetBundleUtilities { - foreach (var pair in AssetBundles) + public static Dictionary AssetBundles = new Dictionary(); + + public static void ClearCache() { - if (!pair.Value.keepLoaded) + foreach (var pair in AssetBundles) { - if (pair.Value.bundle == null) - { - NHLogger.LogError($"The asset bundle for {pair.Key} was null when trying to unload"); - } - else + if (!pair.Value.keepLoaded) { - pair.Value.bundle.Unload(true); + if (pair.Value.bundle == null) + { + NHLogger.LogError($"The asset bundle for {pair.Key} was null when trying to unload"); + } + else + { + pair.Value.bundle.Unload(true); + } } - } + } + AssetBundles = AssetBundles.Where(x => x.Value.keepLoaded).ToDictionary(x => x.Key, x => x.Value); } - AssetBundles = AssetBundles.Where(x => x.Value.keepLoaded).ToDictionary(x => x.Key, x => x.Value); - } - public static void PreloadBundle(string assetBundleRelativeDir, IModBehaviour mod) - { - string key = Path.GetFileName(assetBundleRelativeDir); - var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); - var request = AssetBundle.LoadFromFileAsync(completePath); - request.completed += _ => + public static void PreloadBundle(string assetBundleRelativeDir, IModBehaviour mod) { - NHLogger.Log($"Finished loading async bundle {assetBundleRelativeDir}"); - AssetBundles[key] = (request.assetBundle, true); - }; - } - - public static T Load(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) where T : UnityEngine.Object - { - string key = Path.GetFileName(assetBundleRelativeDir); - T obj; + string key = Path.GetFileName(assetBundleRelativeDir); + var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); + var request = AssetBundle.LoadFromFileAsync(completePath); + request.completed += _ => + { + NHLogger.Log($"Finished loading async bundle {assetBundleRelativeDir}"); + AssetBundles[key] = (request.assetBundle, true); + }; + } - try + public static T Load(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) where T : UnityEngine.Object { - AssetBundle bundle; + string key = Path.GetFileName(assetBundleRelativeDir); + T obj; - if (AssetBundles.ContainsKey(key)) - { - bundle = AssetBundles[key].bundle; - } - else + try { - var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); - bundle = AssetBundle.LoadFromFile(completePath); - if (bundle == null) + AssetBundle bundle; + + if (AssetBundles.ContainsKey(key)) { - NHLogger.LogError($"Couldn't load AssetBundle at [{completePath}] for [{mod.ModHelper.Manifest.Name}]"); - return null; + bundle = AssetBundles[key].bundle; + } + else + { + var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); + bundle = AssetBundle.LoadFromFile(completePath); + if (bundle == null) + { + NHLogger.LogError($"Couldn't load AssetBundle at [{completePath}] for [{mod.ModHelper.Manifest.Name}]"); + return null; + } + + AssetBundles[key] = (bundle, false); } - AssetBundles[key] = (bundle, false); + obj = bundle.LoadAsset(pathInBundle); + } + catch (Exception e) + { + NHLogger.LogError($"Couldn't load asset {pathInBundle} from AssetBundle {assetBundleRelativeDir}:\n{e}"); + return null; } - obj = bundle.LoadAsset(pathInBundle); - } - catch (Exception e) - { - NHLogger.LogError($"Couldn't load asset {pathInBundle} from AssetBundle {assetBundleRelativeDir}:\n{e}"); - return null; + return obj; } - return obj; - } - - public static GameObject LoadPrefab(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) - { - var prefab = Load(assetBundleRelativeDir, pathInBundle, mod); + public static GameObject LoadPrefab(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) + { + var prefab = Load(assetBundleRelativeDir, pathInBundle, mod); - prefab.SetActive(false); + prefab.SetActive(false); - ReplaceShaders(prefab); + ReplaceShaders(prefab); - return prefab; - } + return prefab; + } - public static void ReplaceShaders(GameObject prefab) - { - foreach (var renderer in prefab.GetComponentsInChildren(true)) + public static void ReplaceShaders(GameObject prefab) { - foreach (var material in renderer.sharedMaterials) + foreach (var renderer in prefab.GetComponentsInChildren(true)) { - if (material == null) continue; - - var replacementShader = Shader.Find(material.shader.name); - if (replacementShader == null) continue; - - // preserve override tag and render queue (for Standard shader) - // keywords and properties are already preserved - if (material.renderQueue != material.shader.renderQueue) - { - var renderType = material.GetTag("RenderType", false); - var renderQueue = material.renderQueue; - material.shader = replacementShader; - material.SetOverrideTag("RenderType", renderType); - material.renderQueue = renderQueue; - } - else + foreach (var material in renderer.sharedMaterials) { - material.shader = replacementShader; + if (material == null) continue; + + var replacementShader = Shader.Find(material.shader.name); + if (replacementShader == null) continue; + + // preserve override tag and render queue (for Standard shader) + // keywords and properties are already preserved + if (material.renderQueue != material.shader.renderQueue) + { + var renderType = material.GetTag("RenderType", false); + var renderQueue = material.renderQueue; + material.shader = replacementShader; + material.SetOverrideTag("RenderType", renderType); + material.renderQueue = renderQueue; + } + else + { + material.shader = replacementShader; + } } } - } - foreach (var trenderer in prefab.GetComponentsInChildren(true)) - { - foreach (var material in trenderer.sharedMaterials) + foreach (var trenderer in prefab.GetComponentsInChildren(true)) { - if (material == null) continue; - - var replacementShader = Shader.Find(material.shader.name); - if (replacementShader == null) continue; - - // preserve override tag and render queue (for Standard shader) - // keywords and properties are already preserved - if (material.renderQueue != material.shader.renderQueue) - { - var renderType = material.GetTag("RenderType", false); - var renderQueue = material.renderQueue; - material.shader = replacementShader; - material.SetOverrideTag("RenderType", renderType); - material.renderQueue = renderQueue; - } - else + foreach (var material in trenderer.sharedMaterials) { - material.shader = replacementShader; + if (material == null) continue; + + var replacementShader = Shader.Find(material.shader.name); + if (replacementShader == null) continue; + + // preserve override tag and render queue (for Standard shader) + // keywords and properties are already preserved + if (material.renderQueue != material.shader.renderQueue) + { + var renderType = material.GetTag("RenderType", false); + var renderQueue = material.renderQueue; + material.shader = replacementShader; + material.SetOverrideTag("RenderType", renderType); + material.renderQueue = renderQueue; + } + else + { + material.shader = replacementShader; + } } } } } -} +} \ No newline at end of file From e73afbebc713ccae33f4e224a47f275a6195c99d Mon Sep 17 00:00:00 2001 From: JohnCorby Date: Sat, 3 Feb 2024 15:20:51 -0800 Subject: [PATCH 10/10] wait until preload bundles are loaded before loading scene --- .../SubmitActionLoadScenePatches.cs | 49 ++++++++++++++++++- .../Utility/Files/AssetBundleUtilities.cs | 14 +++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/NewHorizons/Patches/EyeScenePatches/SubmitActionLoadScenePatches.cs b/NewHorizons/Patches/EyeScenePatches/SubmitActionLoadScenePatches.cs index 9feba1693..891f439ce 100644 --- a/NewHorizons/Patches/EyeScenePatches/SubmitActionLoadScenePatches.cs +++ b/NewHorizons/Patches/EyeScenePatches/SubmitActionLoadScenePatches.cs @@ -1,4 +1,5 @@ using HarmonyLib; +using NewHorizons.Utility.Files; using NewHorizons.Utility.OWML; namespace NewHorizons.Patches.EyeScenePatches @@ -6,10 +7,18 @@ namespace NewHorizons.Patches.EyeScenePatches [HarmonyPatch(typeof(SubmitActionLoadScene))] public static class SubmitActionLoadScenePatches { + // To call the base method + [HarmonyReversePatch] + [HarmonyPatch(typeof(SubmitActionConfirm), nameof(SubmitActionConfirm.ConfirmSubmit))] + public static void SubmitActionConfirm_ConfirmSubmit(SubmitActionConfirm instance) { } + + [HarmonyPrefix] [HarmonyPatch(nameof(SubmitActionLoadScene.ConfirmSubmit))] - public static void SubmitActionLoadScene_ConfirmSubmit(SubmitActionLoadScene __instance) + public static bool SubmitActionLoadScene_ConfirmSubmit(SubmitActionLoadScene __instance) { + if (__instance._receivedSubmitAction) return false; + // Title screen can warp you to eye and cause problems. if (__instance._sceneToLoad == SubmitActionLoadScene.LoadableScenes.EYE) { @@ -17,6 +26,44 @@ public static void SubmitActionLoadScene_ConfirmSubmit(SubmitActionLoadScene __i Main.Instance.IsWarpingBackToEye = true; __instance._sceneToLoad = SubmitActionLoadScene.LoadableScenes.GAME; } + + // modified from patched function + SubmitActionConfirm_ConfirmSubmit(__instance); + __instance._receivedSubmitAction = true; + Locator.GetMenuInputModule().DisableInputs(); + + Delay.RunWhen(() => + { + // update text. just use 0% + __instance.ResetStringBuilder(); + __instance._nowLoadingSB.Append(UITextLibrary.GetString(UITextType.LoadingMessage)); + __instance._nowLoadingSB.Append(0.ToString("P0")); + __instance._loadingText.text = __instance._nowLoadingSB.ToString(); + + return AssetBundleUtilities.AreRequiredAssetsLoaded(); + }, () => + { + switch (__instance._sceneToLoad) + { + case SubmitActionLoadScene.LoadableScenes.GAME: + LoadManager.LoadSceneAsync(OWScene.SolarSystem, false, LoadManager.FadeType.ToBlack, 1f, false); + __instance.ResetStringBuilder(); + __instance._waitingOnStreaming = true; + break; + case SubmitActionLoadScene.LoadableScenes.EYE: + LoadManager.LoadSceneAsync(OWScene.EyeOfTheUniverse, true, LoadManager.FadeType.ToBlack, 1f, false); + __instance.ResetStringBuilder(); + break; + case SubmitActionLoadScene.LoadableScenes.TITLE: + LoadManager.LoadScene(OWScene.TitleScreen, LoadManager.FadeType.ToBlack, 2f, true); + break; + case SubmitActionLoadScene.LoadableScenes.CREDITS: + LoadManager.LoadScene(OWScene.Credits_Fast, LoadManager.FadeType.ToBlack, 1f, false); + break; + } + }); + + return false; } } } diff --git a/NewHorizons/Utility/Files/AssetBundleUtilities.cs b/NewHorizons/Utility/Files/AssetBundleUtilities.cs index 72bb05c2f..104d73931 100644 --- a/NewHorizons/Utility/Files/AssetBundleUtilities.cs +++ b/NewHorizons/Utility/Files/AssetBundleUtilities.cs @@ -10,7 +10,9 @@ namespace NewHorizons.Utility.Files { public static class AssetBundleUtilities { - public static Dictionary AssetBundles = new Dictionary(); + public static Dictionary AssetBundles = new(); + + private static readonly List _loadingBundles = new(); public static void ClearCache() { @@ -37,13 +39,21 @@ public static void PreloadBundle(string assetBundleRelativeDir, IModBehaviour mo string key = Path.GetFileName(assetBundleRelativeDir); var completePath = Path.Combine(mod.ModHelper.Manifest.ModFolderPath, assetBundleRelativeDir); var request = AssetBundle.LoadFromFileAsync(completePath); + _loadingBundles.Add(request); + NHLogger.Log($"Preloading bundle {assetBundleRelativeDir} - {_loadingBundles.Count} left"); request.completed += _ => { - NHLogger.Log($"Finished loading async bundle {assetBundleRelativeDir}"); + _loadingBundles.Remove(request); + NHLogger.Log($"Finshed preloading bundle {assetBundleRelativeDir} - {_loadingBundles.Count} left"); AssetBundles[key] = (request.assetBundle, true); }; } + /// + /// are preloaded bundles done loading? + /// + public static bool AreRequiredAssetsLoaded() => _loadingBundles.Count == 0; + public static T Load(string assetBundleRelativeDir, string pathInBundle, IModBehaviour mod) where T : UnityEngine.Object { string key = Path.GetFileName(assetBundleRelativeDir);