Skip to content

Commit d921cf5

Browse files
committed
patch Part::Highlight
1 parent f78d5fe commit d921cf5

File tree

2 files changed

+90
-9
lines changed

2 files changed

+90
-9
lines changed

Source/DynamicProperties/Patches/PartPatch.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System.Collections.Generic;
2+
using System.Reflection.Emit;
23
using HarmonyLib;
4+
using UnityEngine;
35

46
namespace Shabby;
57

@@ -37,6 +39,85 @@ private static bool SetOpacity_Prefix(Part __instance, float opacity)
3739
return false;
3840
}
3941

42+
private static void Highlight_SetRimColor(Part part, Color color)
43+
{
44+
highlightProperties[part].SetColor(PropertyIDs._RimColor, color);
45+
}
46+
47+
[HarmonyTranspiler]
48+
[HarmonyPatch(nameof(Part.Highlight), typeof(Color))]
49+
private static IEnumerable<CodeInstruction> Highlight_Transpiler(
50+
IEnumerable<CodeInstruction> insns)
51+
{
52+
var MPB_SetColor = AccessTools.Method(
53+
typeof(MaterialPropertyBlock),
54+
nameof(MaterialPropertyBlock.SetColor),
55+
[typeof(int)]);
56+
var Part_get_mpb = AccessTools.PropertyGetter(typeof(Part), nameof(Part.mpb));
57+
var Part_highlightRenderer =
58+
AccessTools.Field(typeof(Part), nameof(Part.highlightRenderer));
59+
var PropertyIDs__RimColor =
60+
AccessTools.Field(typeof(PropertyIDs), nameof(PropertyIDs._RimColor));
61+
var Renderer_SetPropertyBlock = AccessTools.Method(
62+
typeof(Renderer),
63+
nameof(Renderer.SetPropertyBlock),
64+
[typeof(MaterialPropertyBlock)]);
65+
66+
CodeMatch[] matchDupPop = [new(OpCodes.Dup), new(OpCodes.Pop)];
67+
68+
// mpb.SetColor(PropertyIDs._RimColor, value);
69+
// IL_0049: ldarg.0 // this
70+
// IL_004a: call instance class UnityEngine.MaterialPropertyBlock Part::get_mpb()
71+
// IL_004f: ldsfld int32 PropertyIDs::_RimColor
72+
// IL_0054: ldloc.0 // color
73+
// IL_0055: callvirt instance void UnityEngine.MaterialPropertyBlock::SetColor(int32, valuetype UnityEngine.Color)
74+
CodeMatch[] matchSetRimColor = [
75+
new(OpCodes.Ldarg_0),
76+
new(OpCodes.Call, Part_get_mpb),
77+
new(OpCodes.Ldsfld, PropertyIDs__RimColor),
78+
new(OpCodes.Ldloc_0),
79+
new(OpCodes.Callvirt, MPB_SetColor)
80+
];
81+
82+
// highlightRenderer[count].SetPropertyBlock(mpb);
83+
// IL_008a: ldarg.0 // this; jump target
84+
// IL_008b: ldfld class System.Collections.Generic.List`1<class UnityEngine.Renderer> Part::highlightRenderer
85+
// IL_0090: ldloc.1 // count
86+
// IL_0091: callvirt instance !0/*class UnityEngine.Renderer*/ class System.Collections.Generic.List`1<class UnityEngine.Renderer>::get_Item(int32)
87+
// IL_0096: ldarg.0 // this
88+
// IL_0097: call instance class UnityEngine.MaterialPropertyBlock Part::get_mpb()
89+
// IL_009c: callvirt instance void UnityEngine.Renderer::SetPropertyBlock(class UnityEngine.MaterialPropertyBlock)
90+
CodeMatch[] matchSetMpb = [
91+
new(OpCodes.Ldarg_0),
92+
new(OpCodes.Ldfld, Part_highlightRenderer),
93+
new(OpCodes.Ldloc_1),
94+
new(OpCodes.Callvirt), // can't easily specify indexer...
95+
new(OpCodes.Ldarg_0),
96+
new(OpCodes.Call, Part_get_mpb),
97+
new(OpCodes.Callvirt, Renderer_SetPropertyBlock)
98+
];
99+
100+
var matcher = new CodeMatcher(insns);
101+
matcher
102+
.MatchStartForward(matchDupPop)
103+
.Repeat(cm => cm.RemoveInstructions(matchDupPop.Length))
104+
.Start()
105+
.MatchStartForward(matchSetRimColor)
106+
.ThrowIfNotMatch("failed to find MPB set _RimColor call")
107+
.RemoveInstructions(matchSetRimColor.Length)
108+
.InsertAndAdvance(
109+
// PartPatch.Highlight_SetRimColor(this, value);
110+
new CodeInstruction(OpCodes.Ldarg_0), // `this`
111+
new CodeInstruction(OpCodes.Ldloc_0), // `value`
112+
CodeInstruction.Call(() => Highlight_SetRimColor(default, default)))
113+
.MatchStartForward(matchSetMpb)
114+
.ThrowIfNotMatch("failed to find Renderer.SetMPB call")
115+
// No need to replace application, since that is automatic.
116+
.SetAndAdvance(OpCodes.Nop, null) // preserve label
117+
.RemoveInstructions(matchSetMpb.Length - 1);
118+
return matcher.InstructionEnumeration();
119+
}
120+
40121
[HarmonyPostfix]
41122
[HarmonyPatch("OnDestroy")]
42123
private static void OnDestroy_Postfix(Part __instance)

Source/DynamicProperties/Props.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,27 +89,27 @@ public sealed class Props(int priority)
8989
{
9090
public readonly int Priority = priority;
9191

92-
private readonly Dictionary<int, Prop> _props = [];
92+
private readonly Dictionary<int, Prop> props = [];
9393

9494
internal bool Changed = false;
9595

9696
private static uint _idCounter = 0;
9797
private static uint _nextId() => _idCounter++;
98-
private readonly uint _uniqueId = _nextId();
98+
private readonly uint uniqueId = _nextId();
9999

100100
// Note that this is compatible with default object reference equality.
101101
public static readonly Comparer<Props> PriorityComparer = Comparer<Props>.Create((a, b) =>
102102
{
103103
var priorityCmp = a.Priority.CompareTo(b.Priority);
104-
return priorityCmp != 0 ? priorityCmp : a._uniqueId.CompareTo(b._uniqueId);
104+
return priorityCmp != 0 ? priorityCmp : a.uniqueId.CompareTo(b.uniqueId);
105105
});
106106

107-
internal IEnumerable<int> ManagedIds => _props.Keys;
107+
internal IEnumerable<int> ManagedIds => props.Keys;
108108

109109
[MethodImpl(MethodImplOptions.AggressiveInlining)]
110110
private void _internalSet<T, TProp>(int id, T value) where TProp : Prop<T>
111111
{
112-
if (_props.TryGetValue(id, out var prop)) {
112+
if (props.TryGetValue(id, out var prop)) {
113113
if (prop is TProp typedProp) {
114114
if (EqualityComparer<T>.Default.Equals(value, typedProp.Value)) return;
115115

@@ -122,7 +122,7 @@ private void _internalSet<T, TProp>(int id, T value) where TProp : Prop<T>
122122
$"property {PropIdToName.Get(id)} has mismatched type; overwriting with {typeof(T).Name}!");
123123
}
124124

125-
_props[id] = (TProp)Activator.CreateInstance(typeof(TProp), value);
125+
props[id] = (TProp)Activator.CreateInstance(typeof(TProp), value);
126126
Changed = true;
127127
}
128128

@@ -138,7 +138,7 @@ private void _internalSet<T, TProp>(int id, T value) where TProp : Prop<T>
138138
public void SetTexture(int id, Texture value) => _internalSet<Texture, PropTexture>(id, value);
139139
public void SetVector(int id, Vector4 value) => _internalSet<Vector4, PropVector>(id, value);
140140

141-
private bool _internalHas<T>(int id) => _props.TryGetValue(id, out var prop) && prop is Prop<T>;
141+
private bool _internalHas<T>(int id) => props.TryGetValue(id, out var prop) && prop is Prop<T>;
142142

143143
public bool HasColor(int id) => _internalHas<Color>(id);
144144
public bool HasFloat(int id) => _internalHas<float>(id);
@@ -149,7 +149,7 @@ private void _internalSet<T, TProp>(int id, T value) where TProp : Prop<T>
149149
[MethodImpl(MethodImplOptions.AggressiveInlining)]
150150
internal void Write(int id, MaterialPropertyBlock mpb)
151151
{
152-
if (!_props.TryGetValue(id, out var prop)) {
152+
if (!props.TryGetValue(id, out var prop)) {
153153
throw new KeyNotFoundException($"property {PropIdToName.Get(id)} not found");
154154
}
155155

@@ -163,7 +163,7 @@ public override string ToString()
163163
{
164164
var sb = StringBuilderCache.Acquire();
165165
sb.AppendFormat("(Priority {0}) {{\n", Priority);
166-
foreach (var (id, prop) in _props) {
166+
foreach (var (id, prop) in props) {
167167
sb.AppendFormat("{0} = {1}\n", PropIdToName.Get(id), prop);
168168
}
169169

0 commit comments

Comments
 (0)