Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework tank UsedBy logic #342

Merged
merged 1 commit into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}"
Expand Down
6 changes: 3 additions & 3 deletions Source/Engines/ModuleEngineConfigs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion Source/Engines/ModuleEnginesRF.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Source/RealFuels.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
<Compile Include="EntryCosts\EntryCostDatabase.cs" />
<Compile Include="EntryCosts\PartEntryCostHolder.cs" />
<Compile Include="Pumps\RefuelingPump.cs" />
<Compile Include="Tanks\EditorCrossfeedSetMaintainer.cs" />
<Compile Include="Tanks\EditorPartSetMaintainer.cs" />
<Compile Include="Tanks\FuelInfo.cs" />
<Compile Include="Tanks\FuelTank.cs" />
<Compile Include="Tanks\FuelTankList.cs" />
Expand Down
23 changes: 0 additions & 23 deletions Source/Tanks/EditorCrossfeedSetMaintainer.cs

This file was deleted.

138 changes: 138 additions & 0 deletions Source/Tanks/EditorPartSetMaintainer.cs
Original file line number Diff line number Diff line change
@@ -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<Part, Part> hostTarget)
{
// Attaching: host is the incoming part
if (hostTarget.target?.packed == true)
ScheduleUpdateIfNeeded(hostTarget, isAttachEvent: true);
}

private void OnPartRemove(GameEvents.HostTargetAction<Part, Part> hostTarget)
{
// Removing: target is the detaching part
if (hostTarget.target.localRoot == EditorLogic.RootPart)
ScheduleUpdateIfNeeded(hostTarget, isAttachEvent: false);
}

private void ScheduleUpdateIfNeeded(GameEvents.HostTargetAction<Part, Part> 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<ModuleEngines>() || p.FindModuleImplementing<ModuleRCS>();
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<PartModule>();
var tankModules = new List<ModuleFuelTanks>();
List<Part> 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();
}
}
}
}
9 changes: 4 additions & 5 deletions Source/Tanks/FuelInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -42,15 +41,15 @@ public FuelInfo(List<Propellant> 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<string>(pm)}: {_title}";
if (source.part.FindModuleImplementing<ModuleEngineConfigs>() 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<Propellant> 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}");
Expand Down
72 changes: 6 additions & 66 deletions Source/Tanks/ModuleFuelTanks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using KSP.Localization;
using ROUtils;
using UnityEngine.Profiling;

// ReSharper disable InconsistentNaming, CompareOfFloatsByEqualityOperator

Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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<ModuleEngines>() || p.FindModuleImplementing<ModuleRCS>();
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<Part, Part> hostTarget)
{
// Attaching: host is the incoming part
if (PartContainsEngineOrRCS(hostTarget.host, true) || PartContainsEngineOrRCS(hostTarget.target, false))
UpdateUsedBy();
}

private void OnPartRemove(GameEvents.HostTargetAction<Part, Part> 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)
Expand Down Expand Up @@ -587,7 +559,7 @@ private void UpdateTankType (bool initializeAmounts = false)

massDirty = true;
}
UpdateUsedBy();
EditorPartSetMaintainer.Instance?.ScheduleUsedBySetsUpdate();

UpdateTankTypeRF(def);
UpdateTestFlight();
Expand Down Expand Up @@ -1009,46 +981,14 @@ private void SetUtilization(float value)
internal readonly Dictionary<PartModule, FuelInfo> usedBy = new Dictionary<PartModule, FuelInfo>();
internal readonly HashSet<FuelTank> usedByTanks = new HashSet<FuelTank>();

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<Part> 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<string> displayedParts = new HashSet<string>();
Expand Down
4 changes: 2 additions & 2 deletions Source/assembly/AssemblyInfoRF.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading