private static IEnumerable <CodeInstruction> CreatePrebuilds_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il)
        {
            CodeMatcher matcher = new CodeMatcher(instructions, il)
                                  .MatchForward(false,
                                                new CodeMatch(OpCodes.Ldarg_0),
                                                new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "get_controller"),
                                                new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(PlayerController), nameof(PlayerController.cmd))),
                                                new CodeMatch(OpCodes.Ldc_I4_1),
                                                new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(CommandState), nameof(CommandState.stage))));

            if (matcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("BuildTool_Path.CreatePrebuilds_Transpiler failed. Mod version not compatible with game version.");
                return(instructions);
            }

            return(matcher
                   .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <bool> >(() =>
            {
                return Multiplayer.IsActive && Multiplayer.Session.Factories.IsIncomingRequest.Value && Multiplayer.Session.Factories.PacketAuthor != Multiplayer.Session.LocalPlayer.Id;
            }))
                   .CreateLabelAt(matcher.Pos + 19 + 22, out Label jmpLabel)
                   .InsertAndAdvance(new CodeInstruction(OpCodes.Brtrue, jmpLabel))
                   .InstructionEnumeration());
        }
Beispiel #2
0
        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());
        }
Beispiel #3
0
        static IEnumerable <CodeInstruction> CreatePrebuilds_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il)
        {
            CodeMatcher matcher = new CodeMatcher(instructions, il)
                                  .MatchForward(false,
                                                new CodeMatch(OpCodes.Ldarg_0),
                                                new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "get_controller"),
                                                new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(PlayerController), "cmd")),
                                                new CodeMatch(OpCodes.Ldc_I4_1),
                                                new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(CommandState), "stage")));

            if (matcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("BuildTool_Path.CreatePrebuilds_Transpiler failed. Mod version not compatible with game version.");
                return(instructions);
            }

            return(matcher
                   .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <bool> >(() =>
            {
                return SimulatedWorld.Initialized && (FactoryManager.EventFromServer || FactoryManager.EventFromClient) && FactoryManager.PacketAuthor != LocalPlayer.PlayerId;
            }))
                   .CreateLabelAt(matcher.Pos + 19, out Label jmpLabel)
                   .InsertAndAdvance(new CodeInstruction(OpCodes.Brtrue, jmpLabel))
                   .InstructionEnumeration());
        }
Beispiel #4
0
        public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .End()
                                  .MatchBack(false,
                                             new CodeMatch(OpCodes.Ldarg_0),
                                             new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(BuildTool_BlueprintPaste), nameof(BuildTool_BlueprintPaste.cursorTarget))));

            CodeMatcher matcher2 = matcher.Clone().MatchForward(false,
                                                                new CodeMatch(OpCodes.Ldloc_S),
                                                                new CodeMatch(OpCodes.Ldc_I4_S)
                                                                , new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.condition))));

            object previewVariable = matcher2.Operand;

            matcher.Advance(1);

            while (matcher.Opcode != OpCodes.Stloc_S)
            {
                matcher.RemoveInstruction();
            }

            matcher.InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, previewVariable))
            .InsertAndAdvance(Transpilers.EmitDelegate <Func <BuildTool_BlueprintPaste, BuildPreview, Vector3> >((tool, preview) =>
                                                                                                                 preview.lpos.normalized * Mathf.Min(tool.planet.realRadius * 0.025f, 20f)));

            return(matcher.InstructionEnumeration());
        }
Beispiel #5
0
        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());
        }
Beispiel #6
0
        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());
        }
Beispiel #7
0
        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());
        }
Beispiel #8
0
        private static IEnumerable <CodeInstruction> CreatePrebuilds_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il)
        {
            /*
             * Inserts
             *  if(!Multiplayer.IsActive)
             * Before trying to take items, so that all prebuilds are assumed to require items while in MP
             */
            CodeMatcher matcher = new CodeMatcher(instructions, il)
                                  .MatchForward(true,
                                                new CodeMatch(i => i.IsLdloc()), // count
                                                new CodeMatch(OpCodes.Ldc_I4_1),
                                                new CodeMatch(OpCodes.Ceq),
                                                new CodeMatch(OpCodes.Brfalse)
                                                );

            if (matcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("BuildTool_BlueprintPaste.CreatePrebuilds_Transpiler failed. Mod version not compatible with game version.");
                return(instructions);
            }

            object jumpOperand = matcher.Instruction.operand;

            matcher = matcher
                      .MatchBack(false,
                                 new CodeMatch(i => i.IsLdloc()),
                                 new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "item"),
                                 new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "ID"),
                                 new CodeMatch(i => i.IsStloc())
                                 );

            if (matcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("BuildTool_BlueprintPaste.CreatePrebuilds_Transpiler 2 failed. Mod version not compatible with game version.");
                return(instructions);
            }

            return(matcher
                   .InsertAndAdvance(HarmonyLib.Transpilers.EmitDelegate <Func <bool> >(() =>
            {
                return Multiplayer.IsActive;
            }))
                   .InsertAndAdvance(new CodeInstruction(OpCodes.Brtrue, jumpOperand))
                   .InstructionEnumeration());
        }
        static IEnumerable <CodeInstruction> CreatePrebuilds_Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            // Set int count = 1 before trying to use hand items or take tail items so that it passes if player did not generate event
            var codeMatcher = new CodeMatcher(instructions)
                              .MatchForward(false,
                                            new CodeMatch(i => i.IsLdarg()),
                                            new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "get_player"),
                                            new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_inhandItemId"));

            if (codeMatcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("BuildTool_Common.CreatePrebuilds_Transpiler failed. Mod version not compatible with game version.");
                return(instructions);
            }

            return(codeMatcher
                   .InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_1))
                   .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());
            }
Beispiel #11
0
        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());
        }
        static IEnumerable <CodeInstruction> CreatePrebuilds_Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            var codeMatcher = new CodeMatcher(instructions)
                              .MatchForward(false,
                                            new CodeMatch(i => i.IsLdarg()),
                                            new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "get_player"),
                                            new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "get_inhandItemId"));

            if (codeMatcher.IsInvalid)
            {
                NebulaModel.Logger.Log.Error("BuildTool_Common.CreatePrebuilds_Transpiler failed. Mod version not compatible with game version.");
                return(instructions);
            }

            // num = 1; from within the if statement
            var numInstruction = codeMatcher.InstructionAt(11);

            return(codeMatcher
                   .InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_1))
                   .InsertAndAdvance(numInstruction)
                   .InstructionEnumeration());
        }
Beispiel #13
0
        static void PatchHighlightInRedConstructionObstacles(CodeMatcher codeMatcherCursor, ILGenerator generator)
        {
            codeMatcherCursor.Start();
            codeMatcherCursor.MatchForward(true,
                                           new CodeMatch(OpCodes.Ldloc_3),
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List <GameObject>), "Item")),
                                           new CodeMatch(OpCodes.Stloc_S)
                                           );

            if (codeMatcherCursor.IsValid)
            {
                var obstacleLocal = (LocalBuilder)codeMatcherCursor.Operand;

                codeMatcherCursor.MatchForward(false,
                                               new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(Builder), nameof(Builder.obstaclesBuffer))),
                                               new CodeMatch(OpCodes.Ldloc_S), // Renderer
                                               new CodeMatch(OpCodes.Ldsfld, AccessTools.Field(typeof(Builder), nameof(Builder.builderObstacleMaterial))),
                                               new CodeMatch(OpCodes.Ldloc_S), // int
                                               new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(CommandBuffer), nameof(CommandBuffer.DrawRenderer), new Type[] { typeof(Renderer), typeof(Material), typeof(int) }))
                                               );

                if (codeMatcherCursor.IsValid)
                {
                    var rendererLocal     = (LocalBuilder)codeMatcherCursor.Advance(1).Operand;
                    var submeshIndexLocal = (LocalBuilder)codeMatcherCursor.Advance(2).Operand;

                    codeMatcherCursor.Advance(-3);

                    // Check if obstacle destroying is enabled
                    var disabledDestroyObstaclesLabel = generator.DefineLabel();
                    codeMatcherCursor
                    .InsertAndAdvance(
                        new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Config), nameof(Config.Instance))),
                        new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Config), nameof(Config.destroyLargerObstaclesOnConstruction))),
                        new CodeInstruction(OpCodes.Brfalse_S, disabledDestroyObstaclesLabel)
                        );

                    // Then check if construction obstacle
                    var notConstructionObstacleLabel = generator.DefineLabel();
                    codeMatcherCursor
                    .InsertAndAdvance(
                        new CodeInstruction(OpCodes.Ldloc_S, obstacleLocal),
                        new CodeInstruction(OpCodes.Call, AccessTools.Method($"{typeof(UpdateAllowedPatch)}:{nameof(IsConstructionObstacle)}", new Type[] { typeof(GameObject) })),
                        new CodeInstruction(OpCodes.Brfalse_S, notConstructionObstacleLabel)
                        );

                    // Then define destroy material
                    codeMatcherCursor
                    .InsertAndAdvance(
                        new CodeInstruction(OpCodes.Call, AccessTools.Method($"{typeof(UpdateAllowedPatch)}:{nameof(DefineDestroyMaterialIfNot)}"))
                        );

                    // And then highlight in red
                    codeMatcherCursor
                    .InsertAndAdvance(
                        new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(Builder), nameof(Builder.obstaclesBuffer))),
                        new CodeInstruction(OpCodes.Ldloc_S, rendererLocal),     // Renderer
                        new CodeInstruction(OpCodes.Ldsfld, AccessTools.Field(typeof(UpdateAllowedPatch), nameof(destroyableObstacleMat))),
                        new CodeInstruction(OpCodes.Ldloc_S, submeshIndexLocal), // int
                        new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(CommandBuffer), nameof(CommandBuffer.DrawRenderer), new Type[] { typeof(Renderer), typeof(Material), typeof(int) }))
                        );

                    // Then jump over instructions within "else" block
                    var isConstructionObstacleLabel = generator.DefineLabel();
                    codeMatcherCursor
                    .InsertAndAdvance(
                        new CodeInstruction(OpCodes.Br_S, isConstructionObstacleLabel)
                        );

                    // Or else highlight in default (yellow)
                    codeMatcherCursor.Instruction.WithLabels(disabledDestroyObstaclesLabel, notConstructionObstacleLabel);
                    codeMatcherCursor.Advance(5);

                    codeMatcherCursor.Instruction.WithLabels(isConstructionObstacleLabel);
                }
            }
        }
Beispiel #14
0
        static void PatchClearFromConstructionObstacles(CodeMatcher codeCursor, ILGenerator generator)
        {
            codeCursor.Start();
            codeCursor.MatchForward(false,
                                    new CodeMatch(OpCodes.Ldloc_1),
                                    new CodeMatch(OpCodes.Ldloc_3),
                                    new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List <GameObject>), nameof(List <GameObject> .Count))),
                                    new CodeMatch(OpCodes.Ldc_I4_0),
                                    new CodeMatch(OpCodes.Ceq),
                                    new CodeMatch(OpCodes.And),
                                    new CodeMatch(OpCodes.Stloc_1)
                                    );

            if (codeCursor.IsValid)
            {
                // Remove boolean result assignent as we are "moving" it to after obstacle highlighting block
                var labels = codeCursor.Instruction.ExtractLabels();
                codeCursor.RemoveInstructions(7);
                codeCursor.AddLabels(labels);

                codeCursor.MatchForward(true,
                                        new CodeMatch(OpCodes.Ldloc_3),
                                        new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List <GameObject>), nameof(List <GameObject> .Count))),
                                        new CodeMatch(OpCodes.Ldc_I4_0),
                                        new CodeMatch(OpCodes.Ble)
                                        );

                if (codeCursor.IsValid)
                {
                    var countCondBranchOperand = (Label)codeCursor.Operand;
                    codeCursor.MatchForward(false, new CodeMatch(instruction => instruction.labels.Contains(countCondBranchOperand)));

                    if (codeCursor.IsValid)
                    {
                        labels = codeCursor.Instruction.ExtractLabels();

                        // Make it to allow construction by clearing contruction obstacles for boolean result assignment if destroying obstacles is enabled
                        var disabledDestroyObstaclesLabel = generator.DefineLabel();
                        codeCursor.InsertAndAdvance(
                            new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Config), nameof(Config.Instance))).WithLabels(labels),
                            new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Config), nameof(Config.destroyLargerObstaclesOnConstruction))),
                            new CodeInstruction(OpCodes.Brfalse_S, disabledDestroyObstaclesLabel)
                            );

                        codeCursor.InsertAndAdvance(
                            new CodeInstruction(OpCodes.Ldloc_3),
                            new CodeInstruction(OpCodes.Call, AccessTools.Method($"{typeof(UpdateAllowedPatch)}:{nameof(ClearConstructionObstacles)}", new Type[] { typeof(List <GameObject>) }))
                            );

                        // Assign boolean result here (was before highlighting block)
                        codeCursor.InsertAndAdvance(
                            new CodeInstruction(OpCodes.Ldloc_1).WithLabels(disabledDestroyObstaclesLabel),
                            new CodeInstruction(OpCodes.Ldloc_3),
                            new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List <GameObject>), nameof(List <GameObject> .Count))),
                            new CodeInstruction(OpCodes.Ldc_I4_0),
                            new CodeInstruction(OpCodes.Ceq),
                            new CodeInstruction(OpCodes.And),
                            new CodeInstruction(OpCodes.Stloc_1)
                            );
                    }
                }
            }
        }
        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());
        }
Beispiel #17
0
        public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il)
        {
            var matcher = new CodeMatcher(instructions, il);

            // Search for the first 4 consecative Ldc_I4 instructions (a Color constructor after if statements which move the trash can in other conditions)
            matcher.MatchForward(false, new CodeMatch(OpCodes.Ldc_I4, 150), new CodeMatch(OpCodes.Ldc_I4, 150), new CodeMatch(OpCodes.Ldc_I4, 150), new CodeMatch(OpCodes.Ldc_I4, 150), new CodeMatch(OpCodes.Newobj));

            matcher.Instruction.opcode  = OpCodes.Nop;
            matcher.Instruction.operand = null;
            matcher.Advance(1);

            Label bailOut;

            matcher.CreateLabelAt(matcher.Pos, out bailOut);

            var trashOffsetCode = new CodeInstruction[]
            {
                new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.playerInventory))),
                new CodeInstruction(OpCodes.Brfalse_S, bailOut),

                new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player))),
                new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.myPlayer))),
                new CodeInstruction(OpCodes.Ldelem_Ref),

                new CodeInstruction(OpCodes.Callvirt, typeof(Player).GetMethod("GetModPlayer", new Type[] { }).MakeGenericMethod(typeof(StoragePlayer))),
                new CodeInstruction(OpCodes.Ldfld, typeof(StoragePlayer).GetField(nameof(StoragePlayer.StorageAccess))),
                new CodeInstruction(OpCodes.Ldfld, typeof(Point16).GetField(nameof(Point16.X))),
                new CodeInstruction(OpCodes.Ldc_I4_0),
                new CodeInstruction(OpCodes.Blt_S, bailOut),

                new CodeInstruction(OpCodes.Ldloc_0),
                new CodeInstruction(OpCodes.Ldsflda, typeof(UI).GetField(nameof(UI.TrashSlotOffset))),
                new CodeInstruction(OpCodes.Ldfld, typeof(Point16).GetField(nameof(Point16.X))),
                new CodeInstruction(OpCodes.Add),
                new CodeInstruction(OpCodes.Stloc_0),

                new CodeInstruction(OpCodes.Ldloc_1),
                new CodeInstruction(OpCodes.Ldsflda, typeof(UI).GetField(nameof(UI.TrashSlotOffset))),
                new CodeInstruction(OpCodes.Ldfld, typeof(Point16).GetField(nameof(Point16.Y))),
                new CodeInstruction(OpCodes.Add),
                new CodeInstruction(OpCodes.Stloc_1),

                new CodeInstruction(OpCodes.Ldc_R4, 0.755f),
                new CodeInstruction(OpCodes.Stsfld, typeof(Main).GetField(nameof(Main.inventoryScale)))
            };

            matcher.InsertAndAdvance(trashOffsetCode);

            matcher.Advance(1);
            matcher.InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4, 150));

            matcher.MatchForward(true, new CodeMatch(OpCodes.Call, typeof(ChestUI).GetMethod("Draw", new Type[] { typeof(Microsoft.Xna.Framework.Graphics.SpriteBatch) })));
            matcher.MatchForward(true, new CodeMatch(OpCodes.Ldsfld), new CodeMatch(OpCodes.Ldsfld), new CodeMatch(OpCodes.Ldelem_Ref), new CodeMatch(OpCodes.Ldfld), new CodeMatch(OpCodes.Ldc_I4_M1), new CodeMatch(OpCodes.Bne_Un), new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.npcShop))), new CodeMatch(OpCodes.Brtrue));

            var iconsLabel = matcher.Instruction.operand;

            matcher.Advance(1);

            var icon1Code = new CodeInstruction[]
            {
                new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player))),
                new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.myPlayer))),
                new CodeInstruction(OpCodes.Ldelem_Ref),

                new CodeInstruction(OpCodes.Callvirt, typeof(Player).GetMethod("GetModPlayer", new Type[] { }).MakeGenericMethod(typeof(StoragePlayer))),
                new CodeInstruction(OpCodes.Ldfld, typeof(StoragePlayer).GetField(nameof(StoragePlayer.StorageAccess))),
                new CodeInstruction(OpCodes.Ldfld, typeof(Point16).GetField(nameof(Point16.X))),

                new CodeInstruction(OpCodes.Ldc_I4_0),
                new CodeInstruction(OpCodes.Bgt, iconsLabel)
            };

            matcher.InsertAndAdvance(icon1Code);

            matcher.MatchForward(true, new CodeMatch(OpCodes.Callvirt, typeof(Player).GetMethod("QuickStackAllChests")));
            var quickStackCode = new CodeInstruction[]
            {
                new CodeInstruction(OpCodes.Ldc_I4_0),
                new CodeInstruction(OpCodes.Call, typeof(StorageUI).GetMethod("TryQuickStack", new Type[] { typeof(bool) })),
                new CodeInstruction(OpCodes.Pop)
            };

            matcher.InsertAndAdvance(quickStackCode);

            matcher.MatchForward(true, new CodeMatch(OpCodes.Ldsfld), new CodeMatch(OpCodes.Ldsfld), new CodeMatch(OpCodes.Ldelem_Ref), new CodeMatch(OpCodes.Ldfld), new CodeMatch(OpCodes.Ldc_I4_M1), new CodeMatch(OpCodes.Bne_Un), new CodeMatch(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.npcShop))), new CodeMatch(OpCodes.Brtrue));

            iconsLabel = matcher.Instruction.operand;
            matcher.Advance(1);

            var icon2Code = new CodeInstruction[]
            {
                new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.player))),
                new CodeInstruction(OpCodes.Ldsfld, typeof(Main).GetField(nameof(Main.myPlayer))),
                new CodeInstruction(OpCodes.Ldelem_Ref),

                new CodeInstruction(OpCodes.Callvirt, typeof(Player).GetMethod("GetModPlayer", new Type[] { }).MakeGenericMethod(typeof(StoragePlayer))),
                new CodeInstruction(OpCodes.Ldfld, typeof(StoragePlayer).GetField(nameof(StoragePlayer.StorageAccess))),
                new CodeInstruction(OpCodes.Ldfld, typeof(Point16).GetField(nameof(Point16.X))),

                new CodeInstruction(OpCodes.Ldc_I4_0),
                new CodeInstruction(OpCodes.Bgt, iconsLabel)
            };

            matcher.InsertAndAdvance(icon2Code);

            return(matcher.InstructionEnumeration());
        }