public static void DysonSphereRelatedGameTick(FactorySystem __instance) { IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions); List <CodeInstruction> instructionsList = instructions.ToList(); List <CodeInstruction> code = new List <CodeInstruction>(); int startIdx = 0; int endIdx = matcher.MatchForward(false, new CodeMatch(OpCodes.Ldloc_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(GameHistoryData), nameof(GameHistoryData.miningCostRate))) ).Pos; if (endIdx == instructionsList.Count) { throw new InvalidOperationException("Cannot extract the dysonsphere part of FactorySystem.GameTick because the first indicator isn't present"); } for (int i = startIdx; i < endIdx; i++) { code.Add(instructionsList[i]); } startIdx = matcher.MatchForward(false, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorCursor))) ).Pos; if (startIdx == instructionsList.Count) { throw new InvalidOperationException("Cannot extract the dysonsphere part of FactorySystem.GameTick because the second indicator isn't present"); } endIdx = matcher.MatchForward(false, new CodeMatch(OpCodes.Ldloc_0), new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(GameHistoryData), nameof(GameHistoryData.currentTech))) ).Pos; if (endIdx == instructionsList.Count) { throw new InvalidOperationException("Cannot extract the dysonsphere part of FactorySystem.GameTick because the third indicator isn't present"); } Debug.Log($"extracted dysonsphere part of FactorySystem.GameTick from {startIdx} to {endIdx}"); for (int i = startIdx; i < endIdx; i++) { code.Add(instructionsList[i]); } return(code.AsEnumerable()); } // make compiler happy _ = Transpiler(null); return; }
public static IEnumerable <CodeInstruction> GameTick_Transpiler(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(true, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlayerControlGizmo), "player")), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_controller"), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_modelVisible"), new CodeMatch(OpCodes.Brfalse)); var jmpPos = matcher.Operand; matcher.Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <PlayerControlGizmo, bool> >((PlayerControlGizmo _this) => { return(Multiplayer.IsActive && _this.player.navigation.indicatorAstroId > 100000); })) .Insert(new CodeInstruction(OpCodes.Brtrue, jmpPos)); matcher.MatchForward(true, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlayerControlGizmo), "player")), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_navigation"), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_indicatorAstroId"), new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Ble)); jmpPos = matcher.Operand; matcher.Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <PlayerControlGizmo, bool> >((PlayerControlGizmo _this) => { return(Multiplayer.IsActive && _this.player.navigation.indicatorAstroId > 100000); })) .Insert(new CodeInstruction(OpCodes.Brtrue, jmpPos)); matcher.MatchForward(true, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PlayerControlGizmo), "player")), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_navigation"), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_indicatorAstroId"), new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Ble)); jmpPos = matcher.Operand; matcher.Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <PlayerControlGizmo, bool> >((PlayerControlGizmo _this) => { return(Multiplayer.IsActive && _this.player.navigation.indicatorAstroId > 100000); })) .Insert(new CodeInstruction(OpCodes.Brtrue, jmpPos)); return(matcher.InstructionEnumeration()); }
public static IEnumerable <CodeInstruction> CargoPathPatch(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(CargoPath), nameof(CargoPath.chunks))), new CodeMatch(OpCodes.Ldloc_S) ); matcher.MatchForward(false, new CodeMatch(OpCodes.Stloc_S)); object beginArg = matcher.Operand; matcher.Advance(1).MatchForward(false, new CodeMatch(OpCodes.Stloc_S)); object speedArg = matcher.Operand; matcher.MatchForward(false, new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Stloc_S)); matcher.Advance(2) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, beginArg)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, speedArg)) .InsertAndAdvance(Transpilers.EmitDelegate <RefAction <CargoPath, int, int> >((CargoPath path, ref int begin, ref int speed) => { // The change if (speed > 10) // If the speed is greater than 10, the length judgment process is performed to prevent crossing the boundary { for (int i = 10; i <= speed; i++) { if (begin + i + 10 >= path.bufferLength) // About to leave the end of the conveyor belt { speed = i; break; } if (path.buffer[begin + i] != 0) // Not empty within the speed range { speed = i; break; } } if (speed < 10) { speed = 10; // If the speed slows down to within a safe speed, set it to a safe speed } } })); return(matcher.InstructionEnumeration()); }
private static IEnumerable <CodeInstruction> TryPlayCustomTopicAdvTpl(IEnumerable <CodeInstruction> instructions) { var matcher = new CodeMatcher(instructions) .MatchForward(true, new CodeMatch(OpCodes.Ldstr, "話題を振る")) .MatchForward(true, new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(TalkScene), nameof(TalkScene.isNPC)))) .Advance(1) .ThrowIfNotMatch("Brtrue not found", new CodeMatch(OpCodes.Brtrue)); var startPos = matcher.Pos; // Figure out where the if else ends so we can jump there if we want to skip it // First go to the start of else, then step back and get the label at the end of if that skips over the else var elseLabel = ((Label)matcher.Operand); matcher.MatchForward(false, new CodeMatch(instruction => instruction.labels.Contains(elseLabel))) .Advance(-1) .ThrowIfNotMatch("Br not found", new CodeMatch(OpCodes.Br)); var skipIfelseLabel = matcher.Operand; // Go back to the start of the if matcher.Advance(startPos - matcher.Pos).ThrowIfNotMatch("Brtrue not found 2", new CodeMatch(OpCodes.Brtrue)); // Go back to the first if, then insert our if before it // Copy the `this` load instead of hardcoding it just in case (isNPC takes it) matcher.Advance(-2); var loadInstrCopy = new CodeInstruction(matcher.Opcode, matcher.Operand); matcher.Advance(1).Insert( new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(TopicHooks), nameof(TryPlayCustomTopicAdvHook))), new CodeInstruction(OpCodes.Brtrue, skipIfelseLabel), loadInstrCopy); return(matcher.Instructions()); }
public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il) { var matcher = new CodeMatcher(instructions, il); // Search for the first time we check if the player has opened a chest, this happens when adding the items in the opened inventory to the dictionary matcher.MatchForward(false, new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player))), new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.myPlayer))), new CodeMatch(OpCodes.Ldelem_Ref), new CodeMatch(OpCodes.Ldfld, typeof(Player).GetField(nameof(Player.chest))), new CodeMatch(OpCodes.Ldc_I4_M1), new CodeMatch(OpCodes.Beq)); matcher.Instruction.opcode = OpCodes.Nop; matcher.Instruction.operand = null; matcher.Advance(1); var getItemCode = new CodeInstruction[] { new CodeInstruction(OpCodes.Ldarg_0), new CodeInstruction(OpCodes.Ldloca_S, 1), new CodeInstruction(OpCodes.Ldloca_S, 2), new CodeInstruction(OpCodes.Call, typeof(StorageUI).GetMethod(nameof(StorageUI.DoWithdrawItemForCraft), new Type[] { typeof(Recipe), typeof(Item).MakeByRefType(), typeof(int).MakeByRefType() })), // The old instruction we destroyed new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player))) }; matcher.InsertAndAdvance(getItemCode); return(matcher.InstructionEnumeration()); }
public static IEnumerable <CodeInstruction> MultiplyUnlockText(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(TechProto), nameof(TechProto.UnlockFunctions)))) .MatchForward(false, new CodeMatch(instr => instr.IsStloc())); OpCode typeStlocOpcode = matcher.Opcode.ToLoad(); object typeStlocOperand = matcher.Operand; matcher.MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(TechProto), nameof(TechProto.UnlockValues)))) .MatchForward(false, new CodeMatch(OpCodes.Stloc_S)); object arg = matcher.Operand; matcher.Advance(1) .InsertAndAdvance(new CodeInstruction(typeStlocOpcode, typeStlocOperand)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, arg)) .InsertAndAdvance(Transpilers.EmitDelegate <RefAction <int, int> >((int type, ref int value) => { if (type == 18) { value *= GigaStationsPlugin.droneCapacityMultiplier; } else if (type == 19) { value *= GigaStationsPlugin.vesselCapacityMultiplier; } })); return(matcher.InstructionEnumeration()); }
public static IEnumerable <CodeInstruction> _OnUpdate_Transpiler(IEnumerable <CodeInstruction> instructions) { try { int pos1, pos2; CodeMatcher matcher = new CodeMatcher(instructions); matcher.MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonNode), "color"))); pos1 = matcher.Pos; matcher.MatchBack(false, new CodeMatch(OpCodes.Ldloc_S)); pos2 = matcher.Pos; matcher.Advance(pos1 - pos2 + 1) .Insert( new CodeInstruction(matcher.InstructionAt(pos2 - pos1 - 1)), new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(UIDysonBrush_Paint_Transpiler), "SendNodePacket")) ); matcher.MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonFrame), "color"))); pos1 = matcher.Pos; matcher.MatchBack(false, new CodeMatch(OpCodes.Ldloc_S)); pos2 = matcher.Pos; matcher.Advance(pos1 - pos2 + 1) .Insert( new CodeInstruction(matcher.InstructionAt(pos2 - pos1 - 1)), new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(UIDysonBrush_Paint_Transpiler), "SendFramePacket")) ); matcher.MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(DysonShell), "color"))); pos1 = matcher.Pos; matcher.MatchBack(false, new CodeMatch(OpCodes.Ldloc_S)); pos2 = matcher.Pos; matcher.Advance(pos1 - pos2 + 1) .Insert( new CodeInstruction(matcher.InstructionAt(pos2 - pos1 - 1)), new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(UIDysonBrush_Paint_Transpiler), "SendShellPacket")) ); return(matcher.InstructionEnumeration()); } catch { NebulaModel.Logger.Log.Error("UIDysonBrush_Paint._OnUpdate_Transpiler failed. Mod version not compatible with game version."); return(instructions); } }
static IEnumerable <CodeInstruction> RedirectSaves(IEnumerable <CodeInstruction> instructions) { var codeMatch = new CodeMatcher(instructions); codeMatch = codeMatch.MatchForward(false, new CodeMatch(OpCodes.Ldstr, "save")); codeMatch = codeMatch.Repeat(matcher => { matcher.SetOperandAndAdvance(folderRedirect + matcher.Operand); }); return(codeMatch.InstructionEnumeration()); }
public static void DestructObject(this PlanetFactory __instance, int objId, out int protoId) { // ReSharper disable once LocalFunctionCanBeMadeStatic #pragma warning disable 8321 IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction>?instructions, ILGenerator?generator) { if (instructions is null) { return(new List <CodeInstruction>()); } // IL length of the calls to PlayerAction_Build::NotifyDestruct(int32) and // instance void PlayerAction_Inspect::NotifyObjectDestruct(EObjectType, int32), minus NOPs const int callNotifyLength = 9; // IL length of the call to PlanetFactory::TakeBackItemsInEntity(Player, int32) const int callTakeItemsLength = 4; var codeMatcher = new CodeMatcher(instructions, generator); // Delete calls to the above methods, along with associated stack transforms. var output = codeMatcher.MatchForward(false, MatchLoadPlayer) // IL_002a: ldarg.1 .SetOpcodeAndAdvance(OpCodes.Nop) // transform: ldarg.1 -> nop .SetOpcodeAndAdvance(OpCodes.Nop) // transform: callvirt -> nop .RemoveInstructions(callNotifyLength) // remove: opcode1 ... opcode9 .With(matcher => Console.WriteLine((string)"Currently at instruction {0}", (object)matcher.Instruction)) .MatchForward(false, MatchCallTakeItems) // IL_0050: call instance void PlanetFactory::TakeBackItemsInEntity(class Player, int32) .MatchBack(false, MatchLoadThis) // IL_004d: ldarg.0 .RemoveInstructions(callTakeItemsLength) // remove: opcode1 ... opcode4 .MatchForward(false, MatchLoadPlayer) // IL_00a4: ldarg.1 .SetOpcodeAndAdvance(OpCodes.Nop) // transform: ldarg.1 -> nop .SetOpcodeAndAdvance(OpCodes.Nop) // transform: callvirt -> nop .RemoveInstructions(callNotifyLength) // remove: opcode1 ... opcode9 .InstructionEnumeration() .ToList(); output.ForEach(instruction => { if (instruction.IsLdarg(2)) { instruction.opcode = OpCodes.Ldarg_1; // transform: ldarg.2 -> ldarg.1 } else if (instruction.IsLdarg(3)) { instruction.opcode = OpCodes.Ldarg_2; // transform: ldarg.3 -> ldarg.2 } }); return(output); } protoId = 0; #pragma warning restore 8321 }
public static IEnumerable <CodeInstruction> ChaControl_SetShapeBodyValue_RemoveHeightLock(IEnumerable <CodeInstruction> instructions) { var cm = new CodeMatcher(instructions); // find branch that overrides the male height cm.MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.PropertyGetter(typeof(ChaInfo), nameof(ChaInfo.sex)))) .MatchForward(false, new CodeMatch(c => c.Branches(out _))); // turn it into an unconditional jump cm.InsertAndAdvance(new CodeInstruction(OpCodes.Pop)) .SetOpcodeAndAdvance(OpCodes.Br); return(cm.Instructions()); }
public static IEnumerable <CodeInstruction> ChaControl_Initialize_RemoveHeightLock(IEnumerable <CodeInstruction> instructions) { var cm = new CodeMatcher(instructions); // find branch that overrides the male height cm.MatchForward(false, new CodeMatch(OpCodes.Ldc_R4, 0.6f)) .MatchBack(false, new CodeMatch(OpCodes.Call)) .MatchBack(false, new CodeMatch(c => c.Branches(out _))); // turn it into an unconditional jump cm.InsertAndAdvance(new CodeInstruction(OpCodes.Pop)) .SetOpcodeAndAdvance(OpCodes.Br); return(cm.Instructions()); }
public static IEnumerable <CodeInstruction> AddColors(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Ldloc_1), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BeltComponent), nameof(BeltComponent.speed))), new CodeMatch(OpCodes.Ldc_I4_1) ); CodeMatcher matcher2 = matcher.Clone(); matcher2.MatchForward(true, new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(OpCodes.Stloc_S)); object arg = matcher2.Operand; matcher2.Advance(1); object label = matcher2.Operand; matcher.Advance(2) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, arg)) .SetInstruction(Transpilers.EmitDelegate <Func <int, int, int> >((speed, other) => { if (speed <= 1) { return(other); } if (speed <= 2) { other += 4; } else if (speed <= 5) { other += 8; } else if (speed <= 12) { other += 12; } return(other); })) .Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Stloc_S, arg)) .SetInstruction(new CodeInstruction(OpCodes.Br, label)); return(matcher.InstructionEnumeration()); }
private static IEnumerable <CodeInstruction> FixUpdateCalcGarbage(IEnumerable <CodeInstruction> instructions, ILGenerator generator) { var c = new CodeMatcher(instructions, generator); // Remove unnecessary new object creation that gets immediately overwritten return(c.MatchForward(false, new CodeMatch(OpCodes.Newobj), new CodeMatch(OpCodes.Stloc_1), new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld)) .RemoveInstruction() .RemoveInstruction() .Instructions()); }
public static IEnumerable <CodeInstruction> Replace10With1Transpiler(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.deliveryDrones)))) .MatchBack(false, new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instr.operand, 10f))) .SetOperandAndAdvance(1f); matcher .MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.deliveryShips)))) .MatchBack(false, new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instr.operand, 10f))) .SetOperandAndAdvance(1f); return(matcher.InstructionEnumeration()); }
public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il) { var matcher = new CodeMatcher(instructions, il); // Search for the first time we check if the player has opened a chest, this happens when adding the items in the opened inventory to the dictionary matcher.MatchForward(false, new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player))), new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.myPlayer))), new CodeMatch(OpCodes.Ldelem_Ref), new CodeMatch(OpCodes.Ldfld, typeof(Player).GetField(nameof(Player.chest))), new CodeMatch(OpCodes.Ldc_I4_M1), new CodeMatch(OpCodes.Beq)); var findRecipesCode = new CodeInstruction[] { new CodeInstruction(OpCodes.Ldloca_S, 6), new CodeInstruction(OpCodes.Call, typeof(MagicStoragePlus).GetMethod(nameof(MagicStoragePlus.FindRecipesPatch), new Type[] { typeof(Dictionary <int, int>).MakeByRefType() })) }; matcher.InsertAndAdvance(findRecipesCode); return(matcher.InstructionEnumeration()); }
private static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions) { CodeMatcher codeMatcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Br)); object label = codeMatcher.Operand; return(codeMatcher .MatchForward(false, new CodeMatch(OpCodes.Call, _currentGetter)) .Advance(2) .Insert( new CodeInstruction(OpCodes.Ldloc_1), new CodeInstruction(OpCodes.Call, FakeNoteHelper._boundsNullCheck), new CodeInstruction(OpCodes.Brtrue_S, label)) .InstructionEnumeration()); }
private static IEnumerable <CodeInstruction> InternalUpdate_Transpiler(IEnumerable <CodeInstruction> instructions) { // Store projectile data after swarm.AddBullet(sailBullet, orbitId) if IsUpdateNeeded == true try { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(SailBullet), nameof(SailBullet.lBegin))) //IL#638 ); CodeInstruction loadInstruction = matcher.InstructionAt(-1); matcher.MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(DysonSwarm), nameof(DysonSwarm.AddBullet))) //IL#679 ) .Advance(2) .InsertAndAdvance( new CodeInstruction(OpCodes.Ldarg_0), new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.planetId))), loadInstruction, new CodeInstruction(OpCodes.Ldarg_0), new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(EjectorComponent), nameof(EjectorComponent.orbitId))), HarmonyLib.Transpilers.EmitDelegate <Action <int, Vector3, int> >((planetId, localPos, orbitId) => { // If the dyson sphere has no subscribers anymore, skip this data if (!Multiplayer.IsActive || !Multiplayer.Session.Launch.Snapshots.ContainsKey(planetId / 100 - 1)) { return; } // Assume orbitId < 65536 DysonLaunchData.Projectile data = new DysonLaunchData.Projectile { PlanetId = planetId, TargetId = (ushort)orbitId, LocalPos = localPos }; Multiplayer.Session.Launch.ProjectileBag.Add(data); }) ); return(matcher.InstructionEnumeration()); } catch { NebulaModel.Logger.Log.Error("EjectorComponent.InternalUpdate_Transpiler failed. Mod version not compatible with game version."); return(instructions); } }
public static IEnumerable <CodeInstruction> RemoveDivisionBy10(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.deliveryDrones)))) .MatchForward(false, new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instr.operand, 0.1f))) .SetAndAdvance(OpCodes.Nop, null) .Advance(2) .SetAndAdvance(OpCodes.Nop, null); matcher .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), nameof(StationComponent.deliveryShips)))) .MatchForward(false, new CodeMatch(instr => instr.opcode == OpCodes.Ldc_R4 && Mathf.Approximately((float)instr.operand, 0.1f))) .SetAndAdvance(OpCodes.Nop, null) .Advance(2) .SetAndAdvance(OpCodes.Nop, null); return(matcher.InstructionEnumeration()); }
private static IEnumerable <CodeInstruction> PhPluginSceneInfoSaveTranspiler(IEnumerable <CodeInstruction> instructions) { var matcher = new CodeMatcher(instructions); matcher.MatchForward(false, new CodeMatch(OpCodes.Ldstr, "【PHStudio】")); var getWriterInstr = matcher.InstructionAt(-1); matcher.Advance(2); matcher.InsertAndAdvance( new CodeInstruction(OpCodes.Ldarg_0), getWriterInstr, CodeInstruction.Call(typeof(Hooks), nameof(SceneInfoSaveHook))); if (matcher.Instruction.opcode != OpCodes.Leave && matcher.Instruction.opcode != OpCodes.Leave_S) { throw new Exception("Failed to patch SceneInfo.Save"); } return(matcher.Instructions()); }
static IEnumerable <CodeInstruction> FactorySystem_DrawModels_Patch(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions); matcher .MatchForward(false, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(FactorySystem), nameof(FactorySystem.ejectorCursor))) ); // replace the code logic with the same logic wrapped in a meaningful lock for thread safety and return matcher .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(Transpilers.EmitDelegate <Action <FactorySystem> >(factorySystem => { if (factorySystem.factory.dysonSphere != null) { lock (factorySystem.factory.dysonSphere) { DysonSphereRelatedGameTick(factorySystem); } } else { DysonSphereRelatedGameTick(factorySystem); } })) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(Transpilers.EmitDelegate <Action <FactorySystem> >(factorySystem => { lock (GameMain.history) { TechRelatedGameTick(factorySystem); } })) .InsertAndAdvance(new CodeInstruction(OpCodes.Ret)); return(matcher.InstructionEnumeration()); }
public static IEnumerable <CodeInstruction> GizmoColor(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Ldloca_S), new CodeMatch(OpCodes.Initobj, typeof(ConnGizmoObj))); object arg = matcher.Operand; matcher.MatchForward(false, new CodeMatch(OpCodes.Ldarg_0) ).Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloca_S, arg)) .InsertAndAdvance(Transpilers.EmitDelegate <RefAction <ConnGizmoObj> >((ref ConnGizmoObj obj) => { if (obj.color == 12) { obj.color = 6; } })); return(matcher.InstructionEnumeration()); }
public static IEnumerable <CodeInstruction> PasteSnap(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(true, new CodeMatch(x => x.IsLdloc()), new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_Click.castGroundPos))) ) .Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(Transpilers.EmitDelegate <Action <BuildTool_BlueprintPaste> >(tool => { float x = tool.blueprint.dragBoxSize_x; float y = tool.blueprint.dragBoxSize_y; currentGridData.snapGrid = new Vector2(x, y); float longitude = 0; float latitude = 0; BlueprintUtils.GetLongitudeLatitudeRad(tool.castGroundPos.normalized, ref longitude, ref latitude); if (isLockedLongitude) { longitude = lockLongitude; } if (isLockedLatitude) { latitude = lockLatitude; } tool.castGroundPos = BlueprintUtils.GetDir(longitude, latitude) * tool.castGroundPos.magnitude; })); matcher.MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(PlanetAuxData), nameof(PlanetAuxData.Snap)))) .SetInstruction(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(GridSnappingPatches), nameof(SnapModified)))); return(matcher.InstructionEnumeration()); }
internal static IEnumerable <CodeInstruction> FindSaveMethod(IEnumerable <CodeInstruction> instructions) { var btnSaveField = AccessTools.Field(typeof(CustomControl), "btnSave") ?? throw new MissingFieldException("could not find btnSave"); var patchMethod = AccessTools.Method(typeof(MakerCardSave), nameof(CardSavePatch)) ?? throw new MissingMethodException("could not find CardSavePatch"); #if KKS var cm = new CodeMatcher(instructions); var asyncGeneratedField = (FieldInfo)cm.MatchForward(false, new CodeMatch(OpCodes.Stfld)).Instruction.operand; var movenext = asyncGeneratedField.DeclaringType.GetMethod("MoveNext", AccessTools.all); if (movenext == null) { throw new ArgumentNullException(nameof(movenext)); } var ctx = new ILContext(new DynamicMethodDefinition(movenext).Definition); var il = new ILCursor(ctx); il.GotoNext(instruction => instruction.MatchLdfld(btnSaveField)); MethodReference targetMethodReference = null; il.GotoNext(instruction => instruction.MatchLdftn(out targetMethodReference)); if (targetMethodReference == null) { throw new ArgumentNullException(nameof(targetMethodReference)); } var targetMethod = targetMethodReference.ResolveReflection(); KoikatuAPI.Logger.LogDebug("Save method found for patching MakerCardSave - " + targetMethod.Name); _harmony.Patch(targetMethod, new HarmonyMethod(patchMethod)); return(instructions); #else var codes = instructions.ToList(); bool buttonFound = false, success = false; for (int i = 0; i < codes.Count; i++) { var code = codes[i]; if (!buttonFound && code.opcode == OpCodes.Ldfld && code.operand as FieldInfo == btnSaveField) { buttonFound = true; } if (buttonFound && code.opcode == OpCodes.Ldftn) { if (code.operand is MethodInfo methodInfo) { _harmony.Patch(methodInfo, new HarmonyMethod(patchMethod)); KoikatuAPI.Logger.LogDebug("Save method found for patching MakerCardSave - " + methodInfo.Name); success = true; } break; } } if (!success) { KoikatuAPI.Logger.LogWarning("Could not find save method for patching MakerCardSave"); } return(codes); #endif }
static IEnumerable <CodeInstruction> AddMoreData(IEnumerable <CodeInstruction> instructions, ILGenerator generator) { CodeMatcher matcher = new CodeMatcher(instructions, generator) .MatchForward(true, new CodeMatch(OpCodes.Ldarg_S), new CodeMatch(OpCodes.Ldc_I4_0) ).Advance(3); Label contLabel = (Label)matcher.Operand; //allow generation when there is no buildings matcher.Advance(-3) .SetAndAdvance(OpCodes.Pop, null) .InsertAndAdvance(Transpilers.EmitDelegate <Func <bool> >(() => BlueprintCopyExtension.isEnabled && BlueprintCopyExtension.reformSelection.Count > 0)) .InsertAndAdvance(new CodeInstruction(OpCodes.Brtrue, contLabel)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_S, 4)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_0)); //Replace function call to include foundations matcher.MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.GetBoundingRange)))) .SetInstruction(Transpilers.EmitDelegate <Func <PlanetData, PlanetAuxData, int[], int, float, BPGratBox> >((data, auxData, arg3, i, f) => { BlueprintCopyExtension.CopyReforms(); return(ReformBPUtils.GetBoundingRange(data, auxData, arg3, i, BlueprintCopyExtension.tmpReformList, f)); })); //Add initialization matcher.MatchForward(false, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(OpCodes.Newarr), new CodeMatch(OpCodes.Stfld) ).Advance(1) .InsertAndAdvance(Transpilers.EmitDelegate <Action <BlueprintData> >(data => { if (BlueprintCopyExtension.isEnabled && !UndoManager.IgnoreAllEvents.Value) { data.reforms = new ReformData[BlueprintCopyExtension.reformSelection.Count]; int i = 0; foreach (var kv in BlueprintCopyExtension.reformSelection) { data.reforms[i] = kv.Value; data.reforms[i].areaIndex = -1; i++; } } else { data.reforms = new ReformData[0]; } })) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)); //Just to anchor matcher.MatchForward(true, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.GetLongitudeSegmentCount), new[] { typeof(Vector3), typeof(int) })), new CodeMatch(OpCodes.Stloc_S), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.GetLongitudeRadPerGrid), new[] { typeof(int), typeof(int) }))); // add my code matcher.MatchForward(false, new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Stloc_S)) .Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 18)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 17)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 7)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 20)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 11)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 19)) .InsertAndAdvance(Transpilers.EmitDelegate <AddAction>( (blueprint, bpgratBox, i, array, longitudeRadPerGrid, latitudeRadPerGrid, longitudeSegmentCount) => { if (!BlueprintCopyExtension.isEnabled) { return; } if (UndoManager.IgnoreAllEvents.Value) { return; } for (int j = 0; j < BlueprintCopyExtension.reformSelection.Count; j++) { if (blueprint.reforms[j].areaIndex >= 0) { continue; } ReformData data = blueprint.reforms[j]; if (!(bpgratBox.y - 1E-05f <= data.latitude) || !(data.latitude <= bpgratBox.w + 1E-05f)) { continue; } blueprint.reforms[j].areaIndex = i; blueprint.reforms[j].localLongitude = (data.longitude - array[i].x) / longitudeRadPerGrid; blueprint.reforms[j].localLatitude = (data.latitude - array[i].y) / latitudeRadPerGrid; if (blueprint.reforms[j].localLongitude < -0.5001f) { blueprint.reforms[j].localLongitude += longitudeSegmentCount * 5; } } })); //Add null check to buildings iteration matcher.MatchForward(true, new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Newarr), new CodeMatch(OpCodes.Stfld)) .Advance(8) .CreateLabel(out Label exitLabel) .Advance(-2) .InsertAndAdvance(new CodeInstruction(OpCodes.Pop)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_2)) .InsertAndAdvance(new CodeInstruction(OpCodes.Brfalse, exitLabel)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 21)); return(matcher.InstructionEnumeration()); }
private static IEnumerable <CodeInstruction> OnBeltBuilt_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator iLGenerator) { /* * Calls * Multiplayer.Session.Factories.OnNewSetInserterPickTarget(objId, pickTarget, inserterId, offset, pointPos); * After * this.factorySystem.SetInserterPickTarget(inserterId, num6, num5 - num7); */ CodeMatcher codeMatcher = new CodeMatcher(instructions, iLGenerator) .MatchForward(true, new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == nameof(FactorySystem.SetInserterPickTarget) ) ); if (codeMatcher.IsInvalid) { NebulaModel.Logger.Log.Error("PlanetFactory_Transpiler.OnBeltBuilt 1 failed. Mod version not compatible with game version."); return(instructions); } List <CodeInstruction> setInserterTargetInsts = codeMatcher.InstructionsWithOffsets(-5, -1); // inserterId, pickTarget, offset CodeInstruction objIdInst = codeMatcher.InstructionAt(-13); // objId List <CodeInstruction> pointPosInsts = codeMatcher.InstructionsWithOffsets(8, 10); // pointPos codeMatcher = codeMatcher .Advance(1) .InsertAndAdvance(setInserterTargetInsts.ToArray()) .InsertAndAdvance(objIdInst) .InsertAndAdvance(pointPosInsts.ToArray()) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Action <int, int, int, int, UnityEngine.Vector3> >((inserterId, pickTarget, offset, objId, pointPos) => { if (Multiplayer.IsActive) { Multiplayer.Session.Factories.OnNewSetInserterPickTarget(objId, pickTarget, inserterId, offset, pointPos); } })); /* * Calls * Multiplayer.Session.Factories.OnNewSetInserterInsertTarget(objId, pickTarget, inserterId, offset, pointPos); * After * this.factorySystem.SetInserterInsertTarget(inserterId, num9, num5 - num10); */ codeMatcher = codeMatcher .MatchForward(true, new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == nameof(FactorySystem.SetInserterInsertTarget) ) ); if (codeMatcher.IsInvalid) { NebulaModel.Logger.Log.Error("PlanetFactory_Transpiler.OnBeltBuilt 2 failed. Mod version not compatible with game version."); return(codeMatcher.InstructionEnumeration()); } setInserterTargetInsts = codeMatcher.InstructionsWithOffsets(-5, -1); // inserterId, pickTarget, offset objIdInst = codeMatcher.InstructionAt(-13); // objId pointPosInsts = codeMatcher.InstructionsWithOffsets(9, 11); // pointPos codeMatcher = codeMatcher .Advance(1) .InsertAndAdvance(setInserterTargetInsts.ToArray()) .InsertAndAdvance(objIdInst) .InsertAndAdvance(pointPosInsts.ToArray()) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Action <int, int, int, int, UnityEngine.Vector3> >((inserterId, pickTarget, offset, objId, pointPos) => { if (Multiplayer.IsActive) { Multiplayer.Session.Factories.OnNewSetInserterInsertTarget(objId, pickTarget, inserterId, offset, pointPos); } })); return(codeMatcher.InstructionEnumeration()); }
public static void CheckBuildConditionsWorker(PlayerAction_Build __instance, BuildPreview bp) { IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions) { // modify the original CheckBuildConditions to only operate on a single item (so only the body of the for loop) injecting the BuildPreview passed as argument CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Stloc_2) ) .SetOpcodeAndAdvance(OpCodes.Nop) .SetOpcodeAndAdvance(OpCodes.Nop) .SetOpcodeAndAdvance(OpCodes.Nop) .SetOpcodeAndAdvance(OpCodes.Nop) .SetOpcodeAndAdvance(OpCodes.Nop) .SetOpcodeAndAdvance(OpCodes.Nop) .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldarg_1)) .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Stloc_3)) .MatchForward(false, new CodeMatch(OpCodes.Ldloc_3), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.desc))), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.isBelt))) ) .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldloc_3)) .SetInstructionAndAdvance(Transpilers.EmitDelegate <Func <PlayerAction_Build, BuildPreview, bool> >((actionBuild, buildPreview) => { // remove checks for belts by stating that the current buildPreview is not a belt. if (BlueprintManager.pastedEntities.Count > 0 && buildPreview.desc.isBelt) { // but we have to take care of collision checks Vector3 testPos = buildPreview.lpos + buildPreview.lpos.normalized * 0.3f; if (!buildPreview.ignoreCollider) { actionBuild.GetOverlappedObjectsNonAlloc(testPos, 0.34f, 3f, false); if (actionBuild._overlappedCount > 0) { buildPreview.condition = EBuildCondition.Collide; } actionBuild.GetOverlappedVeinsNonAlloc(testPos, 0.6f, 3f); if (actionBuild._overlappedCount > 0) { buildPreview.condition = EBuildCondition.Collide; } } return(false); } return(buildPreview.desc.isBelt); })) .MatchForward(false, new CodeMatch(OpCodes.Ldloc_3), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.condition))) ) .Advance(1) .SetInstructionAndAdvance(Transpilers.EmitDelegate <Func <BuildPreview, bool> >(buildPreview => { // ignore checkbuildconditions for all inserters when copy pasting as we already took care of checking for collisions if (BlueprintManager.pastedEntities.Count > 0 && buildPreview.desc.isInserter) { var actionBuild = GameMain.data.mainPlayer.controller.actionBuild; // only check that we have enough items if (buildPreview.coverObjId == 0 || buildPreview.willCover) { int id = buildPreview.item.ID; int num = 1; if (actionBuild.tmpInhandId == id && actionBuild.tmpInhandCount > 0) { num = 1; actionBuild.tmpInhandCount--; } else { actionBuild.tmpPackage.TakeTailItems(ref id, ref num, false); } if (num == 0) { buildPreview.condition = EBuildCondition.NotEnoughItem; } } if (buildPreview.condition != EBuildCondition.Ok || !IsInserterConnected(buildPreview)) { return(true); } // must calculate correct refCount/refArr to ensure inserter moves has the right speed Vector3 posR = Vector3.zero; bool inputIsBelt; Vector3 inputPos; bool outputIsBelt; Vector3 outputPos; if (buildPreview.input == null) { inputPos = actionBuild.GetObjectPose(buildPreview.inputObjId).position; inputIsBelt = actionBuild.ObjectIsBelt(buildPreview.inputObjId); } else { inputPos = buildPreview.input.lpos; inputIsBelt = buildPreview.input.desc.isBelt; } if (buildPreview.output == null) { outputPos = actionBuild.GetObjectPose(buildPreview.outputObjId).position; outputIsBelt = actionBuild.ObjectIsBelt(buildPreview.outputObjId); } else { outputPos = buildPreview.output.lpos; outputIsBelt = buildPreview.output.desc.isBelt; } if (inputIsBelt && !outputIsBelt) { posR = outputPos; } else if (!inputIsBelt && outputIsBelt) { posR = inputPos; } else { posR = (inputPos + outputPos) * 0.5f; } float segmentsCount = actionBuild.planetAux.mainGrid.CalcSegmentsAcross(posR, buildPreview.lpos, buildPreview.lpos2); if (!inputIsBelt && !outputIsBelt) { segmentsCount -= 0.3f; } buildPreview.refCount = Mathf.RoundToInt(Mathf.Clamp(segmentsCount, 1f, 3f)); buildPreview.refArr = new int[buildPreview.refCount]; return(true); } return(buildPreview.condition != EBuildCondition.Ok); })); // trim the code just before the for loop condition checks int endIdx = matcher .MatchForward(false, new CodeMatch(OpCodes.Ldloc_3), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.condition))) ) .SetOpcodeAndAdvance(OpCodes.Nop).Pos; List <CodeInstruction> instructionsList = matcher.InstructionEnumeration().ToList(); List <CodeInstruction> code = new List <CodeInstruction>(); for (int i = 0; i < endIdx; i++) { code.Add(instructionsList[i]); } return(code.AsEnumerable()); } // make compiler happy _ = Transpiler(null); return; }
static IEnumerable <CodeInstruction> RefreshPreviews(IEnumerable <CodeInstruction> instructions, ILGenerator generator) { float MirrorBuildingRotation(float yaw, BlueprintBuilding building) { if (buildingsAxis.ContainsKey(building.modelIndex) && buildingsAxis[building.modelIndex] == MajorAxis.XAXIS) { return(MirrorRotation(yaw + 90f) - 90f); } return(MirrorRotation(yaw)); } CodeMatcher matcher = new CodeMatcher(instructions, generator) .MatchForward(true, new CodeMatch(OpCodes.Ldelem, typeof(Vector4)), new CodeMatch(OpCodes.Stloc_S), new CodeMatch(OpCodes.Ldloca_S)); matcher.Advance(1); object longAxisVar = matcher.Operand; while (matcher.Opcode != OpCodes.Stfld) { matcher.RemoveInstruction(); } matcher.RemoveInstructions(2); object latAxisVar = matcher.Operand; while (matcher.Opcode != OpCodes.Stfld) { matcher.RemoveInstruction(); } matcher.RemoveInstruction() .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, longAxisVar)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, latAxisVar)) .InsertAndAdvance(Transpilers.EmitDelegate <RefAction>(MirrorArea)); matcher.MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.TransitionWidthAndHeight)))) .Advance(-3) .RemoveInstruction().RemoveInstruction().RemoveInstruction() .SetInstruction(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, Vector2> >((yaw, building) => { float x = building.localOffset_x; float y = building.localOffset_y; if ((mirrorLat && !mirrorLong || !mirrorLat && mirrorLong) && buildingsOffsets.ContainsKey(building.modelIndex)) { Vector2 offset = buildingsOffsets[building.modelIndex]; float rotatedYaw = MirrorBuildingRotation(building.yaw, building); offset = offset.Rotate(rotatedYaw + (mirrorLong ? 180 : 0)); x += offset.x; y += offset.y; } return(BlueprintUtils.TransitionWidthAndHeight(yaw, x, y)); })); matcher.MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BlueprintBuilding), nameof(BlueprintBuilding.yaw))) ).Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 30)) .InsertAndAdvance(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, float> >(MirrorBuildingRotation)).Advance(2); matcher.MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BlueprintBuilding), nameof(BlueprintBuilding.yaw2))) ).Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 30)) .InsertAndAdvance(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, float> >(MirrorBuildingRotation)); matcher.MatchForward(true, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.desc))), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.isInserter)))) .Advance(2); object previewVar2 = matcher.Operand; matcher.Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, previewVar2)) .InsertAndAdvance(Transpilers.EmitDelegate <Action <BuildPreview> >(preview => { if (preview.input != null && preview.input.desc.slotPoses.Length > preview.inputFromSlot && !preview.input.desc.isBelt) { Quaternion invRot = Quaternion.Inverse(preview.input.lrot); Vector3 slotPosition = preview.lpos - preview.input.lpos; slotPosition = invRot * slotPosition; Quaternion slotRotation = invRot * preview.lrot; for (int i = 0; i < preview.input.desc.slotPoses.Length; i++) { Pose slotPose = preview.input.desc.slotPoses[i]; if (!((slotPose.position - slotPosition).sqrMagnitude < 0.1f)) { continue; } if (!slotPose.rotation.Approximately(slotRotation)) { continue; } if (preview.inputFromSlot == i) { break; } preview.inputFromSlot = i; break; } } if (preview.output != null && preview.output.desc.slotPoses.Length > preview.outputToSlot && !preview.output.desc.isBelt) { Quaternion invRot = Quaternion.Inverse(preview.output.lrot); Vector3 slotPosition = preview.lpos2 - preview.output.lpos; slotPosition = invRot * slotPosition; Quaternion slotRotation = invRot * (preview.lrot2 * Quaternion.Euler(0f, -180f, 0f)); for (int i = 0; i < preview.output.desc.slotPoses.Length; i++) { Pose slotPose = preview.output.desc.slotPoses[i]; if (!((slotPose.position - slotPosition).sqrMagnitude < 0.1f)) { continue; } if (!slotPose.rotation.Approximately(slotRotation)) { continue; } if (preview.outputToSlot == i) { break; } preview.outputToSlot = i; break; } } })); return(matcher.InstructionEnumeration()); }
public static IEnumerable <CodeInstruction> _OnLateUpdate_Transpiler(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(true, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap), "starPool")), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_Item"), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap.StarNode), "nameText")), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_gameObject"), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "SetActive"), new CodeMatch(OpCodes.Ldloc_S)); if (matcher.IsInvalid) { Log.Warn("UIVirtualStarmap transpiler could not find injection point, not patching!"); return(instructions); } matcher.Advance(1) .SetAndAdvance(OpCodes.Ldloc_2, null) // change 'if (flag2 && flag)' to 'if (flag2 && pressing)' .Advance(2); // now remove original logic in this if(){} for (int i = 0; i < 39; i++) { matcher.SetAndAdvance(OpCodes.Nop, null); } // add own logic matcher.InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0), new CodeInstruction(OpCodes.Ldloc_S, 12), HarmonyLib.Transpilers.EmitDelegate <ShowSolarsystemDetails>((UIVirtualStarmap starmap, int starIndex) => { if (pressSpamProtector) { return; } pressSpamProtector = true; if (Multiplayer.Session != null && Multiplayer.Session.IsInLobby && starmap.clickText == "") { ClearStarmap(starmap); ShowSolarSystem(starmap, starIndex); } else if (Multiplayer.Session != null && Multiplayer.Session.IsInLobby && starmap.clickText != "") { string[] split = starmap.clickText.Split(' '); int starId = 0; starId = Convert.ToInt32(split[0]); StarData starData = starmap._galaxyData.StarById(starId); // no increment as we stored the actual id in there if (starData == null || starIndex == 0) // starIndex == 0 is the star in the middle, so we need to decrement by 1 below { return; } PlanetData pData = starData.planets[starIndex - 1]; if (pData == null) { return; } if (UIRoot.instance.uiGame.planetDetail.planet != null && UIRoot.instance.uiGame.planetDetail.planet.id == pData.id && pData.type != EPlanetType.Gas) { // clicked on planet and details already visible, so set as new birth planet starmap._galaxyData.birthStarId = starId; starmap._galaxyData.birthPlanetId = pData.id; GameMain.data.galaxy.birthStarId = starId; GameMain.data.galaxy.birthPlanetId = pData.id; customBirthStar = starData.id; customBirthPlanet = pData.id; Log.Info($"set birth planet{pData.id} {pData.displayName}"); Text text = GameObject.Find("UI Root/Overlay Canvas/Galaxy Select/start-button/start-text").GetComponent <Text>(); text.text = $"Start Game at {pData.displayName}"; text.horizontalOverflow = HorizontalWrapMode.Overflow; if (pData.data == null) { Button button = GameObject.Find("UI Root/Overlay Canvas/Galaxy Select/start-button").GetComponent <Button>(); button.interactable = false; EPlanetType planetType = pData.type; pData.type = EPlanetType.Gas; PlanetModelingManager.genPlanetReqList.Enqueue(pData); pData.onLoaded += (PlanetData planet) => { pData.type = planetType; button.interactable = true; }; } } starmap.clickText = split[0] + " " + starIndex.ToString(); UIRoot.instance.uiGame.SetPlanetDetail(pData); GameObject.Find("UI Root/Overlay Canvas/Galaxy Select/right-group")?.SetActive(false); UIRoot.instance.uiGame.planetDetail.gameObject.SetActive(true); UIRoot.instance.uiGame.planetDetail.gameObject.GetComponent <RectTransform>().parent.gameObject.SetActive(true); UIRoot.instance.uiGame.planetDetail.gameObject.GetComponent <RectTransform>().parent.gameObject.GetComponent <RectTransform>().parent.gameObject.SetActive(true); UIRoot.instance.uiGame.planetDetail._OnUpdate(); } })); // change for loop to start at 0 instead of 1 matcher.Start(); matcher.MatchForward(true, new CodeMatch(OpCodes.Stloc_2), new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap), "clickText")), new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "IsNullOrEmpty"), new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Ceq), new CodeMatch(OpCodes.Stloc_3) ) .Advance(1) .SetInstruction(new CodeInstruction(OpCodes.Ldc_I4_0)); // mark the correct star as birth point matcher.Start(); matcher.MatchForward(true, new CodeMatch(OpCodes.Ldc_R4), new CodeMatch(OpCodes.Stloc_1), new CodeMatch(OpCodes.Br), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(OpCodes.Stloc_1), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(OpCodes.Stloc_0), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(OpCodes.Brtrue) ) .Advance(-1) .SetAndAdvance(OpCodes.Nop, null) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 5)) .Insert(HarmonyLib.Transpilers.EmitDelegate <IsBirthStar>((UIVirtualStarmap starmap, int starIndex) => { return(starmap.starPool[starIndex].starData.id != starmap._galaxyData.birthStarId && starmap.starPool[starIndex].starData.id != starmap._galaxyData.birthPlanetId); })); // listen for general mouse clicks to deselect planet / solar system matcher.Start(); matcher.MatchForward(true, new CodeMatch(OpCodes.Br), new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap), "starPool")), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_Item"), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(UIVirtualStarmap.StarNode), "active")), new CodeMatch(OpCodes.Brfalse), new CodeMatch(OpCodes.Ldloc_S), new CodeMatch(OpCodes.Ldloc_0), new CodeMatch(OpCodes.Ceq) ) .Advance(3) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_0)) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <TrackPlayerClick>((UIVirtualStarmap starmap, int starIndex) => { bool pressing = VFInput.rtsConfirm.pressing; if ((pressing && !pressSpamProtector) && starIndex == -1) { if (starmap.clickText != "" && UIRoot.instance.uiGame.planetDetail.gameObject.activeSelf) // hide planet details { GameObject.Find("UI Root/Overlay Canvas/Galaxy Select/right-group").SetActive(true); UIRoot.instance.uiGame.planetDetail.gameObject.SetActive(false); } else if (starmap.clickText != "" && !UIRoot.instance.uiGame.planetDetail.gameObject.activeSelf) // hide solar system details { starmap.clickText = ""; starmap.OnGalaxyDataReset(); } pressSpamProtector = true; } })); return(matcher.InstructionEnumeration()); }
static IEnumerable <CodeInstruction> UpdateTargets_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator iL) { /* * Update search for new targets. Do not include targets that are already pending response from server. * Change: * if (!this.serving.Contains(num4) && (prebuildPool[i].itemRequired == 0 || prebuildPool[i].itemRequired <= this.player.package.GetItemCount((int)prebuildPool[i].protoId))) * * To: * if (!this.serving.Contains(num4) && !DroneManager.IsPendingBuildRequest(num4) && (prebuildPool[i].itemRequired == 0 || prebuildPool[i].itemRequired <= this.player.package.GetItemCount((int)prebuildPool[i].protoId))) */ var codeMatcher = new CodeMatcher(instructions, iL) .MatchForward(true, new CodeMatch(i => i.IsLdarg()), new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "serving"), new CodeMatch(i => i.IsLdloc()), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "Contains"), new CodeMatch(OpCodes.Brtrue) ); if (codeMatcher.IsInvalid) { NebulaModel.Logger.Log.Error("MechaDroneLogic_Transpiler.UpdateTargets_Transpiler 1 failed. Mod version not compatible with game version."); return(instructions); } var num4Instruction = codeMatcher.InstructionAt(-2); var jumpInstruction = codeMatcher.Instruction; codeMatcher = codeMatcher .Advance(1) .InsertAndAdvance(num4Instruction) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <int, bool> >((num4) => { return(DroneManager.IsPendingBuildRequest(num4)); })) .InsertAndAdvance(jumpInstruction); /* * Make sure targets are only chosen if player is closest to the build preview * Change: * if (a.sqrMagnitude > this.sqrMinBuildAlt && sqrMagnitude <= num2) * To: * if (DroneManager.AmIClosestPlayer(ref a) && a.sqrMagnitude > this.sqrMinBuildAlt && sqrMagnitude <= num2) */ codeMatcher = codeMatcher .MatchForward(false, new CodeMatch(i => i.IsLdloc()), new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "get_sqrMagnitude"), new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "sqrMinBuildAlt"), new CodeMatch(OpCodes.Ble_Un) ); if (codeMatcher.IsInvalid) { NebulaModel.Logger.Log.Error("MechaDroneLogic_Transpiler.UpdateTargets_Transpiler 2 failed. Mod version not compatible with game version."); return(codeMatcher.InstructionEnumeration()); } var aOperand = codeMatcher.Instruction.operand; var jumpOperand = codeMatcher.InstructionAt(4).operand; codeMatcher = codeMatcher .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, aOperand)) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <Vector3, bool> >((aVar) => { return(DroneManager.AmIClosestPlayer(ref aVar)); })) .InsertAndAdvance(new CodeInstruction(OpCodes.Brfalse, jumpOperand)); /* * Insert * DroneManager.BroadcastDroneOrder(droneId, entityId, stage); * After * this.serving.Add(num3); */ codeMatcher = codeMatcher .MatchForward(true, new CodeMatch(i => i.IsLdarg()), new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "serving"), new CodeMatch(i => i.IsLdloc()), new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "Add"), new CodeMatch(OpCodes.Pop) ); if (codeMatcher.IsInvalid) { NebulaModel.Logger.Log.Error("MechaDroneLogic_Transpiler.UpdateTargets_Transpiler 3 failed. Mod version not compatible with game version."); return(codeMatcher.InstructionEnumeration()); } // The index from drones[j] var droneIdInstruction = codeMatcher.InstructionAt(-8); // num3 from this.serving.Add(num3); var entityIdInstruction = codeMatcher.InstructionAt(-2); // drones[j].stage = 1; var stageInstruction = new CodeInstruction(OpCodes.Ldc_I4_1); return(codeMatcher .Advance(1) .InsertAndAdvance(droneIdInstruction) .InsertAndAdvance(entityIdInstruction) .InsertAndAdvance(stageInstruction) .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Action <int, int, int> >((droneId, entityId, stage) => { DroneManager.BroadcastDroneOrder(droneId, entityId, stage); })) .InstructionEnumeration()); }
public static IEnumerable <CodeInstruction> ShowLockedIconsUpdate(IEnumerable <CodeInstruction> instructions) { CodeMatcher matcher = new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Proto), nameof(Proto.ID))), new CodeMatch(OpCodes.Stloc_S), new CodeMatch(OpCodes.Ldloc_0)) .MatchForward(false, new CodeMatch(OpCodes.Ldloc_3)) .Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_2)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(Transpilers.EmitDelegate <Func <bool, int, UIBuildMenu, bool> >((result, index, that) => { if (result) { return(true); } if (!GameMain.history.TechUnlocked(1001) || index == 9) { return(false); } that.isAnyCategoryUnlocked = true; return(true); })); matcher.MatchForward(false, new CodeMatch(OpCodes.Ldarg_0), new CodeMatch(OpCodes.Ldfld), new CodeMatch(OpCodes.Ldc_I4_1) ) .MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(GameHistoryData), nameof(GameHistoryData.ItemUnlocked)))); CodeMatcher matcher2 = matcher.Clone(); matcher2.MatchForward(false, new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(UIButton), nameof(UIButton.highlighted)))) .Advance(1); Label label = (Label)matcher2.Operand; matcher.Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Pop)) .SetInstruction(new CodeInstruction(OpCodes.Br, label)); matcher.MatchForward(false, new CodeMatch(OpCodes.Ldfld), new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(Component), "get_gameObject")), new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(GameObject), nameof(GameObject.SetActive))) ) .Advance(5) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 9)) .InsertAndAdvance(Transpilers.EmitDelegate <Action <UIBuildMenu, int> >(ButtonLogic)); return(matcher.InstructionEnumeration()); }