public static IEnumerable <CodeInstruction> AllowBuildingWithLockedRecipes(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false,
                                                new CodeMatch(OpCodes.Ldarg_1),
                                                new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(GameHistoryData), nameof(GameHistoryData.RecipeUnlocked)))
                                                )
                                  .Repeat(codeMatcher =>
            {
                codeMatcher.Advance(-1).SetOpcodeAndAdvance(OpCodes.Nop)
                .SetOpcodeAndAdvance(OpCodes.Nop)
                .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_1));
            });

            matcher.Start().MatchForward(false,
                                         new CodeMatch(OpCodes.Ldarg_3),
                                         new CodeMatch(OpCodes.Ldc_I4_0),
                                         new CodeMatch(OpCodes.Ldelem_I4),
                                         new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(GameHistoryData), nameof(GameHistoryData.ItemUnlocked)))
                                         )
            .Advance(-1).SetOpcodeAndAdvance(OpCodes.Nop)
            .SetOpcodeAndAdvance(OpCodes.Nop)
            .SetOpcodeAndAdvance(OpCodes.Nop)
            .SetOpcodeAndAdvance(OpCodes.Nop)
            .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_1));

            return(matcher.InstructionEnumeration());
        }
Esempio n. 2
0
        static IEnumerable <CodeInstruction> LoadCurrentGame_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator iLGenerator)
        {
            /* using (BinaryReader binaryReader = new BinaryReader(fileStream)) => Create lzstream and replace binaryReader.
             * set PerformanceMonitor.BeginStream to lzstream.
             * if (fileStream.Length != binaryReader.ReadInt64()) => Replace binaryReader.ReadInt64() to pass file length check.
             * fileStream.Seek((long)num2, SeekOrigin.Current); => Use lzstream.Read to seek forward
             * binaryReader.Dispose(); => Dispose lzstream before fileStream close.
             */
            try
            {
                var matcher = new CodeMatcher(instructions, iLGenerator)
                              .MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(BinaryReader), new Type[] { typeof(FileStream) })))
                              .Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryReader"))
                              .MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream")));

                if (matcher.IsValid)
                {
                    matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream"));
                }

                matcher.Start().MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), "ReadInt64")))
                .Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthRead"))
                .MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IDisposable), "Dispose")))
                .Advance(1)
                .Insert(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "DisposeLzstream")))
                .MatchBack(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IO.Stream), "Seek")));
                if (matcher.IsValid)
                {
                    matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "ReadSeek"));
                }
                matcher.Start()
                .MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(GameSaveHeader))));
                if (matcher.IsValid)
                {
                    matcher.Set(OpCodes.Newobj, AccessTools.Constructor(typeof(CompressionGameSaveHeader))); //ReadHeader
                }
                EnableDecompress = true;
                return(matcher.InstructionEnumeration());
            }
            catch (Exception ex)
            {
                SaveUtil.logger.LogError("LoadCurrentGame_Transpiler failed. Mod version not compatible with game version.");
                SaveUtil.logger.LogError(ex);
            }
            return(instructions);
        }
Esempio n. 3
0
 private static IEnumerable <CodeInstruction> DysonSwarmGameTick_Transpiler(IEnumerable <CodeInstruction> instructions)
 {
     // Remove first part of the functions about computeShader
     // (this.computeShader.Set..., this.Dispatch_UpdateVel, this.Dispatch_UpdatePos)
     try
     {
         CodeMatcher matcher = new CodeMatcher(instructions)
                               .MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(DysonSwarm), nameof(DysonSwarm.Dispatch_UpdatePos))));
         int num = matcher.Pos + 1;
         matcher.Start()
         .RemoveInstructions(num);
         return(matcher.InstructionEnumeration());
     }
     catch
     {
         Log.Error("DysonSwarmGameTick_Transpiler failed. Mod version not compatible with game version.");
         return(instructions);
     }
 }
Esempio n. 4
0
        public static IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(false,
                                                new CodeMatch(OpCodes.Ldloc_S),
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.lpos))));

            object bpIndex = matcher.Instruction.operand;

            matcher.Start().MatchForward(false,
                                         new CodeMatch(OpCodes.Ldloc_S),
                                         new CodeMatch(OpCodes.Ldc_R4),
                                         new CodeMatch(OpCodes.Ldsfld),
                                         new CodeMatch(i =>
                                                       i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "GetVeinsInAreaNonAlloc"))
            .Advance(1)
            .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldloc, bpIndex))
            .InsertAndAdvance(
                Transpilers.EmitDelegate <Func <BuildPreview, float> >(preview =>
                                                                       DSPAdvancedMiner.getMinerRadius(preview.desc) + 4))

            .MatchForward(true,
                          new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(Vector3), nameof(Vector3.Dot))),
                          new CodeMatch(OpCodes.Stloc_S),
                          new CodeMatch(OpCodes.Ldc_R4))
            .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldloc, bpIndex))
            .InsertAndAdvance(
                Transpilers.EmitDelegate <Func <BuildPreview, float> >(preview =>
            {
                float radius = DSPAdvancedMiner.getMinerRadius(preview.desc);
                return(radius * radius);
            })
                )
            .MatchForward(true,
                          new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(Mathf), nameof(Mathf.Abs), new[] { typeof(float) })),
                          new CodeMatch(OpCodes.Ldc_R4))
            .SetInstructionAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Castclass, typeof(BuildTool)))
            .InsertAndAdvance(Transpilers.EmitDelegate <Func <BuildTool, float> >(tool => 400f / tool.planet.realRadius));

            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());
        }
Esempio n. 6
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);
                }
            }
        }
Esempio n. 7
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)
                            );
                    }
                }
            }
        }
Esempio n. 8
0
        public static IEnumerable <CodeInstruction> RematchRemotePairs_Transpiler(IEnumerable <CodeInstruction> instructions)
        {
            // tell clients about all changes to workShipDatas
            CodeMatcher matcher = new CodeMatcher(instructions)
                                  .MatchForward(true,
                                                new CodeMatch(OpCodes.Ldarg_0),
                                                new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), "workShipDatas")),
                                                new CodeMatch(OpCodes.Ldloc_S),
                                                new CodeMatch(OpCodes.Ldelema),
                                                new CodeMatch(OpCodes.Ldc_I4_1),
                                                new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(ShipData), "direction")))
                                  .Repeat(localMatcher =>
            {
                localMatcher
                .Advance(1)
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 10))
                .Insert(HarmonyLib.Transpilers.EmitDelegate <RematchRemotePairs>((StationComponent stationComponent, int index) =>
                {
                    ShipIndex.Add(index);
                    OtherGId.Add(stationComponent.workShipDatas[index].otherGId);
                    ItemId.Add(stationComponent.workShipDatas[index].itemId);
                    Direction.Add(stationComponent.workShipDatas[index].direction);
                }));
            });

            matcher
            .Start()
            .MatchForward(true,
                          new CodeMatch(OpCodes.Ldarg_0),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), "workShipDatas")),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldelema),
                          new CodeMatch(OpCodes.Ldc_I4_M1),
                          new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(ShipData), "direction")))
            .Repeat(localMatcher =>
            {
                localMatcher
                .Advance(1)
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 10))
                .Insert(HarmonyLib.Transpilers.EmitDelegate <RematchRemotePairs>((StationComponent stationComponent, int index) =>
                {
                    ShipIndex.Add(index);
                    OtherGId.Add(stationComponent.workShipDatas[index].otherGId);
                    ItemId.Add(stationComponent.workShipDatas[index].itemId);
                    Direction.Add(stationComponent.workShipDatas[index].direction);
                }));
            });

            matcher.Start()
            .MatchForward(true,
                          new CodeMatch(OpCodes.Ret))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .Insert(HarmonyLib.Transpilers.EmitDelegate <SendRematchPacket>((StationComponent stationComponent) =>
            {
                if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost && ShipIndex.Count > 0)
                {
                    Multiplayer.Session.Network.SendPacket(new ILSRematchRemotePairs(stationComponent.gid, ShipIndex, OtherGId, Direction, ItemId));
                }
                ShipIndex.Clear();
                OtherGId.Clear();
                Direction.Clear();
                ItemId.Clear();
            }));

            return(matcher.InstructionEnumeration());
        }
Esempio n. 9
0
        public static void ILSUpdateShipPos(StationComponent stationComponent, PlanetFactory factory, int timeGene, double dt, float shipSailSpeed, float shipWarpSpeed, int shipCarries, StationComponent[] gStationPool, AstroPose[] astroPoses, VectorLF3 relativePos, Quaternion relativeRot, bool starmap, int[] consumeRegister)
        {
            IEnumerable <CodeInstruction> Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il)
            {
                // find begin of ship movement computation, c# 460 IL 2090
                CodeMatcher matcher    = new CodeMatcher(instructions, il);
                int         indexStart = matcher
                                         .MatchForward(false,
                                                       new CodeMatch(i => i.IsLdarg()),
                                                       new CodeMatch(OpCodes.Ldc_R4),
                                                       new CodeMatch(OpCodes.Div),
                                                       new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "Sqrt"),
                                                       new CodeMatch(OpCodes.Stloc_S),
                                                       new CodeMatch(OpCodes.Ldloc_S),
                                                       new CodeMatch(OpCodes.Stloc_S),
                                                       new CodeMatch(OpCodes.Ldloc_S),
                                                       new CodeMatch(OpCodes.Ldc_R4),
                                                       new CodeMatch(OpCodes.Ble_Un))
                                         .Pos;

                // cut out only that part of original function, but keep the first 5 IL lines (they create the 'bool flag' which is needed)
                for (matcher.Start().Advance(6); matcher.Pos < indexStart;)
                {
                    matcher.SetAndAdvance(OpCodes.Nop, null);
                }

                // add null check at the beginning of the while(){} for gStationPool[shipData.otherGId] and if it is null skip this shipData until all data received from server
                matcher
                .MatchForward(true,
                              new CodeMatch(OpCodes.Br),
                              new CodeMatch(OpCodes.Ldarg_0),
                              new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), "workShipDatas")),
                              new CodeMatch(OpCodes.Ldloc_S),
                              new CodeMatch(OpCodes.Ldelem, typeof(ShipData)),
                              new CodeMatch(OpCodes.Stloc_S));
                object jmpNextLoopIter = matcher.InstructionAt(-5).operand;

                matcher.CreateLabelAt(matcher.Pos + 1, out Label jmpNormalFlow);
                matcher
                .Advance(1)
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_S, 7))     // gStationPool
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 60))    // shipData
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "otherGId")))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldelem, typeof(StationComponent)))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Brtrue, jmpNormalFlow))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 59))     // j
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_1))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Add))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Stloc_S, 59))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Br, jmpNextLoopIter));

                // remove c# 502-525 (adding item from landing ship to station and modify remote order and shifitng those arrays AND j-- (as we end up in an endless loop if not))
                indexStart = matcher
                             .MatchForward(false,
                                           new CodeMatch(OpCodes.Ldarg_0),
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "itemId")),
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "itemCount")),
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "inc")),
                                           new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "AddItem"))
                             .Pos;
                int indexEnd = matcher
                               .MatchForward(true,
                                             new CodeMatch(OpCodes.Sub),
                                             new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "Clear"),
                                             new CodeMatch(OpCodes.Ldloc_S),
                                             new CodeMatch(OpCodes.Ldc_I4_1),
                                             new CodeMatch(OpCodes.Sub),
                                             new CodeMatch(OpCodes.Stloc_S))
                               .Advance(1)
                               .Pos;

                for (matcher.Start().Advance(indexStart); matcher.Pos < indexEnd;)
                {
                    matcher.SetAndAdvance(OpCodes.Nop, null);
                }

                // c# 621 remove warp state entering as we do this triggered by host. client always messed up here for whatever reason so just tell him what to do.
                matcher
                .MatchForward(false,
                              new CodeMatch(OpCodes.Ldloca_S),
                              new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(ShipData), "warperCnt")),
                              new CodeMatch(OpCodes.Dup),
                              new CodeMatch(OpCodes.Ldind_I4),
                              new CodeMatch(OpCodes.Ldc_I4_1),
                              new CodeMatch(OpCodes.Sub),
                              new CodeMatch(OpCodes.Stind_I4),
                              new CodeMatch(OpCodes.Ldloca_S),
                              new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(ShipData), "warpState")));
                for (int i = 0; i < 15; i++)
                {
                    matcher.SetAndAdvance(OpCodes.Nop, null);
                }

                // remove c# 956 - 1054 (adding item from landing ship to station and modify remote order)
                indexStart = matcher
                             .MatchForward(false,
                                           new CodeMatch(OpCodes.Ldarg_S),
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "otherGId")),
                                           new CodeMatch(OpCodes.Ldelem_Ref),
                                           new CodeMatch(OpCodes.Stloc_S),
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), "storage")),
                                           new CodeMatch(OpCodes.Stloc_S),
                                           new CodeMatch(OpCodes.Ldarg_S),
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "planetA")))
                             .Pos;
                indexEnd = matcher
                           .MatchForward(true,
                                         new CodeMatch(OpCodes.Ldarg_0),
                                         new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), "remotePairCount")),
                                         new CodeMatch(OpCodes.Rem),
                                         new CodeMatch(OpCodes.Stloc_S),
                                         new CodeMatch(OpCodes.Ldloc_S),
                                         new CodeMatch(OpCodes.Ldloc_S),
                                         new CodeMatch(OpCodes.Bne_Un))
                           .Pos;
                for (matcher.Start().Advance(indexStart); matcher.Pos <= indexEnd;)
                {
                    matcher.SetAndAdvance(OpCodes.Nop, null);
                }

                // remove c# 1058 - 1093 (taking item from station and modify remote order)
                indexStart = matcher
                             .MatchForward(false,
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "itemId")),
                                           new CodeMatch(OpCodes.Stloc_S),
                                           new CodeMatch(OpCodes.Ldarg_S),
                                           new CodeMatch(OpCodes.Stloc_S),
                                           new CodeMatch(OpCodes.Ldloc_S),
                                           new CodeMatch(OpCodes.Ldloca_S),
                                           new CodeMatch(OpCodes.Ldloca_S),
                                           new CodeMatch(OpCodes.Ldloca_S),
                                           new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "TakeItem"))
                             .Pos;
                indexEnd = matcher
                           .MatchForward(true,
                                         new CodeMatch(OpCodes.Stind_I4),
                                         new CodeMatch(OpCodes.Leave),
                                         new CodeMatch(OpCodes.Ldloc_S),
                                         new CodeMatch(OpCodes.Brfalse),
                                         new CodeMatch(OpCodes.Ldloc_S),
                                         new CodeMatch(OpCodes.Call),
                                         new CodeMatch(OpCodes.Endfinally))
                           .Pos;
                for (matcher.Start().Advance(indexStart); matcher.Pos <= indexEnd;)
                {
                    matcher.SetAndAdvance(OpCodes.Nop, null);
                }

                return(matcher.InstructionEnumeration());
            }

            _ = Transpiler(null, null);
        }
Esempio n. 10
0
        public static IEnumerable <CodeInstruction> InternalTickRemote_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator il)
        {
            // tell client when a ship enters warp state. for some reason client gets messed up with warp counter on ships.
            CodeMatcher matcher = new CodeMatcher(instructions, il)
                                  .MatchForward(true,
                                                new CodeMatch(OpCodes.Ldloca_S),
                                                new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(ShipData), "warperCnt")),
                                                new CodeMatch(OpCodes.Dup),
                                                new CodeMatch(OpCodes.Ldind_I4),
                                                new CodeMatch(OpCodes.Ldc_I4_1),
                                                new CodeMatch(OpCodes.Sub),
                                                new CodeMatch(OpCodes.Stind_I4),
                                                new CodeMatch(OpCodes.Ldloca_S),
                                                new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(ShipData), "warpState")))
                                  .Advance(-1)
                                  .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
                                  .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 59))
                                  .Insert(HarmonyLib.Transpilers.EmitDelegate <ShipEnterWarpState> ((StationComponent stationComponent, int j) =>
            {
                if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost)
                {
                    Multiplayer.Session.Network.SendPacket(new ILSShipEnterWarp(stationComponent.gid, j));
                }
            }));

            // tell client about WorkShipBackToIdle here as we need to tell him j too
            // c# 522
            matcher.Start()
            .MatchForward(true,
                          new CodeMatch(OpCodes.Ldarg_0),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "shipIndex")),
                          new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "WorkShipBackToIdle"))
            .Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 59))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 60))
            .Insert(HarmonyLib.Transpilers.EmitDelegate <WorkShipBackToIdle>((StationComponent stationComponent, int j, ShipData shipData) =>
            {
                if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost)
                {
                    ILSWorkShipBackToIdle packet = new ILSWorkShipBackToIdle(stationComponent, shipData, j);
                    Multiplayer.Session.Network.SendPacket(packet);
                }
            }));

            // tell clients about AddItem() calls on host
            // c# 502
            matcher.Start()
            .MatchForward(true,
                          new CodeMatch(OpCodes.Ldarg_0),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "itemId")),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "itemCount")),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "inc")),
                          new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "AddItem"))
            .Advance(2)     // also skip Pop call
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 60))
            .Insert(HarmonyLib.Transpilers.EmitDelegate <AddItem>((StationComponent stationComponent, ShipData shipData) =>
            {
                if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost)
                {
                    Multiplayer.Session.Network.SendPacketToStar(new ILSShipAddTake(true, shipData.itemId, shipData.itemCount, stationComponent.gid, shipData.inc), GameMain.galaxy.PlanetById(stationComponent.planetId).star.id);
                }
            }));

            // c# 970
            matcher
            .MatchForward(true,
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "itemId")),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "itemCount")),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "inc")),
                          new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "AddItem"))
            .Advance(2)     // also skip Pop call
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 147))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 60))
            .Insert(HarmonyLib.Transpilers.EmitDelegate <AddItem>((StationComponent stationComponent, ShipData shipData) =>
            {
                if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost)
                {
                    Multiplayer.Session.Network.SendPacketToStar(new ILSShipAddTake(true, shipData.itemId, shipData.itemCount, stationComponent.gid, shipData.inc), GameMain.galaxy.PlanetById(stationComponent.planetId).star.id);
                }
            }));

            // tell clients about TakeItem() calls on host
            matcher
            .MatchForward(true,
                          new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "TakeItem"))
            // just before the call to TakeItem()
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 147))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 60))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(ShipData), "itemId")))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 157))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 59))
            .Insert(HarmonyLib.Transpilers.EmitDelegate <TakeItem>((StationComponent stationComponent, int itemId, int itemCount, int j) =>
            {
                if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost)
                {
                    Multiplayer.Session.Network.SendPacketToStar(new ILSShipAddTake(false, itemId, itemCount, stationComponent.gid, j), GameMain.galaxy.PlanetById(stationComponent.planetId).star.id);
                }
            }));

            // tell client about StationSTorage[] updates
            // c# 17
            matcher.Start()
            .MatchForward(true,
                          new CodeMatch(OpCodes.Ldarg_0),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), "storage")),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldelema),
                          new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(StationStore), "inc")),
                          new CodeMatch(OpCodes.Dup),
                          new CodeMatch(OpCodes.Ldind_I4),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Sub),
                          new CodeMatch(OpCodes.Stind_I4))
            .Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 26))
            .Insert(HarmonyLib.Transpilers.EmitDelegate <UpdateStorage>((StationComponent stationComponent, int index) =>
            {
                if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost)
                {
                    Multiplayer.Session.Network.SendPacketToStar(new ILSUpdateStorage(stationComponent.gid, index, stationComponent.storage[index].count, stationComponent.storage[index].inc), GameMain.galaxy.PlanetById(stationComponent.planetId).star.id);
                }
            }));

            // c# 242, 367
            matcher
            .MatchForward(true,
                          new CodeMatch(OpCodes.Ldarg_0),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(StationComponent), "storage")),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(SupplyDemandPair), "supplyIndex")),
                          new CodeMatch(OpCodes.Ldelema),
                          new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(StationStore), "inc")),
                          new CodeMatch(OpCodes.Dup),
                          new CodeMatch(OpCodes.Ldind_I4),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Sub),
                          new CodeMatch(OpCodes.Stind_I4))
            .Repeat(localMatcher =>
            {
                localMatcher
                .Advance(1)
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, UpdateStorageMatchCounter == 0 ? 30 : 52))
                .InsertAndAdvance(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(SupplyDemandPair), "supplyIndex")))
                .Insert(HarmonyLib.Transpilers.EmitDelegate <UpdateStorage>((StationComponent stationComponent, int index) =>
                {
                    if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost)
                    {
                        Multiplayer.Session.Network.SendPacketToStar(new ILSUpdateStorage(stationComponent.gid, index, stationComponent.storage[index].count, stationComponent.storage[index].inc), GameMain.galaxy.PlanetById(stationComponent.planetId).star.id);
                    }
                }));

                UpdateStorageMatchCounter++;
            });
            UpdateStorageMatchCounter = 0; // resetting here as it seems our patches are done twice

            // c# 1034
            matcher.Start()
            .MatchForward(true,
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(SupplyDemandPair), "supplyIndex")),
                          new CodeMatch(OpCodes.Ldelema),
                          new CodeMatch(OpCodes.Ldflda, AccessTools.Field(typeof(StationStore), "inc")),
                          new CodeMatch(OpCodes.Dup),
                          new CodeMatch(OpCodes.Ldind_I4),
                          new CodeMatch(OpCodes.Ldloc_S),
                          new CodeMatch(OpCodes.Sub),
                          new CodeMatch(OpCodes.Stind_I4))
            .Advance(1)
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 147))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 151))
            .InsertAndAdvance(new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(SupplyDemandPair), "supplyIndex")))
            .Insert(HarmonyLib.Transpilers.EmitDelegate <UpdateStorage>((StationComponent stationComponent, int index) =>
            {
                if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsHost)
                {
                    Multiplayer.Session.Network.SendPacketToStar(new ILSUpdateStorage(stationComponent.gid, index, stationComponent.storage[index].count, stationComponent.storage[index].inc), GameMain.galaxy.PlanetById(stationComponent.planetId).star.id);
                }
            }));

            return(matcher.InstructionEnumeration());
        }