From 35f0e80df44f520f9637c20a9bc888d729b75a4e Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 18 Jul 2023 00:13:19 -0400 Subject: [PATCH 01/10] Allow bramble dimensions to have gravity --- NewHorizons/Handlers/PlanetCreationHandler.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NewHorizons/Handlers/PlanetCreationHandler.cs b/NewHorizons/Handlers/PlanetCreationHandler.cs index 98d55efa..aa2f480e 100644 --- a/NewHorizons/Handlers/PlanetCreationHandler.cs +++ b/NewHorizons/Handlers/PlanetCreationHandler.cs @@ -349,6 +349,13 @@ namespace NewHorizons.Handlers BrambleDimensionBuilder.Make(body, go, ao, sector, owRigidBody); go = SharedGenerateBody(body, go, sector, owRigidBody); + + // Not included in SharedGenerate to not mess up gravity on base game planets + if (body.Config.Base.surfaceGravity != 0) + { + GravityBuilder.Make(go, ao, owRigidBody, body.Config); + } + body.Object = go; AstroObjectLocator.RegisterCustomAstroObject(ao); From 13c0bdeec59e75b08258e72c81fb08569038b808 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 18 Jul 2023 00:25:02 -0400 Subject: [PATCH 02/10] Prevent onsystemready being called twice, also dont call it from main menu --- NewHorizons/Main.cs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/NewHorizons/Main.cs b/NewHorizons/Main.cs index c24b4b03..babd5acf 100644 --- a/NewHorizons/Main.cs +++ b/NewHorizons/Main.cs @@ -230,6 +230,7 @@ namespace NewHorizons NHLogger.LogWarning("Couldn't find planets folder"); } + // Call this from the menu since we hadn't hooked onto the event yet Delay.FireOnNextUpdate(() => OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single)); Delay.FireOnNextUpdate(() => _firstLoad = false); Instance.ModHelper.Menus.PauseMenu.OnInit += DebugReload.InitializePauseMenu; @@ -545,6 +546,9 @@ namespace NewHorizons { NHLogger.LogError($"Exception thrown when invoking star system loaded event with parameter [{Instance.CurrentStarSystem}]:\n{e}"); } + + // Wait for player to be awake and also for frames to pass + Delay.RunWhenAndInNUpdates(() => OnSystemReady(DidWarpFromShip, DidWarpFromVessel), () => _playerAwake && PlayerSpawned, 30); } else { @@ -560,29 +564,33 @@ namespace NewHorizons _currentStarSystem = _defaultStarSystem; } } - - // Wait for player to be awake and also for frames to pass - Delay.RunWhenAndInNUpdates(() => OnSystemReady(DidWarpFromShip, DidWarpFromVessel), () => _playerAwake && PlayerSpawned, 30); } // Had a bunch of separate unity things firing stuff when the system is ready so I moved it all to here private void OnSystemReady(bool shouldWarpInFromShip, bool shouldWarpInFromVessel) { - IsSystemReady = true; + if (IsSystemReady) + { + NHLogger.LogWarning("OnSystemReady was called twice."); + } + else + { + IsSystemReady = true; - // ShipWarpController will handle the invulnerability otherwise - if (!shouldWarpInFromShip) - Delay.FireOnNextUpdate(() => InvulnerabilityHandler.MakeInvulnerable(false)); + // ShipWarpController will handle the invulnerability otherwise + if (!shouldWarpInFromShip) + Delay.FireOnNextUpdate(() => InvulnerabilityHandler.MakeInvulnerable(false)); - Locator.GetPlayerBody().gameObject.AddComponent(); - Locator.GetPlayerBody().gameObject.AddComponent(); - Locator.GetPlayerBody().gameObject.AddComponent(); + Locator.GetPlayerBody().gameObject.AddComponent(); + Locator.GetPlayerBody().gameObject.AddComponent(); + Locator.GetPlayerBody().gameObject.AddComponent(); - PlayerSpawnHandler.OnSystemReady(shouldWarpInFromShip, shouldWarpInFromVessel); + PlayerSpawnHandler.OnSystemReady(shouldWarpInFromShip, shouldWarpInFromVessel); - VesselCoordinatePromptHandler.RegisterPrompts(SystemDict.Where(system => system.Value.Config.Vessel?.coords != null).Select(x => x.Value).ToList()); + VesselCoordinatePromptHandler.RegisterPrompts(SystemDict.Where(system => system.Value.Config.Vessel?.coords != null).Select(x => x.Value).ToList()); - CloakHandler.OnSystemReady(); + CloakHandler.OnSystemReady(); + } } public void EnableWarpDrive() From f05e2be30a86f2b3358e06ccecfd45faf5a6a4e0 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 18 Jul 2023 00:49:47 -0400 Subject: [PATCH 03/10] Clear all coroutines/delays on scene unload --- .../Components/Volumes/LoadCreditsVolume.cs | 3 +- NewHorizons/Handlers/FadeHandler.cs | 5 ++- NewHorizons/Handlers/PlayerSpawnHandler.cs | 2 +- NewHorizons/Utility/OWML/Delay.cs | 43 +++++++++++++++++-- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/NewHorizons/Components/Volumes/LoadCreditsVolume.cs b/NewHorizons/Components/Volumes/LoadCreditsVolume.cs index 3fc191f2..18edcbd1 100644 --- a/NewHorizons/Components/Volumes/LoadCreditsVolume.cs +++ b/NewHorizons/Components/Volumes/LoadCreditsVolume.cs @@ -1,5 +1,6 @@ using NewHorizons.External.SerializableEnums; using NewHorizons.Handlers; +using NewHorizons.Utility; using NewHorizons.Utility.OWML; using System.Collections; using UnityEngine; @@ -28,7 +29,7 @@ namespace NewHorizons.Components.Volumes if (hitObj.CompareTag("PlayerDetector") && enabled) { // Have to run it off the mod behaviour since the game over controller disables everything - Main.Instance.StartCoroutine(GameOver()); + Delay.StartCoroutine(GameOver()); } } diff --git a/NewHorizons/Handlers/FadeHandler.cs b/NewHorizons/Handlers/FadeHandler.cs index 9316030a..fbaa0b5b 100644 --- a/NewHorizons/Handlers/FadeHandler.cs +++ b/NewHorizons/Handlers/FadeHandler.cs @@ -1,3 +1,4 @@ +using NewHorizons.Utility.OWML; using System; using System.Collections; using UnityEngine; @@ -6,7 +7,7 @@ namespace NewHorizons.Handlers { public static class FadeHandler { - public static void FadeOut(float length) => Main.Instance.StartCoroutine(FadeOutCoroutine(length)); + public static void FadeOut(float length) => Delay.StartCoroutine(FadeOutCoroutine(length)); private static IEnumerator FadeOutCoroutine(float length) { @@ -24,7 +25,7 @@ namespace NewHorizons.Handlers yield return new WaitForEndOfFrame(); } - public static void FadeThen(float length, Action action) => Main.Instance.StartCoroutine(FadeThenCoroutine(length, action)); + public static void FadeThen(float length, Action action) => Delay.StartCoroutine(FadeThenCoroutine(length, action)); private static IEnumerator FadeThenCoroutine(float length, Action action) { diff --git a/NewHorizons/Handlers/PlayerSpawnHandler.cs b/NewHorizons/Handlers/PlayerSpawnHandler.cs index 8d70907b..499a4d7f 100644 --- a/NewHorizons/Handlers/PlayerSpawnHandler.cs +++ b/NewHorizons/Handlers/PlayerSpawnHandler.cs @@ -40,7 +40,7 @@ namespace NewHorizons.Handlers var matchInitialMotion = SearchUtilities.Find("Player_Body").GetComponent(); if (matchInitialMotion != null) UnityEngine.Object.Destroy(matchInitialMotion); - Main.Instance.StartCoroutine(SpawnCoroutine(2)); + Delay.StartCoroutine(SpawnCoroutine(2)); } } diff --git a/NewHorizons/Utility/OWML/Delay.cs b/NewHorizons/Utility/OWML/Delay.cs index 979712b7..e25dc928 100644 --- a/NewHorizons/Utility/OWML/Delay.cs +++ b/NewHorizons/Utility/OWML/Delay.cs @@ -1,15 +1,49 @@ using System; using System.Collections; using UnityEngine; +using UnityEngine.SceneManagement; namespace NewHorizons.Utility.OWML { public static class Delay { - public static void RunWhen(Func predicate, Action action) => Main.Instance.ModHelper.Events.Unity.RunWhen(predicate, action); - public static void FireInNUpdates(Action action, int n) => Main.Instance.ModHelper.Events.Unity.FireInNUpdates(action, n); - public static void FireOnNextUpdate(Action action) => Main.Instance.ModHelper.Events.Unity.FireOnNextUpdate(action); - public static void RunWhenAndInNUpdates(Action action, Func predicate, int n) => Main.Instance.StartCoroutine(RunWhenOrInNUpdatesCoroutine(action, predicate, n)); + #region OnSceneUnloaded + static Delay() => SceneManager.sceneUnloaded += OnSceneUnloaded; + + private static void OnSceneUnloaded(Scene _) => Main.Instance.StopAllCoroutines(); + #endregion + + #region public methods + public static void StartCoroutine(IEnumerator coroutine) => Main.Instance.StartCoroutine(coroutine); + + public static void RunWhen(Func predicate, Action action) => StartCoroutine(RunWhenCoroutine(action, predicate)); + + public static void FireInNUpdates(Action action, int n) => StartCoroutine(FireInNUpdatesCoroutine(action, n)); + + public static void FireOnNextUpdate(Action action) => FireInNUpdates(action, 1); + + public static void RunWhenAndInNUpdates(Action action, Func predicate, int n) => Delay.StartCoroutine(RunWhenOrInNUpdatesCoroutine(action, predicate, n)); + #endregion + + #region Coroutines + private static IEnumerator RunWhenCoroutine(Action action, Func predicate) + { + while (!predicate.Invoke()) + { + yield return new WaitForEndOfFrame(); + } + + action.Invoke(); + } + + private static IEnumerator FireInNUpdatesCoroutine(Action action, int n) + { + for (int i = 0; i < n; i++) + { + yield return new WaitForEndOfFrame(); + } + action?.Invoke(); + } private static IEnumerator RunWhenOrInNUpdatesCoroutine(Action action, Func predicate, int n) { @@ -24,5 +58,6 @@ namespace NewHorizons.Utility.OWML action.Invoke(); } + #endregion } } From 64efe3651198b7851fd865367166b2d6bd4decda Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 18 Jul 2023 01:08:59 -0400 Subject: [PATCH 04/10] Initial planet rotation. Fixes #360 --- NewHorizons/Builder/Orbital/InitialMotionBuilder.cs | 4 +++- NewHorizons/External/Modules/OrbitModule.cs | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs b/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs index c6405bd9..420fa2bf 100644 --- a/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs +++ b/NewHorizons/Builder/Orbital/InitialMotionBuilder.cs @@ -24,10 +24,12 @@ namespace NewHorizons.Builder.Orbital initialMotion._initAngularSpeed = orbit.siderealPeriod == 0 ? 0f : 2f * Mathf.PI / (orbit.siderealPeriod * 60f); var rotationAxis = Quaternion.AngleAxis(orbit.axialTilt, Vector3.right) * Vector3.up; - // For things with children this is broken + // For stock planets with unsuspended rigidbody children this is broken + // For planets with rafts this is broken if (AstroObjectLocator.GetChildren(secondaryBody).Length == 0) { secondaryBody.transform.rotation = Quaternion.FromToRotation(Vector3.up, rotationAxis); + secondaryBody.transform.Rotate(rotationAxis, orbit.initialRotation); } if (!orbit.isStatic && primaryBody != null) diff --git a/NewHorizons/External/Modules/OrbitModule.cs b/NewHorizons/External/Modules/OrbitModule.cs index 84fa1d89..e3a6a566 100644 --- a/NewHorizons/External/Modules/OrbitModule.cs +++ b/NewHorizons/External/Modules/OrbitModule.cs @@ -34,6 +34,12 @@ namespace NewHorizons.External.Modules /// public float siderealPeriod; + /// + /// Offsets the planet's starting sidereal rotation. In degrees. + /// + [Range(0f, 360f)] + public float initialRotation; + /// /// Should the body always have one side facing its primary? /// From 0163f8c822e61e86238050f1bfe72ad92cd6aae7 Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 18 Jul 2023 05:10:55 +0000 Subject: [PATCH 05/10] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index ec905995..a67d997d 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -1034,6 +1034,13 @@ "description": "Rotation period in minutes.", "format": "float" }, + "initialRotation": { + "type": "number", + "description": "Offsets the planet's starting sidereal rotation. In degrees.", + "format": "float", + "maximum": 360.0, + "minimum": 0.0 + }, "isTidallyLocked": { "type": "boolean", "description": "Should the body always have one side facing its primary?" From 40f2480090cc4846c533d8a6a5fc5e0504791616 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 18 Jul 2023 01:20:48 -0400 Subject: [PATCH 06/10] Add option for solar flare size. Implements #369 --- NewHorizons/Builder/Body/StarBuilder.cs | 1 + NewHorizons/External/Modules/VariableSize/StarModule.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index 53379f40..76f0f59a 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -363,6 +363,7 @@ namespace NewHorizons.Builder.Body controller.GetComponent().sharedMaterial = material; controller._color = Color.white; controller._tint = flareTint; + controller._scaleFactor = Vector3.one * starModule.solarFlareScaleFactor; } } diff --git a/NewHorizons/External/Modules/VariableSize/StarModule.cs b/NewHorizons/External/Modules/VariableSize/StarModule.cs index 3476b867..fe35f965 100644 --- a/NewHorizons/External/Modules/VariableSize/StarModule.cs +++ b/NewHorizons/External/Modules/VariableSize/StarModule.cs @@ -104,6 +104,11 @@ namespace NewHorizons.External.Modules.VariableSize /// The type of stellar remnant your star will leave behind. /// [DefaultValue("default")] public StellarRemnantType stellarRemnantType = StellarRemnantType.Default; + + /// + /// Size multiuplier for solar flares. Defaults to 1. + /// + public float solarFlareScaleFactor = 1f; } [JsonConverter(typeof(StringEnumConverter))] From 00a67e243f96017c83c2737255a33979dfa8e55c Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 18 Jul 2023 05:23:53 +0000 Subject: [PATCH 07/10] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index a67d997d..c1442778 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -3213,6 +3213,11 @@ "description": "The type of stellar remnant your star will leave behind.", "default": "default", "$ref": "#/definitions/StellarRemnantType" + }, + "solarFlareScaleFactor": { + "type": "number", + "description": "Size multiuplier for solar flares. Defaults to 1.", + "format": "float" } } }, From 94441dcc4ba8700b0993d6c8e424dbb1573db485 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 18 Jul 2023 01:48:11 -0400 Subject: [PATCH 08/10] Adds solar flare module for scale, life length, time between flares. Implements #368 --- NewHorizons/Builder/Body/StarBuilder.cs | 36 +++++++++++++------ .../Modules/VariableSize/StarModule.cs | 32 +++++++++++++++-- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/NewHorizons/Builder/Body/StarBuilder.cs b/NewHorizons/Builder/Body/StarBuilder.cs index 76f0f59a..f4954af5 100644 --- a/NewHorizons/Builder/Body/StarBuilder.cs +++ b/NewHorizons/Builder/Body/StarBuilder.cs @@ -7,6 +7,7 @@ using System.Linq; using NewHorizons.Components.Stars; using NewHorizons.Utility.OuterWilds; using NewHorizons.Utility.Files; +using NewHorizons.Utility.OWML; namespace NewHorizons.Builder.Body { @@ -348,29 +349,42 @@ namespace NewHorizons.Builder.Body solarFlareEmitter.name = "SolarFlareEmitter"; solarFlareEmitter.SetActive(true); + var emitter = solarFlareEmitter.GetComponent(); + + if (starModule.solarFlareSettings != null) + { + emitter._minTimeBetweenFlares = starModule.solarFlareSettings.minTimeBetweenFlares; + emitter._maxTimeBetweenFlares = starModule.solarFlareSettings.maxTimeBetweenFlares; + emitter._lifeLength = starModule.solarFlareSettings.lifeLength; + } + if (starModule.tint != null) { - var flareTint = starModule.tint.ToColor(); - var emitter = solarFlareEmitter.GetComponent(); - emitter.tint = flareTint; + emitter.tint = starModule.tint.ToColor(); + } - var material = new Material(_flareMaterial); - // Since the star isn't awake yet the controllers haven't been made - foreach (var prefab in new GameObject[] { emitter.domePrefab, emitter.loopPrefab, emitter.streamerPrefab }) + var material = new Material(_flareMaterial); + // Since the star isn't awake yet the controllers haven't been made + foreach (var prefab in new GameObject[] { emitter.domePrefab, emitter.loopPrefab, emitter.streamerPrefab }) + { + var controller = prefab.GetComponent(); + // controller._meshRenderer doesn't exist yet since Awake hasn't been called + if (starModule.tint != null) { - var controller = prefab.GetComponent(); - // controller._meshRenderer doesn't exist yet since Awake hasn't been called controller.GetComponent().sharedMaterial = material; controller._color = Color.white; - controller._tint = flareTint; - controller._scaleFactor = Vector3.one * starModule.solarFlareScaleFactor; + controller._tint = starModule.tint.ToColor(); + } + if (starModule.solarFlareSettings != null) + { + controller._scaleFactor = Vector3.one * starModule.solarFlareSettings.scaleFactor; } } starGO.transform.position = rootObject.transform.position; starGO.transform.localScale = starModule.size * Vector3.one; - TessellatedSphereRenderer surface = sunSurface.GetComponent(); + var surface = sunSurface.GetComponent(); if (starModule.tint != null) { diff --git a/NewHorizons/External/Modules/VariableSize/StarModule.cs b/NewHorizons/External/Modules/VariableSize/StarModule.cs index fe35f965..3b833051 100644 --- a/NewHorizons/External/Modules/VariableSize/StarModule.cs +++ b/NewHorizons/External/Modules/VariableSize/StarModule.cs @@ -106,9 +106,37 @@ namespace NewHorizons.External.Modules.VariableSize [DefaultValue("default")] public StellarRemnantType stellarRemnantType = StellarRemnantType.Default; /// - /// Size multiuplier for solar flares. Defaults to 1. + /// Allows overriding solar flare graphical settings. /// - public float solarFlareScaleFactor = 1f; + public SolarFlareModule solarFlareSettings; + + [JsonObject] + public class SolarFlareModule + { + /// + /// Size multiuplier for solar flares. Defaults to 1. + /// + [DefaultValue(1)] + public float scaleFactor = 1f; + + /// + /// How long a solar flare is visible for. Defaults to 15. + /// + [DefaultValue(15f)] + public float lifeLength = 15f; + + /// + /// Solar flares are emitted randomly. This is the minimum ammount of time between solar flares. + /// + [DefaultValue(5f)] + public float minTimeBetweenFlares = 5f; + + /// + /// Solar flares are emitted randomly. This is the maximum ammount of time between solar flares. + /// + [DefaultValue(30f)] + public float maxTimeBetweenFlares = 30f; + } } [JsonConverter(typeof(StringEnumConverter))] From 6ca3ee7ae6b28754e8dba3ab30e87dd4751d8351 Mon Sep 17 00:00:00 2001 From: Ben C Date: Tue, 18 Jul 2023 05:50:23 +0000 Subject: [PATCH 09/10] Updated Schemas --- NewHorizons/Schemas/body_schema.json | 37 +++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/NewHorizons/Schemas/body_schema.json b/NewHorizons/Schemas/body_schema.json index c1442778..83e87e10 100644 --- a/NewHorizons/Schemas/body_schema.json +++ b/NewHorizons/Schemas/body_schema.json @@ -3214,10 +3214,9 @@ "default": "default", "$ref": "#/definitions/StellarRemnantType" }, - "solarFlareScaleFactor": { - "type": "number", - "description": "Size multiuplier for solar flares. Defaults to 1.", - "format": "float" + "solarFlareSettings": { + "description": "Allows overriding solar flare graphical settings.", + "$ref": "#/definitions/SolarFlareModule" } } }, @@ -3257,6 +3256,36 @@ "custom" ] }, + "SolarFlareModule": { + "type": "object", + "additionalProperties": false, + "properties": { + "scaleFactor": { + "type": "number", + "description": "Size multiuplier for solar flares. Defaults to 1.", + "format": "float", + "default": 1 + }, + "lifeLength": { + "type": "number", + "description": "How long a solar flare is visible for. Defaults to 15.", + "format": "float", + "default": 15.0 + }, + "minTimeBetweenFlares": { + "type": "number", + "description": "Solar flares are emitted randomly. This is the minimum ammount of time between solar flares.", + "format": "float", + "default": 5.0 + }, + "maxTimeBetweenFlares": { + "type": "number", + "description": "Solar flares are emitted randomly. This is the maximum ammount of time between solar flares.", + "format": "float", + "default": 30.0 + } + } + }, "WaterModule": { "type": "object", "additionalProperties": false, From 6c49034e1e8b68d8b623db9b4b5da1c3ab714caa Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 18 Jul 2023 02:10:32 -0400 Subject: [PATCH 10/10] Allow system images to be in systems or planets folders Fixes #637 --- .../Components/ShipLog/ShipLogStarChartMode.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs index afdf5f2e..cae4a4b9 100644 --- a/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs +++ b/NewHorizons/Components/ShipLog/ShipLogStarChartMode.cs @@ -134,9 +134,18 @@ namespace NewHorizons.Components.ShipLog } else { - var path = Path.Combine("planets", uniqueID + ".png"); + var mod = Main.SystemDict[uniqueID].Mod; + + var path = Path.Combine("systems", uniqueID + ".png"); + + // Else check the old location + if (!File.Exists(Path.Combine(mod.ModHelper.Manifest.ModFolderPath, path))) + { + path = Path.Combine("planets", uniqueID + ".png"); + } + NHLogger.LogVerbose($"ShipLogStarChartManager - Trying to load {path}"); - texture = ImageUtilities.GetTexture(Main.SystemDict[uniqueID].Mod, path); + texture = ImageUtilities.GetTexture(mod, path); } } catch (Exception) { }