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