Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c8d23af
First manual version
Jan 4, 2026
c4719ed
Merge branch 'dev' into atlas-friendly
Jan 4, 2026
1b7e68b
Merge branch 'dev' into atlas-friendly
Jan 6, 2026
5d731a8
Merge branch 'MuMech:dev' into atlas-friendly
AntonKuzin Jan 8, 2026
84e7bd8
Small refactoring - more sane control flow.
Jan 8, 2026
6270389
Detect stage and a half automatically
Jan 9, 2026
19e0f93
Merge branch 'dev' into atlas-friendly
Jan 9, 2026
c680899
Merge branch 'MuMech:dev' into atlas-friendly
AntonKuzin Jan 12, 2026
895f062
Merge branch 'dev' into atlas-friendly
Feb 4, 2026
6c9186d
Merge branch 'dev' into atlas-friendly
Feb 5, 2026
13cff53
Small refactoring
Feb 7, 2026
c3e9f54
Merge branch 'staging-controller-fix' into atlas-friendly
Feb 7, 2026
2c9280a
Don't check if there's a fairing twice. Revert the function argument …
Feb 7, 2026
8364162
Merge branch 'staging-controller-fix' into atlas-friendly
Feb 8, 2026
02f5f2f
Revert fairing check removal
Feb 8, 2026
5fed147
Merge branch 'staging-controller-fix' into atlas-friendly
Feb 8, 2026
46447bb
Merge branch 'dev' into atlas-friendly
Feb 11, 2026
2d4f825
Merge remote-tracking branch 'origin/dev' into atlas-friendly
Feb 15, 2026
aabfd35
Merge remote-tracking branch 'origin/dev' into atlas-friendly
Mar 1, 2026
e38049a
Merge branch 'MuMech:dev' into atlas-friendly
AntonKuzin Mar 15, 2026
27768ab
Merge branch 'MuMech:dev' into staging-controller-fix
AntonKuzin Mar 15, 2026
b42cec3
Fix fuel flow for stages with multiple tanks
Mar 15, 2026
c95518c
Merge branch 'MuMech:dev' into atlas-friendly
AntonKuzin Apr 11, 2026
24630c2
Merge branch 'MuMech:dev' into staging-controller-fix
AntonKuzin Apr 11, 2026
a174911
Merge branch 'MuMech:dev' into staging-controller-fix
AntonKuzin Apr 25, 2026
f935f1f
Merge from master
Apr 25, 2026
ca61779
Return immediately after staging
Apr 25, 2026
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
8 changes: 7 additions & 1 deletion MechJeb2/MechJebModuleStageStats.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
extern alias JetBrainsAnnotations;
extern alias JetBrainsAnnotations;
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand All @@ -21,6 +21,10 @@ public class MechJebModuleStageStats : ComputerModule
public double AltSLT = 0;
public double Mach = 0;

public int HalfStageIndex => _vesselManagerVac.HalfStageIndex;
[Persistent(pass = (int)(Pass.TYPE | Pass.GLOBAL))]
public readonly EditableDoubleMult HalfStageEndMass = new EditableDoubleMult(0, 0.001);

private int _vabRebuildTimer = 1;

public readonly List<FuelStats> AtmoStats = new List<FuelStats>();
Expand Down Expand Up @@ -144,6 +148,7 @@ private void RunSimulation()
_vesselManagerVac.SetConditions(0, 0, 0);
_vesselManagerVac.SetInitial(VesselState.time, VesselState.orbitalPosition.WorldToV3Rotated(),
VesselState.orbitalVelocity.WorldToV3Rotated(), VesselState.forward.WorldToV3Rotated());
_vesselManagerVac.SetupStageAndAHalf(HalfStageEndMass);
if (!_vesselManagerVac.TryStartFuelFlowSimulationJob())
throw new Exception("[MechJebModuleStageStats] could not start vac stats job");
}
Expand All @@ -154,6 +159,7 @@ private void RunSimulation()
_vesselManagerAtmo.SetConditions(atmDensity, staticPressureKpa * PhysicsGlobals.KpaToAtmospheres, mach);
_vesselManagerAtmo.SetInitial(VesselState.time, VesselState.orbitalPosition.WorldToV3Rotated(),
VesselState.orbitalVelocity.WorldToV3Rotated(), VesselState.forward.WorldToV3Rotated());
_vesselManagerAtmo.SetupStageAndAHalf(HalfStageEndMass);
if (!_vesselManagerAtmo.TryStartFuelFlowSimulationJob())
throw new Exception("[MechJebModuleStageStats] could not start atmo stats job");
}
Expand Down
10 changes: 9 additions & 1 deletion MechJeb2/MechJebModuleStagingController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ public override void OnUpdate()
UpdateActiveModuleEngines(_allModuleEngines);
UpdateBurnedResources();

if (Vessel.currentStage == _stats.HalfStageIndex && Vessel.totalMass <= _stats.HalfStageEndMass)
{
Stage();
return;
}

// don't decouple active or idle engines or tanks
if (InverseStageDecouplesActiveOrIdleEngineOrTank(Vessel.currentStage - 1, _burnedResources, _activeModuleEngines) &&
!InverseStageReleasesClamps(Vessel.currentStage - 1))
Expand All @@ -308,6 +314,7 @@ public override void OnUpdate()
if (!InverseStageHasActiveEngines(Vessel.currentStage))
{
Stage();
return;
}

// prevent staging when the current stage has active engines and the next stage has any engines (but not decouplers or clamps)
Expand All @@ -324,6 +331,7 @@ public override void OnUpdate()
if (InverseStageDecouplesDeactivatedEngineOrTank(Vessel.currentStage - 1))
{
Stage();
return;
}

// only decouple fairings if the dynamic pressure, altitude, and aerothermal flux conditions are respected
Expand Down Expand Up @@ -453,7 +461,7 @@ private double LastNonZeroDVStageBurnTime()
private bool InverseStageHasActiveEngines(int inverseStage)
{
foreach (ModuleEngines engine in _allModuleEngines)
if (engine.part.inverseStage == inverseStage && engine.EngineHasFuel())
if (engine.part.inverseStage >= inverseStage && engine.EngineHasFuel())
return true;
return false;
}
Expand Down
9 changes: 9 additions & 0 deletions MechJeb2/MechJebStageStatsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,15 @@ public void AllStageStats()

Profiler.EndSample();

if (stats.HalfStageIndex != -1)
{
GUILayout.BeginHorizontal();
GUILayout.Label($"Half-stage index: {stats.HalfStageIndex}", GUILayout.Width(130));
GUILayout.Space(30);
GuiUtils.SimpleTextBox("End mass: ", stats.HalfStageEndMass, "kg");
GUILayout.EndHorizontal();
}

Profiler.BeginSample("AllStageStats.DrawColumns");

GUILayout.BeginHorizontal();
Expand Down
40 changes: 39 additions & 1 deletion MechJebLib/FuelFlowSimulation/FuelFlowSimulation.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/*
/*
* Copyright Lamont Granquist, Sebastien Gaggini and the MechJeb contributors
* SPDX-License-Identifier: LicenseRef-PD-hp OR Unlicense OR CC0-1.0 OR 0BSD OR MIT-0 OR MIT OR LGPL-2.1+
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using MechJebLib.FuelFlowSimulation.PartModules;
using MechJebLib.Utils;
Expand All @@ -25,6 +26,7 @@ public class FuelFlowSimulation : AsyncJob
private readonly HashSet<SimPart> _partsWithRCSDrains = new HashSet<SimPart>();
private readonly HashSet<SimPart> _partsWithRCSDrains2 = new HashSet<SimPart>();
private bool _allocatedFirstSegment;
private bool _halfStageIsDetected;

public override void Run(object? o = null)
{
Expand All @@ -34,6 +36,7 @@ public override void Run(object? o = null)
if (!(o is SimVessel vessel))
throw new ArgumentException("o is not a SimVessel", nameof(o));

_halfStageIsDetected = false;
_allocatedFirstSegment = false;
_time = 0;
Segments.Clear();
Expand All @@ -53,6 +56,9 @@ public override void Run(object? o = null)
Segments.Reverse();

_partsWithResourceDrains.Clear();

if (!_halfStageIsDetected)
vessel.HalfStageIndex = -1;
}

private void SimulateRCS(SimVessel vessel, bool max)
Expand Down Expand Up @@ -110,12 +116,30 @@ private void SimulateStage(SimVessel vessel)
UpdateResourceDrainsAndResiduals(vessel);
double currentThrust = vessel.ThrustMagnitude;

if (!_halfStageIsDetected //is anyone insane enough to build a rocket with multiple half-stages? You never know
&& vessel.ActiveEngines.Count > 0
&& _sources.Count > 0)
{
int earliestDroppedEgineInStage = vessel.ActiveEngines.Max(x => x.Part.DecoupledInStage);
int earliestDroppedTankInStage = _sources.Max(x => x.DecoupledInStage);
if (earliestDroppedEgineInStage > earliestDroppedTankInStage)
{
_halfStageIsDetected = true;
vessel.HalfStageIndex = earliestDroppedEgineInStage + 1;
}
}

for (int steps = MAXSTEPS; steps > 0; steps--)
{
if (AllowedToStage(vessel))
return;

double dt = MinimumTimeStep();
if (_currentSegment.KSPStage == vessel.HalfStageIndex)
{
double massFlow = ResourceMaxMassFlow(vessel);
dt = Min(dt, (vessel.Mass - vessel.HalfStageEndMass) / massFlow);
}

// FIXME: if we have constructed a segment which is > 0 dV, but less than 0.02s, and there's a
// prior > 0dV segment in the same kspStage we should add those together to reduce clutter.
Expand Down Expand Up @@ -381,6 +405,17 @@ private double ResourceMaxTime()
return maxTime;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private double ResourceMaxMassFlow(SimVessel vessel)
{
double massFlow = 0;

foreach (SimModuleEngines engine in vessel.ActiveEngines)
massFlow += engine.MassFlowRate;

return massFlow;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FinishRcsSegment(bool max, double deltaTime, double startMass, double endMass, double rcsThrust)
{
Expand Down Expand Up @@ -457,6 +492,9 @@ private static bool AllowedToStage(SimVessel vessel)
if (vessel.ActiveEngines.Count == 0)
return true;

if (vessel.CurrentStage == vessel.HalfStageIndex && vessel.Mass - vessel.HalfStageEndMass < 1e-6)
return true;

for (int i = 0; i < vessel.ActiveEngines.Count; i++)
{
SimModuleEngines e = vessel.ActiveEngines[i];
Expand Down
14 changes: 13 additions & 1 deletion MechJebLib/FuelFlowSimulation/SimVessel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright Lamont Granquist, Sebastien Gaggini and the MechJeb contributors
* SPDX-License-Identifier: LicenseRef-PD-hp OR Unlicense OR CC0-1.0 OR 0BSD OR MIT-0 OR MIT OR LGPL-2.1+
*/
Expand Down Expand Up @@ -46,6 +46,9 @@ public class SimVessel : IDisposable
public double T;
public V3 R, V, U;

public int HalfStageIndex = -1;
public double HalfStageEndMass = 0;

// CurrentStage gets scribbled over by the FuelFlowSimulation, SetCurrentStage() is intended to be used in
// the VesselBuilder and DecouplingAnalyzer to figure out the right value, ResetCurrentStage() is called by
// the VesselUpdater to reset it back.
Expand Down Expand Up @@ -80,6 +83,15 @@ public void SetInitial(double t, V3 r, V3 v, V3 u)
U = u;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetupStageAndAHalf(double endMass)
{
if (HalfStageIndex != -1)
{
HalfStageEndMass = endMass;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void UpdateMass()
{
Expand Down
24 changes: 7 additions & 17 deletions MechJebLibBindings/FuelFlowSimulation/SimVesselBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright Lamont Granquist, Sebastien Gaggini and the MechJeb contributors
* SPDX-License-Identifier: LicenseRef-PD-hp OR Unlicense OR CC0-1.0 OR 0BSD OR MIT-0 OR MIT OR LGPL-2.1+
*/
Expand Down Expand Up @@ -29,17 +29,9 @@ public class SimVesselBuilder

private static readonly CrewMass _crewMassDelegate;

private SimVessel _vessel
{
get => _manager._vessel;
set => _manager._vessel = value;
}
private SimVessel _vessel => _manager._vessel;

private IShipconstruct _kspVessel
{
get => _manager._kspVessel;
set => _manager._kspVessel = value;
}
private IShipconstruct _kspVessel => _manager._kspVessel;

private readonly SimVesselManager _manager;
private static readonly Type? _rfType;
Expand Down Expand Up @@ -68,7 +60,7 @@ static SimVesselBuilder()

private static double CrewMassNew(ProtoCrewMember crew) => PhysicsGlobals.KerbalCrewMass + crew.ResourceMass() + crew.InventoryMass();

public SimVesselBuilder(SimVesselManager manager)
internal SimVesselBuilder(SimVesselManager manager)
{
_manager = manager;
}
Expand Down Expand Up @@ -126,14 +118,12 @@ internal void UpdateSymmetryParts()
}
}

internal void BuildVessel(IShipconstruct kspVessel)
internal void BuildVessel()
{
_vessel.Dispose();
_vessel = SimVessel.Borrow();
_kspVessel = kspVessel;
BuildParts();
}

internal void BuildParts()
private void BuildParts()
{
_vessel.SetCurrentStage(StageManager.CurrentStage);

Expand Down
15 changes: 10 additions & 5 deletions MechJebLibBindings/FuelFlowSimulation/SimVesselManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright Lamont Granquist, Sebastien Gaggini and the MechJeb contributors
* SPDX-License-Identifier: LicenseRef-PD-hp OR Unlicense OR CC0-1.0 OR 0BSD OR MIT-0 OR MIT OR LGPL-2.1+
*/
Expand All @@ -14,8 +14,6 @@ namespace MechJebLibBindings.FuelFlowSimulation
// need to link against KSP GameObjects (MechJebLibBindings.dll or something like that)
public partial class SimVesselManager
{
public List<FuelStats> Segments => FuelFlowSimulation.Segments;

private readonly SimVesselBuilder _builder;
private readonly SimVesselUpdater _updater;
private SimVessel _vessel;
Expand All @@ -32,6 +30,8 @@ public partial class SimVesselManager
public V3 V => _vessel.V;
public V3 U => _vessel.U;

public int HalfStageIndex => _vessel.HalfStageIndex;

public SimVesselManager()
{
_builder = new SimVesselBuilder(this);
Expand All @@ -42,9 +42,12 @@ public SimVesselManager()

public void Build(IShipconstruct vessel)
{
_vessel.Dispose();
_vessel = SimVessel.Borrow();
_kspVessel = vessel;

Clear();
_builder.BuildVessel(vessel);
_builder.BuildParts();
_builder.BuildVessel();
Update();
_builder.UpdateLinks();
_builder.UpdateCrossFeedSet();
Expand All @@ -60,6 +63,8 @@ public void SetConditions(double atmDensity, double atmPressure, double machNumb

public void SetInitial(double t, V3 r, V3 v, V3 u) => _vessel.SetInitial(t, r, v, u);

public void SetupStageAndAHalf(double endMass) => _vessel.SetupStageAndAHalf(endMass);

public bool TryStartFuelFlowSimulationJob()
{
FuelFlowSimulation.DVLinearThrust = DVLinearThrust;
Expand Down
4 changes: 2 additions & 2 deletions MechJebLibBindings/FuelFlowSimulation/SimVesselUpdater.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright Lamont Granquist, Sebastien Gaggini and the MechJeb contributors
* SPDX-License-Identifier: LicenseRef-PD-hp OR Unlicense OR CC0-1.0 OR 0BSD OR MIT-0 OR MIT OR LGPL-2.1+
*/
Expand Down Expand Up @@ -49,7 +49,7 @@ static SimVesselUpdater()
}
}

public SimVesselUpdater(SimVesselManager manager)
internal SimVesselUpdater(SimVesselManager manager)
{
_manager = manager;
}
Expand Down