Skip to content

Commit

Permalink
New KSP bugfix : EVAKerbalRecovery, see issue #43 (thanks to @JonnyOThan
Browse files Browse the repository at this point in the history
 for bug-reporting)
  • Loading branch information
gotmachine committed Jun 9, 2022
1 parent 7d23b53 commit 051e0ec
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 1 deletion.
4 changes: 4 additions & 0 deletions GameData/KSPCommunityFixes/Settings.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ KSP_COMMUNITY_FIXES
// causing loss of vessel effects audio and random volume or left/right channel weirdness.
LostSoundAfterSceneSwitch = true
// Fix recovery of EVAing kerbals either causing their inventory to be recovered twice or the
// science data they carry not being recovered, depending on the EVA kerbal variant/suit.
EVAKerbalRecovery = true
// ##########################
// Obsolete bugfixes
// ##########################
Expand Down
99 changes: 99 additions & 0 deletions KSPCommunityFixes/BugFixes/EVAKerbalRecovery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// see https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/43

using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace KSPCommunityFixes.BugFixes
{
public class EVAKerbalRecovery : BasePatch
{
protected override Version VersionMin => new Version(1, 11, 0);

protected override void ApplyPatches(ref List<PatchInfo> patches)
{
patches.Add(new PatchInfo(
PatchMethodType.Transpiler,
AccessTools.Method(typeof(ProtoVessel), nameof(ProtoVessel.GetAllProtoPartsIncludingCargo)),
this));
}

static IEnumerable<CodeInstruction> ProtoVessel_GetAllProtoPartsIncludingCargo_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator ilGen)
{
MethodInfo mInfo_IsEVAKerbal = AccessTools.Method(typeof(EVAKerbalRecovery), nameof(EVAKerbalRecovery.IsEVAKerbal));
MethodInfo mInfo_GetAllProtoPartsFromCrew = AccessTools.Method(typeof(ProtoVessel), nameof(ProtoVessel.GetAllProtoPartsFromCrew));

List<CodeInstruction> code = new List<CodeInstruction>(instructions);

for (int i = 0; i < code.Count; i++)
{
// find "if (partInfoByName == null || string.Equals(partInfoByName.name, "kerbalEVA"))"
// and nop the whole "string.Equals(partInfoByName.name, "kerbalEVA")" condition
// Note that this condition was added in KSP 1.12.0 (probably to attempt to fix cargo parts being recovered twice)
// so it won't be found in KSP 1.11.x. Since this "fix" doesn't work and create even more problems, we remove it.
if (code[i].opcode == OpCodes.Ldstr && code[i].operand is string str && str == "kerbalEVA")
{
int j = i + 1;
// advance to the jump
while (code[j].opcode != OpCodes.Brtrue)
j++;

// rewind and nop all instructions until the "partInfoByName == null" jump is reached
while (code[j].opcode != OpCodes.Brfalse)
{
code[j].opcode = OpCodes.Nop;
code[j].operand = null;
j--;
}
}

// find the "list.AddRange(GetAllProtoPartsFromCrew());" line at the end of the method and don't execute it for EVA kerbals
// this is the proper fix to the EVA kerbal inventories being recovered twice.
if (code[i].opcode == OpCodes.Call && ReferenceEquals(code[i].operand, mInfo_GetAllProtoPartsFromCrew))
{
// search the begining of line
int insertPos = i;
while (code[insertPos].opcode != OpCodes.Ldloc_0)
insertPos--;

// search the final "return list;" line
int jumpPos = i;
while (code[jumpPos].opcode != OpCodes.Ret)
jumpPos++;

while (code[jumpPos].opcode != OpCodes.Ldloc_0)
jumpPos--;

// and add a label so we can jump to it
Label jump = ilGen.DefineLabel();
code[jumpPos].labels.Add(jump);

// then add a jump to bypass the "list.AddRange(GetAllProtoPartsFromCrew());" line if our
// IsEVAKerbal() method returns true
CodeInstruction[] insert = new CodeInstruction[3];
insert[0] = new CodeInstruction(OpCodes.Ldarg_0);
insert[1] = new CodeInstruction(OpCodes.Call, mInfo_IsEVAKerbal);
insert[2] = new CodeInstruction(OpCodes.Brtrue, jump);

code.InsertRange(insertPos, insert);
break;
}
}

return code;
}

private static bool IsEVAKerbal(ProtoVessel pv)
{
// EVA kerbals are always 1 part vessels
if (pv.protoPartSnapshots.Count != 1)
return false;

return pv.protoPartSnapshots[0].partInfo.name.StartsWith("kerbalEVA");
}
}


}
1 change: 1 addition & 0 deletions KSPCommunityFixes/KSPCommunityFixes.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<Compile Include="BugFixes\DockingPortRotationDriftAndFixes.cs" />
<Compile Include="BugFixes\ExtendedDeployableParts.cs" />
<Compile Include="BugFixes\DeltaVHideWhenDisabled.cs" />
<Compile Include="BugFixes\EVAKerbalRecovery.cs" />
<Compile Include="BugFixes\KerbalTooltipMaxSustainedG.cs" />
<Compile Include="BugFixes\LostSoundAfterSceneSwitch.cs" />
<Compile Include="BugFixes\PackedPartsRotation.cs" />
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ User options are available from the "ESC" in-game settings menu :<br/><img src="
- **PartListTooltipIconSpin** [KSP 1.8.0 - 1.12.3]<br/> Fix editor tooltip part icons not spinning anymore after hovering on a greyed out surface attachable only part while the editor is empty.
- **[ScatterDistribution](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/41)** [KSP 1.8.0 - 1.12.3]<br/>Fix incorrect terrain scatter distribution when a partial longitude range is defined in the PQSLandControl definition.
- **[LostSoundAfterSceneSwitch](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/42)** [KSP 1.12.0 - 1.12.3]<br/> Fix audio source not being centered/aligned with the current vessel after scene switches, causing loss of vessel effects audio and random volume or left/right channel weirdness.
- **[EVAKerbalRecovery](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/43)** [KSP 1.11.0 - 1.12.3]<br/> Fix recovery of EVAing kerbals either causing their inventory to be recovered twice or the science data they carry not being recovered, depending on the EVA kerbal variant/suit.


#### Quality of Life tweaks

Expand Down Expand Up @@ -125,9 +127,12 @@ If doing so in the `Debug` configuration and if your KSP install is modified to

### Changelog

##### 1.16.0
- New KSP bugfix : [EVAKerbalRecovery](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/43) (thanks to @JonnyOThan for bug-reporting)

##### 1.15.0
- New KSP bugfix : [ScatterDistribution](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/41) (credit to @R-T-B)
- New KSP bugfix : LostSoundAfterSceneSwitch (credit to @ensou04)
- New KSP bugfix : [LostSoundAfterSceneSwitch](https://github.com/KSPModdingLibs/KSPCommunityFixes/issues/42) (credit to @ensou04)
- Fixed KerbalInventoryPersistence patch not being applied on KSP 1.12.3

##### 1.14.1
Expand Down

3 comments on commit 051e0ec

@JonnyOThan
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, is there any concern with ships that contain an eva kerbal part but that part is not the entire vessel? The two cases I can think of are the command chair and the klaw - sometimes grabbing a kerbal with the klaw is a viable rescue option.

@gotmachine
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, those are problematic cases.
The issue is that as the stock code is structured, it isn't really able to handle a case were a vessel both has "in parts" crew and "in eva" kerbals attached.
I could rewrite and replace the whole method to handle this, but :

  1. I'm lazy
  2. In the current state of things, worst thing that will happen in those corner cases is the content of the EVAing kerbal inventories being recovered twice. The major issues were science data not being recovered and IPartCostModifier modules on kerbals not being accounted for, and the current patch fixes that in any case.

@gotmachine
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, implemented the real fix in next commit. Thanks for noticing the potential issue.

Please sign in to comment.