From 7a2a87e0ea3c2c671adc50a3428a22b3a9bd1a5f Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 09:44:34 -0700 Subject: [PATCH 01/17] 1.40.1_RTBFORK_r2 --- .../BugFixes/DoubleCurvePreserveTangents.cs | 48 ++++++++++- .../BugFixes/ExtendedDeployableParts.cs | 43 +++++++++- .../BugFixes/ModuleIndexingMismatch.cs | 85 +++++++++++++------ .../BugFixes/RefundingOnRecovery.cs | 83 +++++++++++++++++- KSPCommunityFixes/KSPCommunityFixes.cs | 29 +++++++ 5 files changed, 253 insertions(+), 35 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs b/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs index 44ab46b4..ddcc6b8f 100644 --- a/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs +++ b/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs @@ -11,7 +11,14 @@ class DoubleCurvePreserveTangents : BasePatch protected override void ApplyPatches() { - AddPatch(PatchType.Transpiler, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); + if (!KSPCommunityFixes.cleanedDll) + { + AddPatch(PatchType.Transpiler, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); + } + else + { + AddPatch(PatchType.Prefix, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); + } } static IEnumerable DoubleCurve_RecomputeTangents_Transpiler(IEnumerable instructions) @@ -37,5 +44,44 @@ static IEnumerable DoubleCurve_RecomputeTangents_Transpiler(IEn return code; } + + static bool DoubleCurve_RecomputeTangents_Prefix(DoubleCurve __instance) + { + int count = __instance.keys.Count; + DoubleKeyframe doubleKeyframe; + if (count == 1) + { + return false; + } + doubleKeyframe = __instance.keys[0]; + if (doubleKeyframe.autoTangent) + { + doubleKeyframe.inTangent = 0.0; + doubleKeyframe.outTangent = (__instance.keys[1].value - doubleKeyframe.value) / (__instance.keys[1].time - doubleKeyframe.time); + __instance.keys[0] = doubleKeyframe; + } + int num3 = count - 1; + doubleKeyframe = __instance.keys[num3]; + if (doubleKeyframe.autoTangent) + { + doubleKeyframe.inTangent = (doubleKeyframe.value - __instance.keys[num3 - 1].value) / (doubleKeyframe.time - __instance.keys[num3 - 1].value); + doubleKeyframe.outTangent = 0.0; + __instance.keys[num3] = doubleKeyframe; + } + if (count > 2) + { + for (int i = 1; i < num3; i++) + { + doubleKeyframe = __instance.keys[i]; + if (doubleKeyframe.autoTangent) + { + double num4 = (doubleKeyframe.value - __instance.keys[i - 1].value) / (doubleKeyframe.time - __instance.keys[i - 1].value); + double num5 = (__instance.keys[i + 1].value - doubleKeyframe.value) / (__instance.keys[i + 1].time - doubleKeyframe.time); + doubleKeyframe.inTangent = (doubleKeyframe.outTangent = (num4 + num5) * 0.5); + } + } + } + return false; + } } } diff --git a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs index bd924df3..99101f37 100644 --- a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs +++ b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs @@ -23,7 +23,14 @@ protected override void ApplyPatches() { AddPatch(PatchType.Transpiler, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM)); - AddPatch(PatchType.Transpiler, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); + if (!KSPCommunityFixes.cleanedDll) + { + AddPatch(PatchType.Transpiler, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); + } + else + { + AddPatch(PatchType.Prefix, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); + } } static IEnumerable ModuleDeployablePart_startFSM_Transpiler(IEnumerable instructions) @@ -90,11 +97,10 @@ static IEnumerable ModuleDeployableSolarPanel_OnStart_Transpile FieldInfo ModuleDeployablePart_deployState = AccessTools.Field(typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.deployState)); List code = new List(instructions); - for (int i = 0; i < code.Count; i++) { if (code[i].opcode == OpCodes.Ldarg_0 - && code[i + 1].opcode == OpCodes.Ldfld && ReferenceEquals(code[i+1].operand, ModuleDeployablePart_deployState) + && code[i + 1].opcode == OpCodes.Ldfld && ReferenceEquals(code[i + 1].operand, ModuleDeployablePart_deployState) && code[i + 2].opcode == OpCodes.Brtrue_S) { code[i].opcode = OpCodes.Br_S; @@ -105,5 +111,36 @@ static IEnumerable ModuleDeployableSolarPanel_OnStart_Transpile return code; } + static bool ModuleDeployableSolarPanel_OnStart_Prefix(ModuleDeployableSolarPanel __instance, PartModule.StartState state) + { + if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) + { + return false; + } + GameEvents.onPartRepaired.Add(new EventData.OnEvent(__instance.OnPartRepaired)); + __instance.Fields["flowRate"].guiFormat = __instance.flowFormat; + __instance.Fields["flowRate"].guiUnits = (__instance.flowUnitsUseSpace ? " " : "") + __instance.flowUnits; + __instance.OnStart(state); + if (__instance.secondaryTransform == null) + { + Debug.LogError("Couldn't access secondaryTransform for raycasts"); + } + if (__instance.useRaycastForTrackingDot) + { + __instance.trackingDotTransform = __instance.secondaryTransform; + } + else + { + __instance.trackingDotTransform = __instance.panelRotationTransform; + } + if (HighLogic.LoadedSceneIsFlight && __instance.anim != null) + { + float num = ((__instance.deployState == ModuleDeployablePart.DeployState.EXTENDED) ? 1f : 0f); + __instance.anim[__instance.animationName].normalizedTime = num; + __instance.anim[__instance.animationName].enabled = true; + __instance.anim[__instance.animationName].weight = 1f; + } + return false; + } } } diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index 2965fc49..991dfba2 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -28,8 +28,7 @@ public class ModuleIndexingMismatch : BasePatch { private const string VALUENAME_MODULEPARTCONFIGID = "modulePartConfigId"; private static readonly HashSet multiModules = new HashSet(); - private static readonly Dictionary allModuleTypes = new Dictionary(); - + private static readonly Dictionary allModuleTypes = new Dictionary(); protected override Version VersionMin => new Version(1, 8, 0); protected override void ApplyPatches() @@ -44,7 +43,6 @@ protected override void ApplyPatches() { AddPatch(PatchType.Transpiler, typeof(ProtoPartSnapshot), "ConfigurePart"); } - AddPatch(PatchType.Transpiler, typeof(ShipConstruct), "LoadShip", new Type[] { typeof(ConfigNode), typeof(uint), typeof(bool), typeof(string).MakeByRefType() }); Type multiModuleType = typeof(IMultipleModuleInPart); @@ -208,8 +206,8 @@ static IEnumerable ProtoPartSnapshot_ConfigurePart_Transpiler(I return code; } - - /// + + /// /// Our own version of ProtoPartModuleSnapshot.Load(). We reimplement it because : /// - Stock would do again all the indice / module matching that we just did /// - That stock logic would prevent our handling of loading derived into base / base into derived modules. @@ -222,6 +220,7 @@ private static void LoadProtoPartSnapshotModule(ProtoPartModuleSnapshot protoMod protoModule.moduleRef = module; } + static void LoadModules(ProtoPartSnapshot protoPart, Part part) { int protoModuleCount = protoPart.modules.Count; @@ -379,31 +378,63 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl // first, remove the original module load call bool originalFound = false; - for (int i = 0; i < code.Count - 6; i++) + + if (!KSPCommunityFixes.cleanedDll) { - //// part.LoadModule(configNode2, ref moduleIndex); - // ldloc.s 6 - // ldloc.3 - // ldloca.s 39 - // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) - // dup - // pop - // pop - if (code[i].opcode == OpCodes.Ldloc_S - && code[i + 1].opcode == OpCodes.Ldloc_3 - && code[i + 2].opcode == OpCodes.Ldloca_S - && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) - && code[i + 4].opcode == OpCodes.Dup - && code[i + 5].opcode == OpCodes.Pop - && code[i + 6].opcode == OpCodes.Pop) + for (int i = 0; i < code.Count - 6; i++) { - originalFound = true; - for (int j = i; j < i + 7; j++) + //// part.LoadModule(configNode2, ref moduleIndex); + // ldloc.s 6 + // ldloc.3 + // ldloca.s 39 + // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) + // dup + // pop + // pop + if (code[i].opcode == OpCodes.Ldloc_S + && code[i + 1].opcode == OpCodes.Ldloc_3 + && code[i + 2].opcode == OpCodes.Ldloca_S + && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) + && code[i + 4].opcode == OpCodes.Dup + && code[i + 5].opcode == OpCodes.Pop + && code[i + 6].opcode == OpCodes.Pop) { - code[j].opcode = OpCodes.Nop; - code[j].operand = null; + originalFound = true; + for (int j = i; j < i + 7; j++) + { + code[j].opcode = OpCodes.Nop; + code[j].operand = null; + } + break; + } + } + } + else + { + for (int i = 0; i < code.Count - 5; i++) + { + //// part.LoadModule(configNode2, ref moduleIndex); + // ldloc.s 6 + // ldloc.3 + // ldloca.s 39 + // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) + // pop + // br + if (code[i].opcode == OpCodes.Ldloc_S + && code[i + 1].opcode == OpCodes.Ldloc_3 + && code[i + 2].opcode == OpCodes.Ldloca_S + && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) + && code[i + 4].opcode == OpCodes.Pop + && code[i + 5].opcode == OpCodes.Br) + { + originalFound = true; + for (int j = i; j < i + 6; j++) + { + code[j].opcode = OpCodes.Nop; + code[j].operand = null; + } + break; } - break; } } @@ -611,4 +642,4 @@ static void LoadShipModuleNodes(Part part, ConfigNode partNode) } } } -} +} \ No newline at end of file diff --git a/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs b/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs index f5d9fb35..07125d0a 100644 --- a/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs +++ b/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs @@ -1,8 +1,10 @@ -using System; +using HarmonyLib; +using KSP.UI.Screens; +using KSP.UI.Screens.SpaceCenter.MissionSummaryDialog; +using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; -using HarmonyLib; namespace KSPCommunityFixes.BugFixes { @@ -56,7 +58,14 @@ class RefundingOnRecovery : BasePatch protected override void ApplyPatches() { - AddPatch(PatchType.Transpiler, typeof(Funding), "onVesselRecoveryProcessing"); + if (!KSPCommunityFixes.cleanedDll) + { + AddPatch(PatchType.Transpiler, typeof(Funding), "onVesselRecoveryProcessing"); + } + else + { + AddPatch(PatchType.Prefix, typeof(Funding), "onVesselRecoveryProcessing"); + } moduleInventoryPartDerivatives.Clear(); moduleInventoryPartDerivatives.Add(nameof(ModuleInventoryPart)); @@ -119,11 +128,77 @@ static IEnumerable Funding_onVesselRecoveryProcessing_Transpile return code; } + static bool Funding_onVesselRecoveryProcessing_Prefix(Funding __instance, ProtoVessel pv, MissionRecoveryDialog mrDialog, float recoveryScore) + { + if (pv == null) + { + return false; + } + bool flag = mrDialog != null; + double num = 0.0; + List allProtoPartsIncludingCargo = pv.GetAllProtoPartsIncludingCargo(); + for (int i = 0; i < allProtoPartsIncludingCargo.Count; i++) + { + ProtoPartSnapshot protoPartSnapshot = allProtoPartsIncludingCargo[i]; + AvailablePart availablePart = null; + if (protoPartSnapshot.partInfo == null) + { + if (!string.IsNullOrEmpty(protoPartSnapshot.partName)) + { + availablePart = PartLoader.getPartInfoByName(protoPartSnapshot.partName); + } + } + else + { + availablePart = protoPartSnapshot.partInfo; + } + if (availablePart != null) + { + float num2; + float num3; + ShipConstruction.GetPartCosts(protoPartSnapshot, true, availablePart, out num2, out num3); + num2 *= recoveryScore; + num3 *= recoveryScore; + num += (double)(num2 + num3); + if (flag) + { + if (!string.Equals(availablePart.name, "kerbalEVA")) + { + mrDialog.AddPartWidget(PartWidget.Create(availablePart, num2, num3, mrDialog)); + } + for (int j = 0; j < protoPartSnapshot.resources.Count; j++) + { + ProtoPartResourceSnapshot protoPartResourceSnapshot = protoPartSnapshot.resources[j]; + PartResourceDefinition definition = PartResourceLibrary.Instance.GetDefinition(protoPartResourceSnapshot.resourceName); + if (definition != null) + { + mrDialog.AddResourceWidget(ResourceWidget.Create(definition, (float)protoPartResourceSnapshot.amount, definition.unitCost * recoveryScore, mrDialog)); + } + else + { + UnityEngine.Debug.LogError("[ShipTemplate]: No Resource definition found for " + protoPartResourceSnapshot.resourceName); + } + } + } + } + else if (flag) + { + UnityEngine.Debug.Log("[Funding]: Cannot recover " + protoPartSnapshot.partName + ". Part has no entry in PartLoader catalog. That is only OK if the part is an EVA."); + } + } + if (flag) + { + mrDialog.fundsEarned = num; + } + __instance.AddFunds(num, TransactionReasons.VesselRecovery); + return false; + } + // Derived from the ModuleInventoryPart.OnLoad() code, get stored parts cost static float GetStoredPartsCosts(ProtoPartSnapshot protoPart) { - ConstructorInfo storedPartCtor = AccessTools.Constructor(typeof(StoredPart), new[] {typeof(ConfigNode)}); + ConstructorInfo storedPartCtor = AccessTools.Constructor(typeof(StoredPart), new[] { typeof(ConfigNode) }); float cost = 0f; foreach (ProtoPartModuleSnapshot protoModule in protoPart.modules) diff --git a/KSPCommunityFixes/KSPCommunityFixes.cs b/KSPCommunityFixes/KSPCommunityFixes.cs index 0113d4cc..ef1fcf55 100644 --- a/KSPCommunityFixes/KSPCommunityFixes.cs +++ b/KSPCommunityFixes/KSPCommunityFixes.cs @@ -52,6 +52,35 @@ public static Version KspVersion } } + public static bool cleanedDll + { + get + { + String dllPath = ""; + if (Application.platform == RuntimePlatform.WindowsPlayer) + { + dllPath = KSPUtil.ApplicationRootPath + "KSP_x64_Data/Managed/Assembly-CSharp.dll"; + } + else if (Application.platform == RuntimePlatform.OSXPlayer) + { + dllPath = KSPUtil.ApplicationRootPath + "KSP.app/Contents/Resources/Data/Managed/Assembly-CSharp.dll"; + } + else if (Application.platform == RuntimePlatform.LinuxPlayer) + { + dllPath = KSPUtil.ApplicationRootPath + "KSP_Data/Managed/Assembly-CSharp.dll"; + } + if (File.Exists(dllPath)) + { + Byte[] data = File.ReadAllBytes(dllPath); + if ((data.Length < 10485760) && (KSPCommunityFixes.KspVersion >= new Version(1, 12, 0))) + { + return true; //certainly a home-cleaned dll, no official 1.12.x build of Assembly-CSharp is less than 10MBs. + } + } + return false; + } + } + static KSPCommunityFixes() { Harmony = new Harmony("KSPCommunityFixes"); From a3e4acc5c2420c61e334fab5699334981df2b1e1 Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 10:17:26 -0700 Subject: [PATCH 02/17] Added a try-catch to ModuleIndexingMismatch.cs to catch MSVC build that behave slightly differently. --- .../BugFixes/ModuleIndexingMismatch.cs | 69 ++++++++++++++----- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index 991dfba2..fd75c493 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -411,29 +411,60 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl } else { - for (int i = 0; i < code.Count - 5; i++) + try { - //// part.LoadModule(configNode2, ref moduleIndex); - // ldloc.s 6 - // ldloc.3 - // ldloca.s 39 - // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) - // pop - // br - if (code[i].opcode == OpCodes.Ldloc_S - && code[i + 1].opcode == OpCodes.Ldloc_3 - && code[i + 2].opcode == OpCodes.Ldloca_S - && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) - && code[i + 4].opcode == OpCodes.Pop - && code[i + 5].opcode == OpCodes.Br) + for (int i = 0; i < code.Count - 5; i++) { - originalFound = true; - for (int j = i; j < i + 6; j++) + //// part.LoadModule(configNode2, ref moduleIndex); + // ldloc.s 6 + // ldloc.3 + // ldloca.s 39 + // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) + // pop + // br + if (code[i].opcode == OpCodes.Ldloc_S + && code[i + 1].opcode == OpCodes.Ldloc_3 + && code[i + 2].opcode == OpCodes.Ldloca_S + && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) + && code[i + 4].opcode == OpCodes.Pop) { - code[j].opcode = OpCodes.Nop; - code[j].operand = null; + originalFound = true; + for (int j = i; j < i + 6; j++) + { + code[j].opcode = OpCodes.Nop; + code[j].operand = null; + } + break; + } + } + } + catch + { + code = new List(instructions); + for (int i = 0; i < code.Count - 5; i++) + { + //// part.LoadModule(configNode2, ref moduleIndex); + // ldloc.s 6 + // ldloc.3 + // ldloca.s 39 + // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) + // pop + // br + if (code[i].opcode == OpCodes.Ldloc_S + && code[i + 1].opcode == OpCodes.Ldloc_3 + && code[i + 2].opcode == OpCodes.Ldloca_S + && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) + && code[i + 4].opcode == OpCodes.Pop + && code[i + 5].opcode == OpCodes.Br) + { + originalFound = true; + for (int j = i; j < i + 6; j++) + { + code[j].opcode = OpCodes.Nop; + code[j].operand = null; + } + break; } - break; } } } From 7a0b751e78f413b6153600976219f934813f9e73 Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 10:22:40 -0700 Subject: [PATCH 03/17] oops. --- KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index fd75c493..7571b82a 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -426,7 +426,8 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl && code[i + 1].opcode == OpCodes.Ldloc_3 && code[i + 2].opcode == OpCodes.Ldloca_S && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) - && code[i + 4].opcode == OpCodes.Pop) + && code[i + 4].opcode == OpCodes.Pop + && code[i + 4].opcode == OpCodes.Br) { originalFound = true; for (int j = i; j < i + 6; j++) @@ -455,7 +456,7 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl && code[i + 2].opcode == OpCodes.Ldloca_S && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) && code[i + 4].opcode == OpCodes.Pop - && code[i + 5].opcode == OpCodes.Br) + && code[i + 5].opcode == OpCodes.Br_S) { originalFound = true; for (int j = i; j < i + 6; j++) From 7edfc846b190610158bd4a7164f77de3f03870ac Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 10:28:45 -0700 Subject: [PATCH 04/17] doh! --- KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index 7571b82a..6265ec9d 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -452,7 +452,7 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl // pop // br if (code[i].opcode == OpCodes.Ldloc_S - && code[i + 1].opcode == OpCodes.Ldloc_3 + && code[i + 1].opcode == OpCodes.Ldloc_S && code[i + 2].opcode == OpCodes.Ldloca_S && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) && code[i + 4].opcode == OpCodes.Pop From 07d0c411dc2e85f5009a7c6e2b473543debd041a Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 10:35:48 -0700 Subject: [PATCH 05/17] rework logic. --- .../BugFixes/ModuleIndexingMismatch.cs | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index 6265ec9d..ede1360e 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -411,35 +411,32 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl } else { - try + for (int i = 0; i < code.Count - 5; i++) { - for (int i = 0; i < code.Count - 5; i++) + //// part.LoadModule(configNode2, ref moduleIndex); + // ldloc.s 6 + // ldloc.3 + // ldloca.s 39 + // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) + // pop + // br + if (code[i].opcode == OpCodes.Ldloc_S + && code[i + 1].opcode == OpCodes.Ldloc_3 + && code[i + 2].opcode == OpCodes.Ldloca_S + && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) + && code[i + 4].opcode == OpCodes.Pop + && code[i + 5].opcode == OpCodes.Br) { - //// part.LoadModule(configNode2, ref moduleIndex); - // ldloc.s 6 - // ldloc.3 - // ldloca.s 39 - // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) - // pop - // br - if (code[i].opcode == OpCodes.Ldloc_S - && code[i + 1].opcode == OpCodes.Ldloc_3 - && code[i + 2].opcode == OpCodes.Ldloca_S - && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) - && code[i + 4].opcode == OpCodes.Pop - && code[i + 4].opcode == OpCodes.Br) + originalFound = true; + for (int j = i; j < i + 6; j++) { - originalFound = true; - for (int j = i; j < i + 6; j++) - { - code[j].opcode = OpCodes.Nop; - code[j].operand = null; - } - break; + code[j].opcode = OpCodes.Nop; + code[j].operand = null; } + break; } } - catch + if (!originalFound) { code = new List(instructions); for (int i = 0; i < code.Count - 5; i++) From 4dc42369564c23132a47950d53b40963a45107fb Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 16:43:02 -0700 Subject: [PATCH 06/17] Fix a counter bug that could produce a crash in custom builds. --- .../BugFixes/ModuleIndexingMismatch.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index ede1360e..f4597c11 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -472,9 +472,17 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl Debug.LogError($"Error applying ModuleIndexingMismatch patch : couldn't find Part.LoadModule() call in ShipConstruct.LoadShip()"); return instructions; } - - // then, insert our own module loading call before the original part nodes parsing loop - for (int i = 0; i < code.Count - 6; i++) + uint minusCount; + if (!KSPCommunityFixes.cleanedDll) + { + minusCount = 6; + } + else + { + minusCount = 5; + } + // then, insert our own module loading call before the original part nodes parsing loop + for (int i = 0; i < code.Count - minusCount; i++) { //// int moduleIndex = 0; // ldc.i4.0 NULL From 4c9f3cff9e5b9cd37140edddebe6dafb92e3c9cb Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 19:19:18 -0700 Subject: [PATCH 07/17] Lower heuristics size. --- KSPCommunityFixes/KSPCommunityFixes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KSPCommunityFixes/KSPCommunityFixes.cs b/KSPCommunityFixes/KSPCommunityFixes.cs index ef1fcf55..af0eaf2e 100644 --- a/KSPCommunityFixes/KSPCommunityFixes.cs +++ b/KSPCommunityFixes/KSPCommunityFixes.cs @@ -72,7 +72,7 @@ public static bool cleanedDll if (File.Exists(dllPath)) { Byte[] data = File.ReadAllBytes(dllPath); - if ((data.Length < 10485760) && (KSPCommunityFixes.KspVersion >= new Version(1, 12, 0))) + if ((data.Length < 10000000) && (KSPCommunityFixes.KspVersion >= new Version(1, 12, 0))) { return true; //certainly a home-cleaned dll, no official 1.12.x build of Assembly-CSharp is less than 10MBs. } From 678a7d95e36c07502f5ea126cee34390ec5d48fc Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 19:42:21 -0700 Subject: [PATCH 08/17] Revert "Fix a counter bug that could produce a crash in custom builds." This reverts commit 4dc42369564c23132a47950d53b40963a45107fb. --- .../BugFixes/ModuleIndexingMismatch.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index f4597c11..ede1360e 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -472,17 +472,9 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl Debug.LogError($"Error applying ModuleIndexingMismatch patch : couldn't find Part.LoadModule() call in ShipConstruct.LoadShip()"); return instructions; } - uint minusCount; - if (!KSPCommunityFixes.cleanedDll) - { - minusCount = 6; - } - else - { - minusCount = 5; - } - // then, insert our own module loading call before the original part nodes parsing loop - for (int i = 0; i < code.Count - minusCount; i++) + + // then, insert our own module loading call before the original part nodes parsing loop + for (int i = 0; i < code.Count - 6; i++) { //// int moduleIndex = 0; // ldc.i4.0 NULL From c21cfbfefc4c94146c43b7d668ed0b62c6eb2323 Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 22:17:30 -0700 Subject: [PATCH 09/17] As per feedback, cache and clean the function. --- .../BugFixes/DoubleCurvePreserveTangents.cs | 2 +- .../BugFixes/ExtendedDeployableParts.cs | 2 +- .../BugFixes/ModuleIndexingMismatch.cs | 2 +- .../BugFixes/RefundingOnRecovery.cs | 2 +- KSPCommunityFixes/KSPCommunityFixes.cs | 27 ++++++++----------- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs b/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs index ddcc6b8f..b32ad353 100644 --- a/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs +++ b/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs @@ -11,7 +11,7 @@ class DoubleCurvePreserveTangents : BasePatch protected override void ApplyPatches() { - if (!KSPCommunityFixes.cleanedDll) + if (!KSPCommunityFixes.IsCleanedDll) { AddPatch(PatchType.Transpiler, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); } diff --git a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs index 99101f37..c12e6d4c 100644 --- a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs +++ b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs @@ -23,7 +23,7 @@ protected override void ApplyPatches() { AddPatch(PatchType.Transpiler, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM)); - if (!KSPCommunityFixes.cleanedDll) + if (!KSPCommunityFixes.IsCleanedDll) { AddPatch(PatchType.Transpiler, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); } diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index ede1360e..fd6e2066 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -379,7 +379,7 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl // first, remove the original module load call bool originalFound = false; - if (!KSPCommunityFixes.cleanedDll) + if (!KSPCommunityFixes.IsCleanedDll) { for (int i = 0; i < code.Count - 6; i++) { diff --git a/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs b/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs index 07125d0a..3e63165f 100644 --- a/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs +++ b/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs @@ -58,7 +58,7 @@ class RefundingOnRecovery : BasePatch protected override void ApplyPatches() { - if (!KSPCommunityFixes.cleanedDll) + if (!KSPCommunityFixes.IsCleanedDll) { AddPatch(PatchType.Transpiler, typeof(Funding), "onVesselRecoveryProcessing"); } diff --git a/KSPCommunityFixes/KSPCommunityFixes.cs b/KSPCommunityFixes/KSPCommunityFixes.cs index af0eaf2e..37c4e4e7 100644 --- a/KSPCommunityFixes/KSPCommunityFixes.cs +++ b/KSPCommunityFixes/KSPCommunityFixes.cs @@ -52,32 +52,27 @@ public static Version KspVersion } } - public static bool cleanedDll + //returns true if KSP Assembly-CSharp is probably a cleaned/deobfuscated image + private static bool? cleanedDllCachedValue = null; + public static bool IsCleanedDll { get { - String dllPath = ""; - if (Application.platform == RuntimePlatform.WindowsPlayer) + if (cleanedDllCachedValue != null) { - dllPath = KSPUtil.ApplicationRootPath + "KSP_x64_Data/Managed/Assembly-CSharp.dll"; + return (bool)cleanedDllCachedValue; } - else if (Application.platform == RuntimePlatform.OSXPlayer) + else { - dllPath = KSPUtil.ApplicationRootPath + "KSP.app/Contents/Resources/Data/Managed/Assembly-CSharp.dll"; - } - else if (Application.platform == RuntimePlatform.LinuxPlayer) - { - dllPath = KSPUtil.ApplicationRootPath + "KSP_Data/Managed/Assembly-CSharp.dll"; - } - if (File.Exists(dllPath)) - { - Byte[] data = File.ReadAllBytes(dllPath); - if ((data.Length < 10000000) && (KSPCommunityFixes.KspVersion >= new Version(1, 12, 0))) + String dllPath = typeof(GameDatabase).Assembly.Location; + if ((new FileInfo(dllPath).Length < 10000000) && Versioning.version_minor.Equals(12)) { + cleanedDllCachedValue = true; return true; //certainly a home-cleaned dll, no official 1.12.x build of Assembly-CSharp is less than 10MBs. } + cleanedDllCachedValue = false; + return false; } - return false; } } From e5755879a860a00a2da7d4a689efaa07d560b598 Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Sun, 12 Apr 2026 23:11:33 -0700 Subject: [PATCH 10/17] Oops. --- KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs index c12e6d4c..b4def372 100644 --- a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs +++ b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs @@ -139,6 +139,7 @@ static bool ModuleDeployableSolarPanel_OnStart_Prefix(ModuleDeployableSolarPanel __instance.anim[__instance.animationName].normalizedTime = num; __instance.anim[__instance.animationName].enabled = true; __instance.anim[__instance.animationName].weight = 1f; + __instance.anim.Stop(__instance.animationName); } return false; } From b6bb961a9a3faee495658354f7288eab1ea27154 Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Mon, 13 Apr 2026 08:59:50 -0700 Subject: [PATCH 11/17] Add one additional prefix alternative path for easier maintenance in the future. --- .../BugFixes/ExtendedDeployableParts.cs | 85 ++++++++++++++++++- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs index b4def372..6cc52705 100644 --- a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs +++ b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs @@ -21,15 +21,15 @@ class ExtendedDeployableParts : BasePatch protected override void ApplyPatches() { - AddPatch(PatchType.Transpiler, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM)); - if (!KSPCommunityFixes.IsCleanedDll) { AddPatch(PatchType.Transpiler, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); + AddPatch(PatchType.Transpiler, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM)); } else { AddPatch(PatchType.Prefix, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); + AddPatch(PatchType.Prefix, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM)); } } @@ -111,6 +111,86 @@ static IEnumerable ModuleDeployableSolarPanel_OnStart_Transpile return code; } + + static bool ModuleDeployablePart_startFSM_Prefix(ModuleDeployableSolarPanel __instance) + { + if (__instance.useAnimation) + { + __instance.anim[__instance.animationName].wrapMode = WrapMode.ClampForever; + switch (__instance.deployState) + { + case ModuleDeployablePart.DeployState.RETRACTED: + __instance.anim[__instance.animationName].normalizedTime = 0f; + __instance.anim[__instance.animationName].enabled = true; + __instance.anim[__instance.animationName].weight = 1f; + __instance.anim[__instance.animationName].speed = 0f; + __instance.bypassSetupAnimation = true; + __instance.Events["Retract"].active = false; + __instance.Events["Extend"].active = true; + break; + case ModuleDeployablePart.DeployState.EXTENDED: + __instance.anim[__instance.animationName].normalizedTime = 1f; + __instance.anim[__instance.animationName].enabled = true; + __instance.anim[__instance.animationName].speed = 0f; + __instance.anim[__instance.animationName].weight = 1f; + __instance.Events["Extend"].active = false; + __instance.Events["Retract"].active = __instance.retractable || HighLogic.LoadedSceneIsEditor; + if (__instance.hasPivot) + { + __instance.panelRotationTransform.localRotation = __instance.currentRotation; + } + break; + case ModuleDeployablePart.DeployState.RETRACTING: + __instance.Events["Retract"].active = false; + __instance.Events["Extend"].active = false; + break; + case ModuleDeployablePart.DeployState.EXTENDING: + __instance.Events["Retract"].active = false; + __instance.Events["Extend"].active = false; + break; + } + if (__instance.deployState == ModuleDeployablePart.DeployState.RETRACTING || __instance.deployState == ModuleDeployablePart.DeployState.EXTENDING || __instance.deployState == ModuleDeployablePart.DeployState.BROKEN) + { + __instance.anim[__instance.animationName].normalizedTime = __instance.storedAnimationTime; + __instance.anim[__instance.animationName].speed = __instance.storedAnimationSpeed; + } + if (!__instance.bypassSetupAnimation) + { + __instance.anim.Play(__instance.animationName); + } + if (!__instance.playAnimationOnStart && __instance.deployState != ModuleDeployablePart.DeployState.EXTENDING && __instance.deployState != ModuleDeployablePart.DeployState.RETRACTING) + { + __instance.stopAnimation = true; + } + } + else + { + if (__instance.hasPivot) + { + __instance.panelRotationTransform.localRotation = __instance.originalRotation; + } + if (__instance.deployState != ModuleDeployablePart.DeployState.BROKEN) + { + __instance.deployState = ModuleDeployablePart.DeployState.EXTENDED; + } + __instance.Events["Retract"].active = false; + __instance.Events["Extend"].active = false; + __instance.Actions["ExtendPanelsAction"].active = false; + __instance.Actions["ExtendAction"].active = false; + __instance.Actions["RetractAction"].active = false; + __instance.Fields["status"].guiActiveEditor = false; + } + if (__instance.deployState == ModuleDeployablePart.DeployState.BROKEN) + { + __instance.Events["Retract"].active = false; + __instance.Events["Extend"].active = false; + if (__instance.panelBreakTransform) + { + __instance.panelBreakTransform.gameObject.SetActive(false); + } + } + return false; + } static bool ModuleDeployableSolarPanel_OnStart_Prefix(ModuleDeployableSolarPanel __instance, PartModule.StartState state) { if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) @@ -139,7 +219,6 @@ static bool ModuleDeployableSolarPanel_OnStart_Prefix(ModuleDeployableSolarPanel __instance.anim[__instance.animationName].normalizedTime = num; __instance.anim[__instance.animationName].enabled = true; __instance.anim[__instance.animationName].weight = 1f; - __instance.anim.Stop(__instance.animationName); } return false; } From bef5e73596051a042b053a7fc257241f4096e273 Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Mon, 13 Apr 2026 10:30:16 -0700 Subject: [PATCH 12/17] Redesign this patch to be not prefix based. --- .../BugFixes/ExtendedDeployableParts.cs | 87 ++++++++----------- 1 file changed, 36 insertions(+), 51 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs index 6cc52705..650608a5 100644 --- a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs +++ b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs @@ -21,16 +21,8 @@ class ExtendedDeployableParts : BasePatch protected override void ApplyPatches() { - if (!KSPCommunityFixes.IsCleanedDll) - { - AddPatch(PatchType.Transpiler, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); - AddPatch(PatchType.Transpiler, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM)); - } - else - { - AddPatch(PatchType.Prefix, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); - AddPatch(PatchType.Prefix, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM)); - } + AddPatch(PatchType.Transpiler, typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.startFSM)); + AddPatch(PatchType.Transpiler, typeof(ModuleDeployableSolarPanel), nameof(ModuleDeployablePart.OnStart)); } static IEnumerable ModuleDeployablePart_startFSM_Transpiler(IEnumerable instructions) @@ -95,21 +87,45 @@ static IEnumerable ModuleDeployableSolarPanel_OnStart_Transpile // We remove that entire if statement by replacing the if (Brtrue_S) by an unconditional jump (Br_S) FieldInfo ModuleDeployablePart_deployState = AccessTools.Field(typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.deployState)); - - List code = new List(instructions); - for (int i = 0; i < code.Count; i++) + if (!KSPCommunityFixes.IsCleanedDll) { - if (code[i].opcode == OpCodes.Ldarg_0 - && code[i + 1].opcode == OpCodes.Ldfld && ReferenceEquals(code[i + 1].operand, ModuleDeployablePart_deployState) - && code[i + 2].opcode == OpCodes.Brtrue_S) + List code = new List(instructions); + for (int i = 0; i < code.Count; i++) { - code[i].opcode = OpCodes.Br_S; - code[i].operand = code[i + 2].operand; // grab the target instruction from the original jump - break; + if (code[i].opcode == OpCodes.Ldarg_0 + && code[i + 1].opcode == OpCodes.Ldfld && ReferenceEquals(code[i + 1].operand, ModuleDeployablePart_deployState) + && code[i + 2].opcode == OpCodes.Brtrue_S) + { + code[i].opcode = OpCodes.Br_S; + code[i].operand = code[i + 2].operand; // grab the target instruction from the original jump + break; + } } + + return code; } + else + { + List code = new List(instructions); + for (int i = 0; i < code.Count; i++) + { + if (code[i].opcode == OpCodes.Ldarg_0 + && code[i + 1].opcode == OpCodes.Ldfld + && code[i + 2].opcode == OpCodes.Ldarg_0 + && code[i + 3].opcode == OpCodes.Ldfld + && code[i + 4].opcode == OpCodes.Callvirt) + { + code[i].opcode = OpCodes.Nop; + code[i + 1].opcode = OpCodes.Nop; + code[i + 2].opcode = OpCodes.Nop; + code[i + 3].opcode = OpCodes.Nop; + code[i + 4].opcode = OpCodes.Nop; // change call to stop to nop, no idea why this works and the other doesn't in cleaned dlls + break; + } + } - return code; + return code; + } } static bool ModuleDeployablePart_startFSM_Prefix(ModuleDeployableSolarPanel __instance) @@ -191,36 +207,5 @@ static bool ModuleDeployablePart_startFSM_Prefix(ModuleDeployableSolarPanel __in } return false; } - static bool ModuleDeployableSolarPanel_OnStart_Prefix(ModuleDeployableSolarPanel __instance, PartModule.StartState state) - { - if (!HighLogic.LoadedSceneIsEditor && !HighLogic.LoadedSceneIsFlight) - { - return false; - } - GameEvents.onPartRepaired.Add(new EventData.OnEvent(__instance.OnPartRepaired)); - __instance.Fields["flowRate"].guiFormat = __instance.flowFormat; - __instance.Fields["flowRate"].guiUnits = (__instance.flowUnitsUseSpace ? " " : "") + __instance.flowUnits; - __instance.OnStart(state); - if (__instance.secondaryTransform == null) - { - Debug.LogError("Couldn't access secondaryTransform for raycasts"); - } - if (__instance.useRaycastForTrackingDot) - { - __instance.trackingDotTransform = __instance.secondaryTransform; - } - else - { - __instance.trackingDotTransform = __instance.panelRotationTransform; - } - if (HighLogic.LoadedSceneIsFlight && __instance.anim != null) - { - float num = ((__instance.deployState == ModuleDeployablePart.DeployState.EXTENDED) ? 1f : 0f); - __instance.anim[__instance.animationName].normalizedTime = num; - __instance.anim[__instance.animationName].enabled = true; - __instance.anim[__instance.animationName].weight = 1f; - } - return false; - } } } From 0eb3f9bc0f4666a96532a2976fc35ed143026c68 Mon Sep 17 00:00:00 2001 From: Raymond Beehler <21GunSoftware@comcast.net> Date: Wed, 15 Apr 2026 13:35:27 -0700 Subject: [PATCH 13/17] As per the review, make these overrides. --- KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs | 8 ++++---- KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs b/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs index b32ad353..47061615 100644 --- a/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs +++ b/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs @@ -17,7 +17,7 @@ protected override void ApplyPatches() } else { - AddPatch(PatchType.Prefix, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); + AddPatch(PatchType.Override, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); } } @@ -45,13 +45,13 @@ static IEnumerable DoubleCurve_RecomputeTangents_Transpiler(IEn return code; } - static bool DoubleCurve_RecomputeTangents_Prefix(DoubleCurve __instance) + static void DoubleCurve_RecomputeTangents_Override(DoubleCurve __instance) { int count = __instance.keys.Count; DoubleKeyframe doubleKeyframe; if (count == 1) { - return false; + return; } doubleKeyframe = __instance.keys[0]; if (doubleKeyframe.autoTangent) @@ -81,7 +81,7 @@ static bool DoubleCurve_RecomputeTangents_Prefix(DoubleCurve __instance) } } } - return false; + return; } } } diff --git a/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs b/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs index 3e63165f..c33052b3 100644 --- a/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs +++ b/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs @@ -64,7 +64,7 @@ protected override void ApplyPatches() } else { - AddPatch(PatchType.Prefix, typeof(Funding), "onVesselRecoveryProcessing"); + AddPatch(PatchType.Override, typeof(Funding), "onVesselRecoveryProcessing"); } moduleInventoryPartDerivatives.Clear(); @@ -128,11 +128,11 @@ static IEnumerable Funding_onVesselRecoveryProcessing_Transpile return code; } - static bool Funding_onVesselRecoveryProcessing_Prefix(Funding __instance, ProtoVessel pv, MissionRecoveryDialog mrDialog, float recoveryScore) + static void Funding_onVesselRecoveryProcessing_Override(Funding __instance, ProtoVessel pv, MissionRecoveryDialog mrDialog, float recoveryScore) { if (pv == null) { - return false; + return; } bool flag = mrDialog != null; double num = 0.0; @@ -191,7 +191,7 @@ static bool Funding_onVesselRecoveryProcessing_Prefix(Funding __instance, ProtoV mrDialog.fundsEarned = num; } __instance.AddFunds(num, TransactionReasons.VesselRecovery); - return false; + return; } From e2424c39ea16305d8c8308cea45f5bc6d0ce6c71 Mon Sep 17 00:00:00 2001 From: Phantomical Date: Wed, 15 Apr 2026 23:00:32 -0700 Subject: [PATCH 14/17] Simplify DoubleCurvePreserveTangents We don't actually need a transpiler for this one. A prefix patch can do the job just as well. --- .../BugFixes/DoubleCurvePreserveTangents.cs | 74 ++----------------- 1 file changed, 6 insertions(+), 68 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs b/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs index 47061615..e4af8f87 100644 --- a/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs +++ b/KSPCommunityFixes/BugFixes/DoubleCurvePreserveTangents.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; -using HarmonyLib; -using System.Reflection.Emit; +using System.Runtime.CompilerServices; namespace KSPCommunityFixes.BugFixes { @@ -11,77 +9,17 @@ class DoubleCurvePreserveTangents : BasePatch protected override void ApplyPatches() { - if (!KSPCommunityFixes.IsCleanedDll) - { - AddPatch(PatchType.Transpiler, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); - } - else - { - AddPatch(PatchType.Override, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); - } + AddPatch(PatchType.Prefix, typeof(DoubleCurve), nameof(DoubleCurve.RecomputeTangents)); } - static IEnumerable DoubleCurve_RecomputeTangents_Transpiler(IEnumerable instructions) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool DoubleCurve_RecomputeTangents_Prefix(DoubleCurve __instance) { // The existing function has a test if ( count == 1 ) and, if true, it // will flatten the tangents of the key regardless of if it is // set to autotangent or not. Since the tangents of a single-key - // curve don't matter, let's just return. - List code = new List(instructions); - for (int i = 1; i < code.Count; ++i) - { - if (code[i].opcode == OpCodes.Ldc_I4_1 && code[i - 1].opcode != OpCodes.Ldloc_1) - { - code[i] = new CodeInstruction(OpCodes.Ret); - code[i + 1] = new CodeInstruction(OpCodes.Nop); - code[i + 2] = new CodeInstruction(OpCodes.Nop); - code[i + 3] = new CodeInstruction(OpCodes.Nop); - code[i + 4] = new CodeInstruction(OpCodes.Nop); - code[i + 5] = new CodeInstruction(OpCodes.Nop); - break; - } - } - - return code; - } - - static void DoubleCurve_RecomputeTangents_Override(DoubleCurve __instance) - { - int count = __instance.keys.Count; - DoubleKeyframe doubleKeyframe; - if (count == 1) - { - return; - } - doubleKeyframe = __instance.keys[0]; - if (doubleKeyframe.autoTangent) - { - doubleKeyframe.inTangent = 0.0; - doubleKeyframe.outTangent = (__instance.keys[1].value - doubleKeyframe.value) / (__instance.keys[1].time - doubleKeyframe.time); - __instance.keys[0] = doubleKeyframe; - } - int num3 = count - 1; - doubleKeyframe = __instance.keys[num3]; - if (doubleKeyframe.autoTangent) - { - doubleKeyframe.inTangent = (doubleKeyframe.value - __instance.keys[num3 - 1].value) / (doubleKeyframe.time - __instance.keys[num3 - 1].value); - doubleKeyframe.outTangent = 0.0; - __instance.keys[num3] = doubleKeyframe; - } - if (count > 2) - { - for (int i = 1; i < num3; i++) - { - doubleKeyframe = __instance.keys[i]; - if (doubleKeyframe.autoTangent) - { - double num4 = (doubleKeyframe.value - __instance.keys[i - 1].value) / (doubleKeyframe.time - __instance.keys[i - 1].value); - double num5 = (__instance.keys[i + 1].value - doubleKeyframe.value) / (__instance.keys[i + 1].time - doubleKeyframe.time); - doubleKeyframe.inTangent = (doubleKeyframe.outTangent = (num4 + num5) * 0.5); - } - } - } - return; + // curve don't matter, we skip the function in that case. + return __instance.keys.Count != 1; } } } From f2550aafdfc144301d1f034d0dc4fb201d12865a Mon Sep 17 00:00:00 2001 From: Phantomical Date: Wed, 15 Apr 2026 23:39:17 -0700 Subject: [PATCH 15/17] Simplify ShipConstruct.LoadShip transpiler patch --- .../BugFixes/ModuleIndexingMismatch.cs | 195 ++++++------------ 1 file changed, 64 insertions(+), 131 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs index fd6e2066..1c19443a 100644 --- a/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs +++ b/KSPCommunityFixes/BugFixes/ModuleIndexingMismatch.cs @@ -28,7 +28,7 @@ public class ModuleIndexingMismatch : BasePatch { private const string VALUENAME_MODULEPARTCONFIGID = "modulePartConfigId"; private static readonly HashSet multiModules = new HashSet(); - private static readonly Dictionary allModuleTypes = new Dictionary(); + private static readonly Dictionary allModuleTypes = new Dictionary(); protected override Version VersionMin => new Version(1, 8, 0); protected override void ApplyPatches() @@ -206,8 +206,8 @@ static IEnumerable ProtoPartSnapshot_ConfigurePart_Transpiler(I return code; } - - /// + + /// /// Our own version of ProtoPartModuleSnapshot.Load(). We reimplement it because : /// - Stock would do again all the indice / module matching that we just did /// - That stock logic would prevent our handling of loading derived into base / base into derived modules. @@ -347,7 +347,7 @@ static void LoadModules(ProtoPartSnapshot protoPart, Part part) { // see https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/236 // when persisted type is a derived type of the module type, or when the module type is a derived type of the persisted type, still attempt to load it. - if (protoModuleIdx < partModuleCount && allModuleTypes.TryGetValue(protoModule.moduleName, out Type moduleType) + if (protoModuleIdx < partModuleCount && allModuleTypes.TryGetValue(protoModule.moduleName, out Type moduleType) && (part.Modules[protoModuleIdx].GetType().IsAssignableFrom(moduleType) || moduleType.IsInstanceOfType(part.Modules[protoModuleIdx]))) { LoadProtoPartSnapshotModule(protoPart.modules[protoModuleIdx], part.modules[protoModuleIdx]); @@ -374,136 +374,69 @@ static IEnumerable ShipConstruct_LoadShip_Transpiler(IEnumerabl MethodInfo LoadShipModuleNodes = AccessTools.Method(typeof(ModuleIndexingMismatch), nameof(ModuleIndexingMismatch.LoadShipModuleNodes)); MethodInfo ConfigNode_get_nodes = AccessTools.PropertyGetter(typeof(ConfigNode), nameof(ConfigNode.nodes)); - List code = new List(instructions); + var matcher = new CodeMatcher(instructions); // first, remove the original module load call - bool originalFound = false; - - if (!KSPCommunityFixes.IsCleanedDll) - { - for (int i = 0; i < code.Count - 6; i++) - { - //// part.LoadModule(configNode2, ref moduleIndex); - // ldloc.s 6 - // ldloc.3 - // ldloca.s 39 - // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) - // dup - // pop - // pop - if (code[i].opcode == OpCodes.Ldloc_S - && code[i + 1].opcode == OpCodes.Ldloc_3 - && code[i + 2].opcode == OpCodes.Ldloca_S - && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) - && code[i + 4].opcode == OpCodes.Dup - && code[i + 5].opcode == OpCodes.Pop - && code[i + 6].opcode == OpCodes.Pop) - { - originalFound = true; - for (int j = i; j < i + 7; j++) - { - code[j].opcode = OpCodes.Nop; - code[j].operand = null; - } - break; - } - } - } - else - { - for (int i = 0; i < code.Count - 5; i++) - { - //// part.LoadModule(configNode2, ref moduleIndex); - // ldloc.s 6 - // ldloc.3 - // ldloca.s 39 - // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) - // pop - // br - if (code[i].opcode == OpCodes.Ldloc_S - && code[i + 1].opcode == OpCodes.Ldloc_3 - && code[i + 2].opcode == OpCodes.Ldloca_S - && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) - && code[i + 4].opcode == OpCodes.Pop - && code[i + 5].opcode == OpCodes.Br) - { - originalFound = true; - for (int j = i; j < i + 6; j++) - { - code[j].opcode = OpCodes.Nop; - code[j].operand = null; - } - break; - } - } - if (!originalFound) - { - code = new List(instructions); - for (int i = 0; i < code.Count - 5; i++) - { - //// part.LoadModule(configNode2, ref moduleIndex); - // ldloc.s 6 - // ldloc.3 - // ldloca.s 39 - // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) - // pop - // br - if (code[i].opcode == OpCodes.Ldloc_S - && code[i + 1].opcode == OpCodes.Ldloc_S - && code[i + 2].opcode == OpCodes.Ldloca_S - && code[i + 3].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 3].operand, Part_LoadModule) - && code[i + 4].opcode == OpCodes.Pop - && code[i + 5].opcode == OpCodes.Br_S) - { - originalFound = true; - for (int j = i; j < i + 6; j++) - { - code[j].opcode = OpCodes.Nop; - code[j].operand = null; - } - break; - } - } - } - } - - if (!originalFound) - { - Debug.LogError($"Error applying ModuleIndexingMismatch patch : couldn't find Part.LoadModule() call in ShipConstruct.LoadShip()"); - return instructions; - } + //// part.LoadModule(configNode2, ref moduleIndex); + // ldloc.s 6 + // ldloc.3 + // ldloca.s 39 + // callvirt instance class PartModule Part::LoadModule(class ConfigNode, int32&) + // dup ; only if obfuscated + // pop ; only if obfuscated + // pop + matcher + .MatchStartForward( + new CodeMatch(OpCodes.Ldloc_S), + new CodeMatch(OpCodes.Ldloc_3), + new CodeMatch(OpCodes.Ldloca_S), + new CodeMatch(OpCodes.Callvirt, Part_LoadModule) + ) + .ThrowIfInvalid("couldn't find Part.LoadModule() call in ShipConstruct.LoadShip()"); + + // The dup;pop; pair might not be present in a deobfuscated assembly + // so we push a dummy value on the stack instead of deleting them. + // + // One of these appears to have a label, so removing it causes patching + // to fail. The same applies to using SetInstruction. + matcher + .SetAndAdvance(OpCodes.Nop, null) + .SetAndAdvance(OpCodes.Nop, null) + .SetAndAdvance(OpCodes.Nop, null) + .SetAndAdvance(OpCodes.Ldnull, null); // then, insert our own module loading call before the original part nodes parsing loop - for (int i = 0; i < code.Count - 6; i++) - { - //// int moduleIndex = 0; - // ldc.i4.0 NULL - // stloc.s 39(System.Int32) - //// int m = 0; - // ldc.i4.0 NULL - // stloc.s 45(System.Int32) - //// int count5 = configNode.nodes.Count - // ldloc.2 NULL - // callvirt ConfigNodeList ConfigNode::get_nodes() - // dup NULL - // pop NULL - // callvirt System.Int32 ConfigNodeList::get_Count() - - if (code[i].opcode == OpCodes.Ldc_I4_0 - && code[i + 1].opcode == OpCodes.Stloc_S - && code[i + 2].opcode == OpCodes.Ldc_I4_0 - && code[i + 3].opcode == OpCodes.Stloc_S - && code[i + 4].opcode == OpCodes.Ldloc_2 - && code[i + 5].opcode == OpCodes.Callvirt && ReferenceEquals(code[i + 5].operand, ConfigNode_get_nodes)) - { - code.Insert(i, new CodeInstruction(OpCodes.Ldloc_S, 6)); // ldloc.s 6 is the Part local variable - code.Insert(i + 1, new CodeInstruction(OpCodes.Ldloc_2)); // Ldloc_2 is the part ConfigNode variable - code.Insert(i + 2, new CodeInstruction(OpCodes.Call, LoadShipModuleNodes)); - break; - } - } - - return code; + //// int moduleIndex = 0; + // ldc.i4.0 NULL + // stloc.s 39(System.Int32) + //// int m = 0; + // ldc.i4.0 NULL + // stloc.s 45(System.Int32) + //// int count5 = configNode.nodes.Count + // ldloc.2 NULL + // callvirt ConfigNodeList ConfigNode::get_nodes() + // dup NULL ; only if obfuscated + // pop NULL ; only obfuscated + // callvirt System.Int32 ConfigNodeList::get_Count() + matcher + .Start() + .MatchStartForward( + new CodeMatch(OpCodes.Ldc_I4_0), + new CodeMatch(OpCodes.Stloc_S), + new CodeMatch(OpCodes.Ldc_I4_0), + new CodeMatch(OpCodes.Stloc_S), + new CodeMatch(OpCodes.Ldloc_2), + new CodeMatch(OpCodes.Callvirt, ConfigNode_get_nodes) + ) + .ThrowIfInvalid("Could not find the call to ConfigNode::get_nodes") + .Advance(6) + .Insert( + new CodeInstruction(OpCodes.Ldloc_S, 6), // Part local variable + new CodeInstruction(OpCodes.Ldloc_2), // part ConfigNode variable + new CodeInstruction(OpCodes.Call, LoadShipModuleNodes) + ); + + return matcher.Instructions(); } private static readonly List currentModuleNodes = new List(); @@ -649,7 +582,7 @@ static void LoadShipModuleNodes(Part part, ConfigNode partNode) { // see https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/236 // when persisted type is a derived type of the module type, or when the module type is a derived type of the persisted type, still attempt to load it. - if (moduleNodeIdx < partModuleCount + if (moduleNodeIdx < partModuleCount && allModuleTypes.TryGetValue(nodeModuleName, out Type moduleType) && (part.Modules[moduleNodeIdx].GetType().IsAssignableFrom(moduleType) || moduleType.IsInstanceOfType(part.Modules[moduleNodeIdx]))) { From 002f29a1f9b7bd943125a6ba64008549fe751d76 Mon Sep 17 00:00:00 2001 From: Phantomical Date: Thu, 16 Apr 2026 02:43:58 -0700 Subject: [PATCH 16/17] Make RefundingOnRecovery patch work in both cases --- .../BugFixes/RefundingOnRecovery.cs | 140 ++++-------------- 1 file changed, 26 insertions(+), 114 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs b/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs index c33052b3..6934733e 100644 --- a/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs +++ b/KSPCommunityFixes/BugFixes/RefundingOnRecovery.cs @@ -58,14 +58,7 @@ class RefundingOnRecovery : BasePatch protected override void ApplyPatches() { - if (!KSPCommunityFixes.IsCleanedDll) - { - AddPatch(PatchType.Transpiler, typeof(Funding), "onVesselRecoveryProcessing"); - } - else - { - AddPatch(PatchType.Override, typeof(Funding), "onVesselRecoveryProcessing"); - } + AddPatch(PatchType.Transpiler, typeof(Funding), "onVesselRecoveryProcessing"); moduleInventoryPartDerivatives.Clear(); moduleInventoryPartDerivatives.Add(nameof(ModuleInventoryPart)); @@ -81,6 +74,7 @@ static IEnumerable Funding_onVesselRecoveryProcessing_Transpile // public static float GetPartCosts(ProtoPartSnapshot protoPart, bool includeModuleCosts, AvailablePart aP, out float dryCost, out float fuelCost) MethodInfo getPartCostsMethod = AccessTools.Method(typeof(ShipConstruction), "GetPartCosts", new[] { typeof(ProtoPartSnapshot), typeof(bool), typeof(AvailablePart), typeof(float).MakeByRefType(), typeof(float).MakeByRefType() }); + MethodInfo getStoredPartsModuleCosts = AccessTools.Method(typeof(RefundingOnRecovery), nameof(GetStoredPartsCosts)); if (getPartCostsMethod == null) { @@ -88,113 +82,32 @@ static IEnumerable Funding_onVesselRecoveryProcessing_Transpile return instructions; } - - int insertionIndex = -1; - object dryCostVarOperand = default; - OpCode protoPartSnapshotOpcode = default; - - for (int i = 4; i < code.Count - 1; i++) - { - if (code[i].opcode == OpCodes.Call && (MethodInfo)code[i].operand == getPartCostsMethod) - { - // change includeModuleCosts from false to true - code[i - 4].opcode = OpCodes.Ldc_I4_1; - // Insert our method call after "pop" - insertionIndex = i + 1; - // find the variables we need - dryCostVarOperand = code[i - 2].operand; - protoPartSnapshotOpcode = code[i - 5].opcode; - break; - } - } - - if (insertionIndex == -1) - { - UnityEngine.Debug.LogError("Error patching recovery costs : transpiler patch failed"); - return instructions; - } - - List instructionsToInsert = new List(); - MethodInfo getStoredPartsModuleCosts = AccessTools.Method(typeof(RefundingOnRecovery), nameof(GetStoredPartsCosts)); - - instructionsToInsert.Add(new CodeInstruction(OpCodes.Ldloc_S, dryCostVarOperand)); - instructionsToInsert.Add(new CodeInstruction(protoPartSnapshotOpcode)); - instructionsToInsert.Add(new CodeInstruction(OpCodes.Call, getStoredPartsModuleCosts)); - instructionsToInsert.Add(new CodeInstruction(OpCodes.Sub)); - instructionsToInsert.Add(new CodeInstruction(OpCodes.Stloc_S, dryCostVarOperand)); - - code.InsertRange(insertionIndex, instructionsToInsert); - - return code; - } - - static void Funding_onVesselRecoveryProcessing_Override(Funding __instance, ProtoVessel pv, MissionRecoveryDialog mrDialog, float recoveryScore) - { - if (pv == null) - { - return; - } - bool flag = mrDialog != null; - double num = 0.0; - List allProtoPartsIncludingCargo = pv.GetAllProtoPartsIncludingCargo(); - for (int i = 0; i < allProtoPartsIncludingCargo.Count; i++) - { - ProtoPartSnapshot protoPartSnapshot = allProtoPartsIncludingCargo[i]; - AvailablePart availablePart = null; - if (protoPartSnapshot.partInfo == null) - { - if (!string.IsNullOrEmpty(protoPartSnapshot.partName)) - { - availablePart = PartLoader.getPartInfoByName(protoPartSnapshot.partName); - } - } - else - { - availablePart = protoPartSnapshot.partInfo; - } - if (availablePart != null) - { - float num2; - float num3; - ShipConstruction.GetPartCosts(protoPartSnapshot, true, availablePart, out num2, out num3); - num2 *= recoveryScore; - num3 *= recoveryScore; - num += (double)(num2 + num3); - if (flag) - { - if (!string.Equals(availablePart.name, "kerbalEVA")) - { - mrDialog.AddPartWidget(PartWidget.Create(availablePart, num2, num3, mrDialog)); - } - for (int j = 0; j < protoPartSnapshot.resources.Count; j++) - { - ProtoPartResourceSnapshot protoPartResourceSnapshot = protoPartSnapshot.resources[j]; - PartResourceDefinition definition = PartResourceLibrary.Instance.GetDefinition(protoPartResourceSnapshot.resourceName); - if (definition != null) - { - mrDialog.AddResourceWidget(ResourceWidget.Create(definition, (float)protoPartResourceSnapshot.amount, definition.unitCost * recoveryScore, mrDialog)); - } - else - { - UnityEngine.Debug.LogError("[ShipTemplate]: No Resource definition found for " + protoPartResourceSnapshot.resourceName); - } - } - } - } - else if (flag) - { - UnityEngine.Debug.Log("[Funding]: Cannot recover " + protoPartSnapshot.partName + ". Part has no entry in PartLoader catalog. That is only OK if the part is an EVA."); - } - } - if (flag) - { - mrDialog.fundsEarned = num; - } - __instance.AddFunds(num, TransactionReasons.VesselRecovery); - return; + var matcher = new CodeMatcher(instructions); + matcher + .MatchStartForward(new CodeMatch(OpCodes.Call, getPartCostsMethod)) + .Advance(-5); + + var loadProtoPart = new CodeInstruction(matcher.Instruction); + matcher + .Advance(1) // index - 4 + // change includeModuleCosts from false to true + .SetInstruction(new CodeInstruction(OpCodes.Ldc_I4_1)) + .Advance(2); // index - 2 + + var dryCostVarOperand = matcher.Operand; + matcher + .Advance(3) // index + 1 + .Insert( + new CodeInstruction(OpCodes.Ldloc_S, dryCostVarOperand), + loadProtoPart, + new CodeInstruction(OpCodes.Call, getStoredPartsModuleCosts), + new CodeInstruction(OpCodes.Sub), + new CodeInstruction(OpCodes.Stloc_S, dryCostVarOperand) + ); + + return matcher.Instructions(); } - // Derived from the ModuleInventoryPart.OnLoad() code, get stored parts cost static float GetStoredPartsCosts(ProtoPartSnapshot protoPart) { @@ -256,7 +169,6 @@ static float GetStoredPartsCosts(ProtoPartSnapshot protoPart) return cost; } - } public class ModulePartCostTest : PartModule, IPartCostModifier From 77bc5d374b108771fd30526c70630af0037d7c00 Mon Sep 17 00:00:00 2001 From: Phantomical Date: Thu, 16 Apr 2026 03:02:57 -0700 Subject: [PATCH 17/17] Make the ExtendedDeployableParts transpiler both ways --- .../BugFixes/ExtendedDeployableParts.cs | 57 ++++++------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs index 650608a5..6f1921f1 100644 --- a/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs +++ b/KSPCommunityFixes/BugFixes/ExtendedDeployableParts.cs @@ -1,4 +1,5 @@ -using HarmonyLib; +using CommNet.Network; +using HarmonyLib; using System; using System.Collections.Generic; using System.Reflection; @@ -84,48 +85,26 @@ static IEnumerable ModuleDeployableSolarPanel_OnStart_Transpile // anim.Stop(animationName); //} - // We remove that entire if statement by replacing the if (Brtrue_S) by an unconditional jump (Br_S) + // We remove that entire if statement by making the if (Brtrue_S) unconditional FieldInfo ModuleDeployablePart_deployState = AccessTools.Field(typeof(ModuleDeployablePart), nameof(ModuleDeployablePart.deployState)); - if (!KSPCommunityFixes.IsCleanedDll) - { - List code = new List(instructions); - for (int i = 0; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Ldarg_0 - && code[i + 1].opcode == OpCodes.Ldfld && ReferenceEquals(code[i + 1].operand, ModuleDeployablePart_deployState) - && code[i + 2].opcode == OpCodes.Brtrue_S) - { - code[i].opcode = OpCodes.Br_S; - code[i].operand = code[i + 2].operand; // grab the target instruction from the original jump - break; - } - } - return code; - } - else - { - List code = new List(instructions); - for (int i = 0; i < code.Count; i++) - { - if (code[i].opcode == OpCodes.Ldarg_0 - && code[i + 1].opcode == OpCodes.Ldfld - && code[i + 2].opcode == OpCodes.Ldarg_0 - && code[i + 3].opcode == OpCodes.Ldfld - && code[i + 4].opcode == OpCodes.Callvirt) - { - code[i].opcode = OpCodes.Nop; - code[i + 1].opcode = OpCodes.Nop; - code[i + 2].opcode = OpCodes.Nop; - code[i + 3].opcode = OpCodes.Nop; - code[i + 4].opcode = OpCodes.Nop; // change call to stop to nop, no idea why this works and the other doesn't in cleaned dlls - break; - } - } + var matcher = new CodeMatcher(instructions); + matcher + .MatchStartForward( + new CodeMatch(OpCodes.Ldarg_0), + new CodeMatch(OpCodes.Ldfld, ModuleDeployablePart_deployState), + new CodeMatch(OpCodes.Brtrue_S) + ); - return code; - } + if (!matcher.IsValid) + return matcher.Instructions(); + + matcher + .RemoveInstructions(2) + .Insert(new CodeInstruction(OpCodes.Ldc_I4_1)); + + return matcher.Instructions(); } static bool ModuleDeployablePart_startFSM_Prefix(ModuleDeployableSolarPanel __instance)