Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

##### Unreleased
**New/Improved patches**
- New KSP bugfix : **DuplicateAppLauncherButtons** Fix the stock toolbar accumulating duplicate buttons on switching scene, and the flood of `NullReferenceException` that follows. A `UIApp` adds itself to the toolbar again every time the app launcher restarts, losing track of the button it already has, and then destroys itself because its duplicate check tests whether an instance exists rather than whether it is a different one.
- Improved the **FastLoader** patch to reuse the initial GameDatabase directory tree during the second config pass while refreshing files and directories created or modified by `Startup.Instantly` addons. Avoids reparsing unchanged configs and saves several seconds in heavily modded installs.

**Bug Fixes**
Expand Down
6 changes: 6 additions & 0 deletions GameData/KSPCommunityFixes/Settings.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,12 @@ KSP_COMMUNITY_FIXES
// dialog being wildly inflated.
FlightLoggerInflatedDistance = true

// Fix stock toolbar buttons being duplicated on switching scene, and the flood of NullReferenceException
// that follows. An app adds itself to the toolbar again whenever the launcher restarts, losing track of
// the button it already has, and then destroys itself because its own duplicate check tests whether an
// instance exists rather than whether it is a different one.
DuplicateAppLauncherButtons = true

// ##########################
// Obsolete bugfixes
// ##########################
Expand Down
70 changes: 70 additions & 0 deletions KSPCommunityFixes/BugFixes/DuplicateAppLauncherButtons.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections;
using System.Collections.Generic;
using KSP.UI.Screens;

namespace KSPCommunityFixes.BugFixes
{
internal class DuplicateAppLauncherButtons : BasePatch
{
// The apps that are part way through adding themselves, by instance id. Keyed on the id
// rather than the app because a destroyed UnityEngine.Object compares equal to null.
static readonly HashSet<int> addsInProgress = new HashSet<int>();

protected override Version VersionMin => new Version(1, 8, 0);

protected override void ApplyPatches()
{
AddPatch(PatchType.Prefix, typeof(UIApp), "AddToAppLauncher");
AddPatch(PatchType.Postfix, typeof(UIApp), "set_appLauncherButton",
nameof(UIApp_SetAppLauncherButton_Postfix));
AddPatch(PatchType.Postfix, typeof(UIApp), "OnDestroy");
}

static bool UIApp_AddToAppLauncher_Prefix(UIApp __instance, ref IEnumerator __result)
{
if (IsOnAppLauncher(__instance.appLauncherButton) ||
!addsInProgress.Add(__instance.GetInstanceID()))
{
// Already on the toolbar, or on its way there. Going again would overwrite the only
// reference to the button it ends up with, leaving the other one on the toolbar for
// the rest of the session, and would run OnAppInitialized a second time.
__result = Nothing();
return false;
}

return true;
}

static void UIApp_SetAppLauncherButton_Postfix(UIApp __instance)
{
addsInProgress.Remove(__instance.GetInstanceID());
}

static void UIApp_OnDestroy_Postfix(UIApp __instance)
{
// The app can be destroyed part way through adding itself, including by
// OnAppInitialized when it turns out to be a second instance.
addsInProgress.Remove(__instance.GetInstanceID());
}

static bool IsOnAppLauncher(ApplicationLauncherButton button)
{
if (button.IsNullOrDestroyed())
return false;

// The button outlives the launcher being torn down and built again, so holding one is
// not on its own enough to say the app is still on the toolbar.
ApplicationLauncher launcher = ApplicationLauncher.Instance;
if (launcher.IsNullOrDestroyed())
return false;

return launcher.appList.Contains(button) || launcher.appListHidden.Contains(button);
}

static IEnumerator Nothing()
{
yield break;
}
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ User options are available from the "ESC" in-game settings menu :<br/><img src="
- [**FIUpdateRadiation**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/364) [KSP 1.12.0 - 1.12.5]<br/>Fix radiation flux being inconsistent for timewarp rates between 1x and 100x, caused by `PartThermalData.expFlux` / `unexpFlux` being mutated during every thermal integration pass.
- [**EditorAnimatedPartsShipModified**](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/388) [KSP 1.12.0 - 1.12.5]<br/>Fix the Engineer's Report craft dimensions (and other `onEditorShipModified` consumers) lagging one vessel modification behind when an animated part is actuated in the editor (deployable solar panels/radiators/antennas, `ModuleAnimateGeneric` animations, Breaking Ground robotic servos), by re-firing the modification event once the animation has actually finished.
- [**FlightLoggerInflatedDistance**](https://github.com/KSPModdingLibs/KSPCommunityFixes/pull/402) [KSP 1.12.0 - 1.12.5]<br/>Fix the "distance travelled" / "distance over ground" values shown in the F3 flight results dialog being wildly inflated.
- **DuplicateAppLauncherButtons** [KSP 1.8.0 - 1.12.5]<br/>Fix the stock toolbar accumulating duplicate buttons (resource display, action groups, alarm clock, delta-v...) on switching scene, and the flood of `NullReferenceException` from `KSP.UI` that follows. A `UIApp` adds itself to the toolbar again every time the app launcher restarts, losing track of the button it already has, and then destroys itself because its duplicate check tests whether an instance exists rather than whether it is a different one.
- **PartTooltipUpgradesApplyToSubstituteParts** [KSP 1.12.0 - 1.12.5]<br/>Disabled by default, you can enable it with a MM patch. Fix part upgrades being applied directly to the prefab part when creating the `PartListTooltip`, instead using a substitute part instance. Requires **UpgradeBugs** to be enabled.

#### Quality of Life tweaks
Expand Down