diff --git a/CHANGELOG.md b/CHANGELOG.md index 62d7b73d..abf2fdae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * [Fix #301] "volumeOverride" is not working . * [Fix #297] IMC-15K has wrong part volume. * [Fix #302] Structurel panel has wrong nodes. +* [Fix #303] Game's crashing on the new parts. # 1.17 (January 28th, 2019): * KSP 1.6 support. This mod's version is not compatible with the prior versions of KSP! diff --git a/Source/KIS_IconViewer.cs b/Source/KIS_IconViewer.cs index 2a9c1833..7693da38 100644 --- a/Source/KIS_IconViewer.cs +++ b/Source/KIS_IconViewer.cs @@ -1,4 +1,5 @@ using KISAPIv1; +using KSPDev.LogUtils; using KSPDev.PartUtils; using System; using System.Linq; @@ -6,7 +7,7 @@ namespace KIS { -public sealed class KIS_IconViewer : IDisposable { + public sealed class KIS_IconViewer { const float IconPosY = 0; const int CameraLayer = 22; const float LightIntensity = 0.4f; @@ -14,12 +15,13 @@ public sealed class KIS_IconViewer : IDisposable { const float RotationsPerSecond = 0.20f; // Full round in 5 seconds. Camera camera; - int cameraShift; + float cameraShift; GameObject iconPrefab; + bool disposed; static Light iconLight; - static int cameraGlobalShift; static int iconCount; + static float globalCameraShift; public Texture texture { get; private set; } @@ -29,39 +31,45 @@ public sealed class KIS_IconViewer : IDisposable { } else { MakePartIcon(part.partInfo, resolution, VariantsUtils.GetCurrentPartVariant(part)); } - iconCount += 1; } public KIS_IconViewer(AvailablePart avPart, int resolution, PartVariant variant) { MakePartIcon(avPart, resolution, variant); - iconCount += 1; } + /// Warns if the icon is not disposed properly. + /// + /// This method cannot release the Unity resources since teh access to them is only allowed from + /// the Unity main thread. The best thing this method can do is spamming log errors. + /// ~KIS_IconViewer() { - if (camera != null) { - Dispose(); + if (!disposed) { + DebugEx.Error("RESOURCES LEAK! The IconViewer was not disposed: camera={0}, iconPrefab={1}", + camera, iconPrefab); } } + + /// Releases all the used resources. + /// + /// This method must be called if an icon becomes unusable. Otherwise, all the cached Unity + /// objects will live and take memory till the scene is reloaded. Some of the internal counters + /// will also not get updated as expected. Simply put, jut call it! + /// It's safe to call this method multiple times. + /// + public void Dispose() { + if (!disposed) { + if (camera != null) { + UnityEngine.Object.Destroy(camera.gameObject); + } + if (iconPrefab != null) { + UnityEngine.Object.Destroy(iconPrefab); + } + ReleaseCameraSpot(); - // The Dispose() method MUST be called instead of garbage-collecting icon instances - // because we can only access the cam.gameObject member from the main thread. - public void Dispose() - { - if (camera != null) { - UnityEngine.Object.DestroyImmediate(camera.gameObject); camera = null; - } - if (iconPrefab != null) { - UnityEngine.Object.DestroyImmediate(iconPrefab); iconPrefab = null; - } - if (texture != null) { - (texture as RenderTexture).Release(); texture = null; - } - iconCount -= 1; - if (iconCount == 0) { - cameraGlobalShift = 0; + disposed = true; } } @@ -78,10 +86,6 @@ public void Dispose() camera.Render(); // Update snapshot. } - static void ResetCamIndex() { - cameraGlobalShift = 0; - } - #region Local utility methods void SetLayerRecursively(GameObject obj, int newLayer) { if (null == obj) { @@ -98,7 +102,8 @@ public void Dispose() void MakeKerbalAvatar(Part ownerPart, int resolution) { // Icon Camera - GameObject camGo = new GameObject("KASCamItem" + cameraGlobalShift); + cameraShift = ReserveCameraSpot(); + GameObject camGo = new GameObject("KASCamItem" + cameraShift); camGo.transform.parent = ownerPart.transform; camGo.transform.localPosition = Vector3.zero + new Vector3(0, 0.35f, 0.7f); camGo.transform.localRotation = Quaternion.identity; @@ -134,8 +139,9 @@ public void Dispose() } // Icon Camera - GameObject camGo = new GameObject("KASCamItem" + cameraGlobalShift); - camGo.transform.position = new Vector3(cameraGlobalShift, IconPosY, 0); + cameraShift = ReserveCameraSpot(); + GameObject camGo = new GameObject("KASCamItem" + cameraShift); + camGo.transform.position = new Vector3(cameraShift, IconPosY, 0); camGo.transform.rotation = Quaternion.identity; camera = camGo.AddComponent(); camera.orthographic = true; @@ -146,6 +152,25 @@ public void Dispose() RenderTexture tex = new RenderTexture(resolution, resolution, 8); texture = tex; + // Layer + camera.cullingMask = 1 << CameraLayer; + SetLayerRecursively(iconPrefab, CameraLayer); + + // Texture + camera.targetTexture = tex; + camera.ResetAspect(); + + ResetPos(); + } + + void ReleaseCameraSpot() { + if (--iconCount == 0) { + globalCameraShift = 0; + DebugEx.Fine("Icon camera global shift is reset to zero"); + } + } + + float ReserveCameraSpot() { //light if (iconLight == null && HighLogic.LoadedSceneIsFlight) { GameObject lightGo = new GameObject("KASLight"); @@ -157,18 +182,10 @@ public void Dispose() iconLight.renderMode = LightRenderMode.ForcePixel; } - // Layer - camera.cullingMask = 1 << CameraLayer; - SetLayerRecursively(iconPrefab, CameraLayer); - - // Texture - camera.targetTexture = tex; - camera.ResetAspect(); - - // Cam index - cameraShift = cameraGlobalShift; - cameraGlobalShift += 2; - ResetPos(); + iconCount++; + globalCameraShift += 2.0f; + + return globalCameraShift; } #endregion } diff --git a/Source/ModuleKISInventory.cs b/Source/ModuleKISInventory.cs index 8a02acb1..c4059a86 100644 --- a/Source/ModuleKISInventory.cs +++ b/Source/ModuleKISInventory.cs @@ -868,6 +868,7 @@ public enum InventoryType { GameEvents.OnHelmetChanged.Remove(OnHelmetChanged); GameEvents.onPartActionUICreate.Remove(OnPartActionMenuCreate); GameEvents.onEditorVariantApplied.Remove(OnPartVariandChanged); + DisableIcon(); // Release the resources! } #endregion