diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 09feb1e7..05c744f9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -46,7 +46,7 @@ jobs:
uses: KSP-RO/BuildTools/update-assembly-info@master
with:
path: ${GITHUB_WORKSPACE}/Source/assembly/AssemblyInfoRF.cs
- tag: "2.0.0.0"
+ tag: "15.9.0.0"
- name: Build mod solution
run: msbuild ${GITHUB_WORKSPACE}/Source/RealFuels.sln /t:build /restore /p:RestorePackagesConfig=true /p:Configuration=Release /p:ReferencePath="${{ steps.download-assemblies.outputs.ksp-dll-path }}"
diff --git a/Source/Engines/ModuleEngineConfigs.cs b/Source/Engines/ModuleEngineConfigs.cs
index e1c4a8d2..43185e6f 100644
--- a/Source/Engines/ModuleEngineConfigs.cs
+++ b/Source/Engines/ModuleEngineConfigs.cs
@@ -4,10 +4,11 @@
using System.Reflection;
using System.Linq;
using UnityEngine;
-using Debug = UnityEngine.Debug;
+using RealFuels.Tanks;
using RealFuels.TechLevels;
using KSP.UI.Screens;
using KSP.Localization;
+using Debug = UnityEngine.Debug;
namespace RealFuels
{
@@ -870,8 +871,7 @@ protected void SetConfiguration(ConfigNode newConfig, bool resetTechLevels)
if (HighLogic.LoadedSceneIsEditor && EditorLogic.fetch.ship != null)
{
- foreach (Part p in EditorLogic.fetch.ship.parts)
- p.SendMessage("UpdateUsedBy", SendMessageOptions.DontRequireReceiver);
+ EditorPartSetMaintainer.Instance.ScheduleUsedBySetsUpdate();
}
SetupFX();
diff --git a/Source/Engines/ModuleEnginesRF.cs b/Source/Engines/ModuleEnginesRF.cs
index f19b0d28..a0d730a9 100644
--- a/Source/Engines/ModuleEnginesRF.cs
+++ b/Source/Engines/ModuleEnginesRF.cs
@@ -403,7 +403,7 @@ public override void OnSave(ConfigNode node)
}
// Event fired by MEC after changing a part.
- public virtual void UpdateUsedBy() { }
+
public virtual void OnEngineConfigurationChanged()
{
if (started) Start(); // Wait until we've started once to allow this path to restart
diff --git a/Source/RealFuels.csproj b/Source/RealFuels.csproj
index 0733d6c9..c018998d 100644
--- a/Source/RealFuels.csproj
+++ b/Source/RealFuels.csproj
@@ -103,7 +103,7 @@
-
+
diff --git a/Source/Tanks/EditorCrossfeedSetMaintainer.cs b/Source/Tanks/EditorCrossfeedSetMaintainer.cs
deleted file mode 100644
index 6ee01ef4..00000000
--- a/Source/Tanks/EditorCrossfeedSetMaintainer.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using UnityEngine;
-
-namespace RealFuels.Tanks
-{
- [KSPAddon(KSPAddon.Startup.EditorAny, false)]
- public class EditorCrossfeedSetMaintainer : MonoBehaviour
- {
- public void Start()
- {
- GameEvents.onEditorShipModified.Add(UpdateCrossfeedSets);
- }
-
- public void OnDestroy()
- {
- GameEvents.onEditorShipModified.Remove(UpdateCrossfeedSets);
- }
-
- private void UpdateCrossfeedSets(ShipConstruct ship)
- {
- PartSet.BuildPartSets(ship.parts, null);
- }
- }
-}
diff --git a/Source/Tanks/EditorPartSetMaintainer.cs b/Source/Tanks/EditorPartSetMaintainer.cs
new file mode 100644
index 00000000..fe802ee5
--- /dev/null
+++ b/Source/Tanks/EditorPartSetMaintainer.cs
@@ -0,0 +1,138 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace RealFuels.Tanks
+{
+ [KSPAddon(KSPAddon.Startup.EditorAny, false)]
+ public class EditorPartSetMaintainer : MonoBehaviour
+ {
+ public static EditorPartSetMaintainer Instance { get; private set; }
+
+ private bool _updateScheduled = false;
+
+ protected void Awake()
+ {
+ if (Instance != null)
+ Destroy(Instance);
+
+ Instance = this;
+ }
+
+ protected void Start()
+ {
+ GameEvents.onEditorShipModified.Add(UpdateCrossfeedSets);
+ GameEvents.onPartAttach.Add(OnPartAttach);
+ GameEvents.onPartRemove.Add(OnPartRemove);
+ }
+
+ protected void OnDestroy()
+ {
+ GameEvents.onEditorShipModified.Remove(UpdateCrossfeedSets);
+ GameEvents.onPartAttach.Remove(OnPartAttach);
+ GameEvents.onPartRemove.Remove(OnPartRemove);
+
+ if (Instance == this)
+ Instance = null;
+ }
+
+ public void ScheduleUsedBySetsUpdate()
+ {
+ if (!_updateScheduled)
+ {
+ StartCoroutine(UsedBySetsUpdateRoutine());
+ _updateScheduled = true;
+ }
+ }
+
+ private void UpdateCrossfeedSets(ShipConstruct ship)
+ {
+ PartSet.BuildPartSets(ship.parts, null);
+ }
+
+ // Only trigger updates if a part in the tree that was added/removed is a fuel consumer
+ // Note that part packed status will be false when a ShipContruct is being loaded.
+ // We don't want to run the checks and scheduling in this case.
+ // Also note that we do not run updates on ghosted parts.
+ private void OnPartAttach(GameEvents.HostTargetAction hostTarget)
+ {
+ // Attaching: host is the incoming part
+ if (hostTarget.target?.packed == true)
+ ScheduleUpdateIfNeeded(hostTarget, isAttachEvent: true);
+ }
+
+ private void OnPartRemove(GameEvents.HostTargetAction hostTarget)
+ {
+ // Removing: target is the detaching part
+ if (hostTarget.target.localRoot == EditorLogic.RootPart)
+ ScheduleUpdateIfNeeded(hostTarget, isAttachEvent: false);
+ }
+
+ private void ScheduleUpdateIfNeeded(GameEvents.HostTargetAction hostTarget, bool isAttachEvent)
+ {
+ if (PartContainsEngineOrRCS(hostTarget.host, testChildren: isAttachEvent) ||
+ PartContainsEngineOrRCS(hostTarget.target, testChildren: !isAttachEvent))
+ {
+ ScheduleUsedBySetsUpdate();
+ }
+ }
+
+ private static bool PartContainsEngineOrRCS(Part p, bool testChildren = false)
+ {
+ if (p == null) return false;
+ bool result = p.FindModuleImplementing() || p.FindModuleImplementing();
+ if (testChildren && !result)
+ foreach (Part p2 in p.children)
+ result |= PartContainsEngineOrRCS(p2, testChildren);
+ return result;
+ }
+
+ private IEnumerator UsedBySetsUpdateRoutine()
+ {
+ yield return new WaitForEndOfFrame();
+ _updateScheduled = false;
+ UpdateUsedBySets();
+ }
+
+ private static void UpdateUsedBySets()
+ {
+ Debug.Log("[RF] UpdateUsedBySets start");
+ if (EditorLogic.fetch.ship == null)
+ return;
+
+ var thrusterModules = new List();
+ var tankModules = new List();
+ List parts = EditorLogic.fetch.ship.parts;
+ foreach (Part p in parts)
+ {
+ foreach (PartModule m in p.Modules)
+ {
+ if (m is ModuleEngines || m is ModuleRCS)
+ thrusterModules.Add(m);
+ else if (m is ModuleFuelTanks mft)
+ tankModules.Add(mft);
+ }
+ }
+
+ foreach (ModuleFuelTanks mft in tankModules)
+ {
+ mft.usedBy.Clear();
+ mft.usedByTanks.Clear();
+
+ foreach (PartModule m in thrusterModules)
+ {
+ FuelInfo f = null;
+ if (m is ModuleEngines me)
+ f = new FuelInfo(me.propellants, mft, m);
+ else if (m is ModuleRCS mRcs)
+ f = new FuelInfo(mRcs.propellants, mft, m);
+
+ if (f?.valid == true)
+ mft.UpdateFuelInfo(f, m);
+ }
+
+ mft.UpdateTweakableButtonsDelegate();
+ }
+ }
+ }
+}
diff --git a/Source/Tanks/FuelInfo.cs b/Source/Tanks/FuelInfo.cs
index 9e288604..e85ba104 100644
--- a/Source/Tanks/FuelInfo.cs
+++ b/Source/Tanks/FuelInfo.cs
@@ -28,7 +28,6 @@ private string BuildLabel()
{
Propellant tfuel = kvp.Key;
if (PartResourceLibrary.Instance.GetDefinition(tfuel.name).resourceTransferMode != ResourceTransferMode.NONE && !IgnoreFuel(tfuel.name))
- //labelString.Add($"{Math.Round(100 * tfuel.ratio * kvp.Value / efficiency, 3)}% {tfuel.name}");
labelString.Add($"{Math.Round(100 * tfuel.ratio * kvp.Value / efficiency, 3)}% {tfuel.displayName}");
}
return string.Join(" / ", labelString);
@@ -42,15 +41,15 @@ public FuelInfo(List props, ModuleFuelTanks tank, PartModule source)
this.source = source;
string _title = source.part.partInfo.title;
- if (source.part.Modules.GetModule("ModuleEngineConfigs") is PartModule pm && pm != null)
- _title = $"{pm.Fields["configuration"].GetValue(pm)}: {_title}";
+ if (source.part.FindModuleImplementing() is ModuleEngineConfigs mec)
+ _title = $"{mec.configuration}: {_title}";
title = _title;
ratioFactor = 0.0;
efficiency = 0.0;
// Error conditions: Resource not defined in library, or resource has no tank and is not in IgnoreFuel
- var missingRes = props.FirstOrDefault(p => PartResourceLibrary.Instance.GetDefinition(p.name) == null);
- var noTanks = props.Where(p => !tank.tanksDict.ContainsKey(p.name));
+ Propellant missingRes = props.FirstOrDefault(p => PartResourceLibrary.Instance.GetDefinition(p.name) == null);
+ IEnumerable noTanks = props.Where(p => !tank.tanksDict.ContainsKey(p.name));
bool noTanksAndNotIgnored = noTanks.Any(p => !IgnoreFuel(p.name));
if (missingRes != null)
Debug.LogError($"[MFT/RF] FuelInfo: Unknown RESOURCE: {missingRes.name}");
diff --git a/Source/Tanks/ModuleFuelTanks.cs b/Source/Tanks/ModuleFuelTanks.cs
index ce9a3ece..952e1985 100644
--- a/Source/Tanks/ModuleFuelTanks.cs
+++ b/Source/Tanks/ModuleFuelTanks.cs
@@ -8,6 +8,7 @@
using System.Reflection;
using KSP.Localization;
using ROUtils;
+using UnityEngine.Profiling;
// ReSharper disable InconsistentNaming, CompareOfFloatsByEqualityOperator
@@ -273,8 +274,6 @@ public override void OnStart(StartState state)
{
if (HighLogic.LoadedSceneIsEditor)
{
- GameEvents.onPartAttach.Add(OnPartAttach);
- GameEvents.onPartRemove.Add(OnPartRemove);
GameEvents.onEditorShipModified.Add(OnEditorShipModified);
GameEvents.onPartActionUIDismiss.Add(OnPartActionGuiDismiss);
GameEvents.onPartActionUIShown.Add(OnPartActionUIShown);
@@ -287,7 +286,7 @@ public override void OnStart(StartState state)
Fields[nameof(utilization)].uiControlEditor.onSymmetryFieldChanged += OnUtilizationChanged;
Fields[nameof(typeDisp)].uiControlEditor.onFieldChanged += OnTypeDispChanged;
Fields[nameof(typeDisp)].uiControlEditor.onSymmetryFieldChanged += OnTypeDispChanged;
- UpdateUsedBy();
+ EditorPartSetMaintainer.Instance.ScheduleUsedBySetsUpdate();
}
OnStartRF(state);
@@ -301,8 +300,6 @@ public override void OnStart(StartState state)
void OnDestroy()
{
- GameEvents.onPartAttach.Remove(OnPartAttach);
- GameEvents.onPartRemove.Remove(OnPartRemove);
GameEvents.onEditorShipModified.Remove(OnEditorShipModified);
GameEvents.onPartActionUIDismiss.Remove(OnPartActionGuiDismiss);
GameEvents.onPartActionUIShown.Remove(OnPartActionUIShown);
@@ -331,31 +328,6 @@ private void OnTypeDispChanged(BaseField f, object obj)
private void OnEditorShipModified(ShipConstruct _) => PartResourcesChanged();
- private bool PartContainsEngineOrRCS(Part p, bool testChildren = false)
- {
- if (p == null) return false;
- bool result = p.FindModuleImplementing() || p.FindModuleImplementing();
- if (testChildren && !result)
- foreach (Part p2 in p.children)
- result |= PartContainsEngineOrRCS(p2, testChildren);
- return result;
- }
-
- // Only trigger updates if a part in the tree that was added/removed is a fuel consumer
- private void OnPartAttach(GameEvents.HostTargetAction hostTarget)
- {
- // Attaching: host is the incoming part
- if (PartContainsEngineOrRCS(hostTarget.host, true) || PartContainsEngineOrRCS(hostTarget.target, false))
- UpdateUsedBy();
- }
-
- private void OnPartRemove(GameEvents.HostTargetAction hostTarget)
- {
- // Removing: target is the detaching part
- if (PartContainsEngineOrRCS(hostTarget.host, false) || PartContainsEngineOrRCS(hostTarget.target, true))
- UpdateUsedBy();
- }
-
private void OnPartActionUIShown(UIPartActionWindow window, Part p)
{
if (p == part && windowDirty)
@@ -587,7 +559,7 @@ private void UpdateTankType (bool initializeAmounts = false)
massDirty = true;
}
- UpdateUsedBy();
+ EditorPartSetMaintainer.Instance?.ScheduleUsedBySetsUpdate();
UpdateTankTypeRF(def);
UpdateTestFlight();
@@ -1009,46 +981,14 @@ private void SetUtilization(float value)
internal readonly Dictionary usedBy = new Dictionary();
internal readonly HashSet usedByTanks = new HashSet();
- private void UpdateFuelInfo(FuelInfo f, PartModule source)
+ internal void UpdateFuelInfo(FuelInfo f, PartModule source)
{
+ Profiler.BeginSample("UpdateFuelInfo");
usedBy[source] = f;
foreach (Propellant tfuel in f.propellantVolumeMults.Keys)
if (tanksDict.TryGetValue(tfuel.name, out FuelTank tank) && tank.canHave)
usedByTanks.Add(tank);
- }
-
- public void UpdateUsedBy()
- {
- if (!HighLogic.LoadedSceneIsEditor) return;
-
- usedBy.Clear();
- usedByTanks.Clear();
-
- // Get part list
- List parts;
- if (HighLogic.LoadedSceneIsEditor && EditorLogic.fetch.ship != null)
- parts = EditorLogic.fetch.ship.parts;
- else if (HighLogic.LoadedSceneIsFlight && vessel != null)
- parts = vessel.parts;
- else
- return;
-
- foreach(Part p in parts)
- {
- string title = p.partInfo.title;
- foreach(PartModule m in p.Modules)
- {
- FuelInfo f = null;
- if (m is ModuleEngines)
- f = new FuelInfo((m as ModuleEngines).propellants, this, m);
- else if (m is ModuleRCS)
- f = new FuelInfo((m as ModuleRCS).propellants, this, m);
- if (f?.valid == true)
- UpdateFuelInfo(f, m);
- }
- }
-
- UpdateTweakableButtonsDelegate();
+ Profiler.EndSample();
}
private readonly HashSet displayedParts = new HashSet();
diff --git a/Source/assembly/AssemblyInfoRF.cs b/Source/assembly/AssemblyInfoRF.cs
index da14f9c2..44a8be73 100644
--- a/Source/assembly/AssemblyInfoRF.cs
+++ b/Source/assembly/AssemblyInfoRF.cs
@@ -24,8 +24,8 @@
[assembly: AssemblyFileVersion("@MAJOR@.@MINOR@.@PATCH@.@BUILD@")]
[assembly: KSPAssembly("RealFuels", @MAJOR@, @MINOR@, @PATCH@)]
#else
-[assembly: AssemblyFileVersion("15.8.1.0")]
-[assembly: KSPAssembly("RealFuels", 15, 8, 1)]
+[assembly: AssemblyFileVersion("15.9.0.0")]
+[assembly: KSPAssembly("RealFuels", 15, 9, 0)]
#endif
// The following attributes are used to specify the signing key for the assembly,