Skip to content

Commit 08cfb4f

Browse files
authored
Add support for hot-reloading (#166)
Most of the changes here are just properly unregistering stuff when being destroyed, but there were a few explicit callbacks needed and it also uncovered a number of cases where HotReloadKSP wasn't quite handling things correctly. The explicit new changes to support reload are: * Load SystemHeatSettings when hot-loaded * Destroy the UI when SystemHeatUI is destroyed * We need to explicitly grab the prefab objects from the old assembly. Hot reloading still takes care of replacing any widgets within.
1 parent 486d743 commit 08cfb4f

10 files changed

Lines changed: 68 additions & 6 deletions

Source/EngineerReport/SystemHeatEngineerReport.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,18 @@ public void OnDestroy()
2020
{
2121
GameEvents.onGUIEngineersReportReady.Remove(ReportReady);
2222
GameEvents.onGUIEngineersReportDestroy.Remove(ReportDestroyed);
23+
24+
RemoveTest();
25+
}
26+
27+
// onGUIEngineersReportReady only fires once per editor session, so on a hot
28+
// reload we need to re-add the tests manually here.
29+
private void OnHotReload(MonoBehaviour old)
30+
{
31+
if (EngineersReport.Instance != null)
32+
AddTest();
2333
}
34+
2435
private void AddTest()
2536
{
2637
//Wait for DeltaV simulation to be instantiated and to finish.
@@ -38,6 +49,9 @@ private void AddTest()
3849

3950
private void RemoveTest()
4051
{
52+
// EngineersReport may already be torn down on scene exit; tests are tied
53+
// to its lifetime and don't need to be removed in that case.
54+
if (EngineersReport.Instance == null) return;
4155

4256
//Only if it was actually added, deregister it.
4357
if (tempTest != null) EngineersReport.Instance.RemoveTest(tempTest);

Source/Modules/ModuleSystemHeatColorAnimator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ protected void Start()
8484
{
8585

8686
targetRenderers = new List<Renderer>();
87-
if (includedTransformList == "")
87+
if (string.IsNullOrEmpty(includedTransformList))
8888
{
8989
foreach (Transform x in part.GetComponentsInChildren<Transform>())
9090
{

Source/SystemHeat.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
<PackageReference Include="KSPBuildTools" Version="1.1.1" />
2929
</ItemGroup>
3030

31+
<ItemGroup Condition="'$(Configuration)' == 'Debug' Or '$(Configuration)' == 'Profiling'">
32+
<AssemblyMetadata Include="HotReload" Value="true" />
33+
</ItemGroup>
34+
3135
<ItemGroup>
3236
<KSPVersionFile Include="SystemHeat.version">
3337
<Destination>$(KSPBT_ModRoot)/Versioning/SystemHeat.version</Destination>

Source/SystemHeatEditor.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ protected void OnDestroy()
4040
RemoveEditorCallbacks();
4141
Instance = null;
4242
}
43+
44+
private void OnHotReload(MonoBehaviour old)
45+
{
46+
if (HighLogic.LoadedSceneIsEditor && EditorLogic.fetch != null)
47+
InitializeEditorConstruct(EditorLogic.fetch.ship, false);
48+
}
4349
protected void FixedUpdate()
4450
{
4551
if (simulator != null)
@@ -51,7 +57,6 @@ protected void FixedUpdate()
5157
simulator.SimulationSpeed = SystemHeatUI.Instance.toolbarPanel.SimSituationVelocity;
5258
}
5359
simulator.SimulateEditor();
54-
5560
}
5661
}
5762
#region Editor

Source/SystemHeatGameSettings.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ namespace SystemHeat
55

66
public class SystemHeatGameSettings_ReactorDamage : GameParameters.CustomParameterNode
77
{
8-
98
[GameParameters.CustomParameterUI("AllowReactorDamage",
109
title = "#LOC_SystemHeat_Settings_AllowReactorDamage_Title",
1110
toolTip = "#LOC_SystemHeat_Settings_AllowReactorDamage_Tooltip",

Source/SystemHeatSettings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public static Color GetLoopColor(int id)
127127
{
128128
return SystemHeatSettings.ColorData[Mathf.Clamp(id, 0, 9)];
129129
}
130+
130131
/// <summary>
131132
/// Load data from configuration
132133
/// </summary>
@@ -216,5 +217,7 @@ public static CoolantType GetCoolantType(string name)
216217
return new CoolantType();
217218
}
218219
}
220+
221+
static void OnHotLoad() => Load();
219222
}
220223
}

Source/UI/Overlay/SystemHeatOverlay.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ protected void OnDestroy()
6969
GameEvents.onEditorStarted.Remove(onEditorStart);
7070
GameEvents.OnMapEntered.Remove(onEnterMapView);
7171
GameEvents.OnMapExited.Remove(onExitMapView);
72+
ClearPanels();
73+
DestroyOverlay();
7274
Instance = null;
7375

7476
}

Source/UI/ReactorUI/ReactorToolbar.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,9 @@ public void OnDestroy()
212212
if (stockToolbarButton != null)
213213
{
214214
ApplicationLauncher.Instance.RemoveModApplication(stockToolbarButton);
215+
stockToolbarButton = null;
215216
}
217+
DestroyToolbarPanel();
216218
}
217219

218220
protected void OnToolbarButtonToggle()

Source/UI/SystemHeatAssets.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.IO;
4+
using System.Reflection;
35
using UnityEngine;
46

57

@@ -20,6 +22,7 @@ public class SystemHeatAssets : MonoBehaviour
2022

2123
internal static string ASSET_PATH = "GameData/SystemHeat/UI/systemheatui.dat";
2224
internal static string SPRITE_ATLAS_NAME = "system-heat-sprites-1";
25+
2326
private void Awake()
2427
{
2528
Utils.Log("[SystemHeatAssets]: Loading Assets", LogType.UI);
@@ -42,5 +45,28 @@ private void Awake()
4245
}
4346
Utils.Log($"[SystemHeatAssets]: Loaded {Sprites.Count} sprites", LogType.UI);
4447
}
48+
49+
// We can't load the asset bundle again - it is already loaded, and it would
50+
// instantiate the old types. Instead, we use reflection to read them out of
51+
// the old assembly. Hot-reloading will take care of replacing any widgets
52+
// within.
53+
private static void OnHotLoad(Assembly prev)
54+
{
55+
const BindingFlags Flags = BindingFlags.Public | BindingFlags.Static;
56+
57+
var old = prev.GetType(typeof(SystemHeatAssets).FullName);
58+
OverlayPanelPrefab = GetPrefabProperty(old, nameof(OverlayPanelPrefab));
59+
ToolbarPanelPrefab = GetPrefabProperty(old, nameof(ToolbarPanelPrefab));
60+
ToolbarPanelLoopPrefab = GetPrefabProperty(old, nameof(ToolbarPanelLoopPrefab));
61+
ReactorWidgetPrefab = GetPrefabProperty(old, nameof(ReactorWidgetPrefab));
62+
ReactorToolbarPanelPrefab = GetPrefabProperty(old, nameof(ReactorToolbarPanelPrefab));
63+
Sprites = (Dictionary<string, Sprite>)old.GetProperty(nameof(Sprites), Flags).GetValue(null);
64+
}
65+
66+
private static GameObject GetPrefabProperty(Type old, string name)
67+
{
68+
const BindingFlags Flags = BindingFlags.Public | BindingFlags.Static;
69+
return (GameObject)old.GetProperty(name, Flags).GetValue(null);
70+
}
4571
}
4672
}

Source/UI/SystemHeatUI.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,14 @@ public void Start()
5050
{
5151
if (ApplicationLauncher.Ready)
5252
OnGUIAppLauncherReady();
53+
}
5354

54-
55+
// Explicitly destroy the toolbar panel because hot-reloading will destroy
56+
// the old component but leave the panel game object orphaned.
57+
private void OnHotReload(MonoBehaviour old)
58+
{
59+
DestroyToolbarPanel();
60+
toolbarPanel = null;
5561
}
5662

5763
protected void CreateToolbarPanel()
@@ -264,11 +270,12 @@ public void OnDestroy()
264270
{
265271
Utils.Log("[UI]: OnDestroy Fired", LogType.UI);
266272
// Remove the stock toolbar button
267-
GameEvents.onGUIApplicationLauncherReady.Remove(OnGUIAppLauncherReady);
268273
if (stockToolbarButton != null)
269274
{
270275
ApplicationLauncher.Instance.RemoveModApplication(stockToolbarButton);
276+
stockToolbarButton = null;
271277
}
278+
DestroyToolbarPanel();
272279
GameEvents.onGUIApplicationLauncherReady.Remove(OnGUIAppLauncherReady);
273280
GameEvents.onGUIApplicationLauncherDestroyed.Remove(OnGUIAppLauncherDestroyed);
274281
GameEvents.onGUIApplicationLauncherUnreadifying.Remove(new EventData<GameScenes>.OnEvent(OnGUIAppLauncherUnreadifying));

0 commit comments

Comments
 (0)