Skip to content

Commit a404b7d

Browse files
gotmachineJonnyOThan
authored andcommitted
Potential new KSP bugfix : ReRootPreserveSurfaceAttach (#142)
* New bugfix patch : ReRootPreserveSurfaceAttach * ReRootPreserveSurfaceAttach : prepare for release
1 parent aa98056 commit a404b7d

File tree

4 files changed

+82
-45
lines changed

4 files changed

+82
-45
lines changed

GameData/KSPCommunityFixes/Settings.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ KSP_COMMUNITY_FIXES
174174
175175
// Disable the stock behavior of altering surface attachment nodes on re-rooting, which is
176176
// unnecessary and doesn't work correctly, leading to permanently borked attachement nodes.
177-
ReRootPreserveSurfaceAttach = true
177+
// Replaces ReRootPreserveSurfaceAttach with some better (but more complex) logic.
178+
// If this patch causes an issue, try turning it off and turning on ReRootPreserveSurfaceAttach
179+
ReRootCloneSurfaceAttach = true
180+
ReRootPreserveSurfaceAttach = false
178181

179182
// Fix leaking a camera and spotlight created by the thumbnail system on certain failures
180183
ThumbnailSpotlight = true
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using HarmonyLib;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using UnityEngine;
8+
9+
namespace KSPCommunityFixes.BugFixes
10+
{
11+
class ReRootCloneSurfaceAttach : BasePatch
12+
{
13+
protected override void ApplyPatches(List<PatchInfo> patches)
14+
{
15+
patches.Add(new PatchInfo(
16+
PatchMethodType.Prefix,
17+
AccessTools.Method(typeof(AttachNode), nameof(AttachNode.ReverseSrfNodeDirection)),
18+
this));
19+
20+
patches.Add(new PatchInfo(
21+
PatchMethodType.Prefix,
22+
AccessTools.Method(typeof(AttachNode), nameof(AttachNode.ChangeSrfNodePosition)),
23+
this));
24+
}
25+
26+
// In stock, this function is called after reversing a surface attachment during a re-root operation.
27+
// it tries to alter a part's surface attachment so that it mirrors the surface attach node of its parent.
28+
// But that's not a great idea, because a lot of things depend on the surface attach node never changing.
29+
// For example, if the user then picks the part back up, it won't attach the same way to anything else
30+
// To fix this, instead of using the child's actual srfAttachNode we create a new surface attach node and
31+
// just stick it in the regular AttachNode list.
32+
static bool AttachNode_ReverseSrfNodeDirection_Prefix(AttachNode __instance, AttachNode fromNode)
33+
{
34+
// note that instead of cloning the child's srfAttachNode and using its properties, we use the fromNode
35+
// because we want to mirror the previous state as much as possible - this node WAS the other part's srfAttachNode
36+
AttachNode newSrfAttachNode = AttachNode.Clone(fromNode);
37+
newSrfAttachNode.owner = __instance.owner;
38+
newSrfAttachNode.attachedPart = fromNode.owner;
39+
// the name here isn't important, but if anyone is debugging an issue I'd like to make it clear where it came from.
40+
// I'm pretty sure the empty string has some special meaning, and we need to be sure it doesn't collide with any existing node IDs
41+
newSrfAttachNode.id = "KSPCF-reroot-srfAttachNode";
42+
43+
// convert the position, orientation from the other part's local space into ours
44+
Vector3 positionWorld = fromNode.owner.transform.TransformPoint(fromNode.position);
45+
Vector3 orientationWorld = fromNode.owner.transform.TransformDirection(fromNode.orientation);
46+
newSrfAttachNode.originalPosition = newSrfAttachNode.owner.transform.InverseTransformPoint(positionWorld);
47+
newSrfAttachNode.originalOrientation = -newSrfAttachNode.owner.transform.InverseTransformDirection(orientationWorld);
48+
newSrfAttachNode.position = newSrfAttachNode.originalPosition;
49+
newSrfAttachNode.orientation = newSrfAttachNode.originalOrientation;
50+
newSrfAttachNode.owner.attachNodes.Add(newSrfAttachNode);
51+
52+
// now clear the srfAttachNodes from both parts
53+
__instance.attachedPart = null;
54+
fromNode.attachedPart = null;
55+
56+
return false;
57+
}
58+
59+
// this function is just horribly broken and no one could call it, ever
60+
static bool AttachNode_ChangeSrfNodePosition_Prefix()
61+
{
62+
return false;
63+
}
64+
}
65+
}

KSPCommunityFixes/BugFixes/ReRootPreserveSurfaceAttach.cs

Lines changed: 12 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
using System.Collections.Generic;
77
using System.Reflection;
88
using System.Reflection.Emit;
9-
using UnityEngine.UIElements;
10-
using UnityEngine;
119

1210
#if REROOT_DEBUG_MODULE
1311
using UnityEngine;
@@ -20,53 +18,23 @@ class ReRootPreserveSurfaceAttach : BasePatch
2018
protected override void ApplyPatches(List<PatchInfo> patches)
2119
{
2220
patches.Add(new PatchInfo(
23-
PatchMethodType.Prefix,
24-
AccessTools.Method(typeof(AttachNode), nameof(AttachNode.ReverseSrfNodeDirection)),
25-
this));
26-
27-
patches.Add(new PatchInfo(
28-
PatchMethodType.Prefix,
29-
AccessTools.Method(typeof(AttachNode), nameof(AttachNode.ChangeSrfNodePosition)),
21+
PatchMethodType.Transpiler,
22+
AccessTools.Method(typeof(Part), nameof(Part.SetHierarchyRoot)),
3023
this));
3124
}
3225

33-
// In stock, this function is called after reversing a surface attachment during a re-root operation.
34-
// it tries to alter a part's surface attachment so that it mirrors the surface attach node of its parent.
35-
// But that's not a great idea, because a lot of things depend on the surface attach node never changing.
36-
// For example, if the user then picks the part back up, it won't attach the same way to anything else
37-
// To fix this, instead of using the child's actual srfAttachNode we create a new surface attach node and
38-
// just stick it in the regular AttachNode list.
39-
static bool AttachNode_ReverseSrfNodeDirection_Prefix(AttachNode __instance, AttachNode fromNode)
26+
// skip the portion of that method that alter surface nodes position/orientation on re-rooting,
27+
// by returning after the recursive SetHierarchyRoot() call.
28+
private static IEnumerable<CodeInstruction> Part_SetHierarchyRoot_Transpiler(IEnumerable<CodeInstruction> instructions)
4029
{
41-
// note that instead of cloning the child's srfAttachNode and using its properties, we use the fromNode
42-
// because we want to mirror the previous state as much as possible - this node WAS the other part's srfAttachNode
43-
AttachNode newSrfAttachNode = AttachNode.Clone(fromNode);
44-
newSrfAttachNode.owner = __instance.owner;
45-
newSrfAttachNode.attachedPart = fromNode.owner;
46-
// the name here isn't important, but if anyone is debugging an issue I'd like to make it clear where it came from.
47-
// I'm pretty sure the empty string has some special meaning, and we need to be sure it doesn't collide with any existing node IDs
48-
newSrfAttachNode.id = "KSPCF-reroot-srfAttachNode";
49-
50-
// convert the position, orientation from the other part's local space into ours
51-
Vector3 positionWorld = fromNode.owner.transform.TransformPoint(fromNode.position);
52-
Vector3 orientationWorld = fromNode.owner.transform.TransformDirection(fromNode.orientation);
53-
newSrfAttachNode.originalPosition = newSrfAttachNode.owner.transform.InverseTransformPoint(positionWorld);
54-
newSrfAttachNode.originalOrientation = -newSrfAttachNode.owner.transform.InverseTransformDirection(orientationWorld);
55-
newSrfAttachNode.position = newSrfAttachNode.originalPosition;
56-
newSrfAttachNode.orientation = newSrfAttachNode.originalOrientation;
57-
newSrfAttachNode.owner.attachNodes.Add(newSrfAttachNode);
58-
59-
// now clear the srfAttachNodes from both parts
60-
__instance.attachedPart = null;
61-
fromNode.attachedPart = null;
62-
63-
return false;
64-
}
30+
MethodInfo m_Part_SetHierarchyRoot = AccessTools.Method(typeof(Part), nameof(Part.SetHierarchyRoot));
6531

66-
// this function is just horribly broken and no one could call it, ever
67-
static bool AttachNode_ChangeSrfNodePosition_Prefix()
68-
{
69-
return false;
32+
foreach (CodeInstruction instruction in instructions)
33+
{
34+
yield return instruction;
35+
if (instruction.opcode == OpCodes.Callvirt && ReferenceEquals(instruction.operand, m_Part_SetHierarchyRoot))
36+
yield return new CodeInstruction(OpCodes.Ret);
37+
}
7038
}
7139
}
7240

KSPCommunityFixes/KSPCommunityFixes.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
<Compile Include="BugFixes\MapSOCorrectWrapping.cs" />
113113
<Compile Include="BugFixes\FixGetUnivseralTime.cs" />
114114
<Compile Include="BugFixes\ModuleAnimateGenericCrewModSpawnIVA.cs" />
115+
<Compile Include="BugFixes\ReRootCloneSurfaceAttach.cs" />
115116
<Compile Include="BugFixes\ReRootPreserveSurfaceAttach.cs" />
116117
<Compile Include="BugFixes\RespawnDeadKerbals.cs" />
117118
<Compile Include="BugFixes\ThumbnailSpotlight.cs" />

0 commit comments

Comments
 (0)