protected override void OnSubModuleLoad()
        {
            base.OnSubModuleLoad();

            _extender = new UIExtender("HorseAmountIndicatorMod");
            _extender.Register();
        }
        /// <summary>
        /// Transpiler which replaces constructors of view models with their expanded counterparts.
        /// Only replaces constructors which are affected by current mod.
        /// </summary>
        /// <param name="moduleName"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public static IEnumerable <CodeInstruction> InstantiationCallsiteTranspiler(string moduleName, IEnumerable <CodeInstruction> input)
        {
            return(input.Select(op =>
            {
                var component = UIExtender.RuntimeFor(moduleName).ViewModelComponent;
                if (op.opcode != OpCodes.Newobj)
                {
                    return op;
                }

                var constructor = op.operand as ConstructorInfo;
                var type = constructor.DeclaringType;

                if (type == null || !type.IsSubclassOf(typeof(TaleWorlds.Library.ViewModel)))
                {
                    return op;
                }

                var baseType = component.BaseTypeForPossiblyExtendedType(type);
                if (baseType != null && component.ExtendsViewModelType(baseType))
                {
                    op.operand = component.ExtendedViewModelTypeForType(baseType, type).GetConstructor(constructor.GetParameters().Types());
                }

                return op;
            }));
        }
        /// <summary>
        /// Method inserted into `WidgetFactory.LoadFrom`, which patches XmlDocument.
        /// A number of those are run in sequence for each extension.
        /// </summary>
        /// <param name="moduleName"></param>
        /// <param name="path"></param>
        /// <param name="document"></param>
        public static void ProcessMovieDocumentIfNeeded(string moduleName, string path, XmlDocument document)
        {
            // currently replicates exactly what WidgetFactory is doing
            var movieName = Path.GetFileNameWithoutExtension(path);

            // proxy call to ProcessMovieIfNeeded
            UIExtender.RuntimeFor(moduleName).PrefabComponent.ProcessMovieIfNeeded(movieName, document);
        }
Example #4
0
        //Initialize UIExtender and CustomMission Instance
        protected override void OnSubModuleLoad()
        {
            base.OnSubModuleLoad();

            _extender = new UIExtender("ExampleUIMod");
            _extender.Register();

            this._editorhandler = new CustomMissionManagerHandler();
        }
        /// <summary>
        /// Proxy method to ViewModelComponent.InitializeMixinsForViewModelInstance
        /// </summary>
        /// <param name="moduleName"></param>
        /// <param name="t"></param>
        /// <param name="instance"></param>
        public static void InitializeMixinsForVMInstance(string moduleName, Type t, object instance)
        {
            var runtime = UIExtender.RuntimeFor(moduleName);

            // Verify should have been called at this point
            Utils.SoftAssert(runtime.VerifyCalled, $"UIExtender.Verify was not called!");

            runtime.ViewModelComponent.InitializeMixinsForVMInstance(t, instance);
        }
Example #6
0
        protected override void OnSubModuleLoad()
        {
            Debug.Print("NCSStork entered OnSubModuleLoad");
            base.OnSubModuleLoad();

            _extender = new UIExtender("NCSStork");
            _extender.Register(typeof(Main).Assembly);
            _extender.Enable();
            Debug.Print("NCSStork enabled UIExtender");
        }
Example #7
0
        public void MixinPropertyIsInjectedTest()
        {
            var uiExtender = new UIExtender(nameof(MixinPropertyIsInjectedTest));

            uiExtender.Register(typeof(PrefabsTests).Assembly);
            uiExtender.Enable();

            var viewModel = new TestVM();

            Assert.True(viewModel.Properties.Contains(nameof(TestVMMixin.MixinProperty)));
        }
Example #8
0
        protected override void OnSubModuleLoad()
        {
            base.OnSubModuleLoad();

            _extender = new UIExtender("CampMod");
            _extender.Register();

            var harmony = new Harmony("net.shdwp.CampMod");

            harmony.PatchAll();
        }
Example #9
0
        public void MixinMethodIsCalledTest()
        {
            var uiExtender = new UIExtender(nameof(MixinMethodIsCalledTest));

            uiExtender.Register(typeof(PrefabsTests).Assembly);
            uiExtender.Enable();

            var viewModel = new TestVM();

            viewModel.ExecuteCommand(nameof(TestVMMixin.MixinMethod), Array.Empty <object>());
            Assert.True(TestVMMixin.MixinMethodCalled);
        }
Example #10
0
        public void PrefabsInsertTest()
        {
            var uiExtender = new UIExtender("TestModule");

            uiExtender.Register(typeof(PrefabsTests).Assembly);
            uiExtender.Enable();

            var widgetTemplateInsert = UIResourceManager.WidgetFactory.GetCustomType("Insert").RootTemplate;
            List <WidgetTemplate>?childrenInsert1 = GetChildren(widgetTemplateInsert);
            List <WidgetTemplate>?childrenInsert2 = GetChildren(childrenInsert1[0]);
            List <WidgetTemplate>?childrenInsert3 = GetChildren(childrenInsert2[0]);

            Assert.True(childrenInsert3.Count == Elements + 1);
            Assert.True(childrenInsert3[4].Id == "Insert");

            var widgetTemplateReplace = UIResourceManager.WidgetFactory.GetCustomType("ReplaceKeepChildren").RootTemplate;
            List <WidgetTemplate>?childrenReplace1 = GetChildren(widgetTemplateReplace);
            List <WidgetTemplate>?childrenReplace2 = GetChildren(childrenReplace1[0]);
            List <WidgetTemplate>?childrenReplace3 = GetChildren(childrenReplace2[0]);

            Assert.True(childrenReplace3.Count == Elements);
            Assert.True(childrenReplace3[2].Id == "Replaced");

            var widgetTemplateInsertAsSiblingAppend = UIResourceManager.WidgetFactory.GetCustomType("InsertAsSiblingAppend").RootTemplate;
            List <WidgetTemplate>?childrenInsertAsSiblingAppend1 = GetChildren(widgetTemplateInsertAsSiblingAppend);
            List <WidgetTemplate>?childrenInsertAsSiblingAppend2 = GetChildren(childrenInsertAsSiblingAppend1[0]);
            List <WidgetTemplate>?childrenInsertAsSiblingAppend3 = GetChildren(childrenInsertAsSiblingAppend2[0]);

            Assert.True(childrenInsertAsSiblingAppend3.Count == Elements + 1);
            Assert.True(childrenInsertAsSiblingAppend3[1].Id == "InsertAsSiblingAppend");

            var widgetTemplateInsertAsSiblingPrepend = UIResourceManager.WidgetFactory.GetCustomType("InsertAsSiblingPrepend").RootTemplate;
            List <WidgetTemplate>?childrenInsertAsSiblingPrepend1 = GetChildren(widgetTemplateInsertAsSiblingPrepend);
            List <WidgetTemplate>?childrenInsertAsSiblingPrepend2 = GetChildren(childrenInsertAsSiblingPrepend1[0]);
            List <WidgetTemplate>?childrenInsertAsSiblingPrepend3 = GetChildren(childrenInsertAsSiblingPrepend2[0]);

            Assert.True(childrenInsertAsSiblingPrepend3.Count == Elements + 1);
            Assert.True(childrenInsertAsSiblingPrepend3[0].Id == "InsertAsSiblingPrepend");

            var widgetTemplateSetAttribute = UIResourceManager.WidgetFactory.GetCustomType("SetAttribute").RootTemplate;
            List <WidgetTemplate>?childrenSetAttribute1 = GetChildren(widgetTemplateSetAttribute);
            List <WidgetTemplate>?childrenSetAttribute2 = GetChildren(childrenSetAttribute1[0]);
            List <WidgetTemplate>?childrenSetAttribute3 = GetChildren(childrenSetAttribute2[0]);

            Assert.True(childrenSetAttribute3.Count == Elements);
            Assert.True(childrenSetAttribute3[3].AllAttributes.Any(a => a.Key == "CustomAttribute" && a.Value == "Value"));

            uiExtender.Disable();
        }
Example #11
0
        public void PrefabsInsertTest()
        {
            var uiExtender = new UIExtender("TestModule");

            uiExtender.Register(typeof(PrefabsTests).Assembly);
            uiExtender.Enable();

            var widgetTemplateInsert = MockWidgetFactory.WidgetPrefabInsert.RootTemplate;
            var childrenInsert1      = GetChildren(widgetTemplateInsert);
            var childrenInsert2      = GetChildren(childrenInsert1[0]);
            var childrenInsert3      = GetChildren(childrenInsert2[0]);

            Assert.True(childrenInsert3.Count == 5);
            Assert.True(childrenInsert3[4].Id == "Insert");

            var widgetTemplateReplace = MockWidgetFactory.WidgetPrefabReplace.RootTemplate;
            var childrenReplace1      = GetChildren(widgetTemplateReplace);
            var childrenReplace2      = GetChildren(childrenReplace1[0]);
            var childrenReplace3      = GetChildren(childrenReplace2[0]);

            Assert.True(childrenReplace3.Count == 4);
            Assert.True(childrenReplace3[1].Id == "Replaced");

            var widgetTemplateInsertAsSiblingAppend = MockWidgetFactory.WidgetPrefabInsertAsSiblingAppend.RootTemplate;
            var childrenInsertAsSiblingAppend1      = GetChildren(widgetTemplateInsertAsSiblingAppend);
            var childrenInsertAsSiblingAppend2      = GetChildren(childrenInsertAsSiblingAppend1[0]);
            var childrenInsertAsSiblingAppend3      = GetChildren(childrenInsertAsSiblingAppend2[0]);

            Assert.True(childrenInsertAsSiblingAppend3.Count == 5);
            Assert.True(childrenInsertAsSiblingAppend3[1].Id == "InsertAsSiblingAppend");

            var widgetTemplateInsertAsSiblingPrepend = MockWidgetFactory.WidgetPrefabInsertAsSiblingPrepend.RootTemplate;
            var childrenInsertAsSiblingPrepend1      = GetChildren(widgetTemplateInsertAsSiblingPrepend);
            var childrenInsertAsSiblingPrepend2      = GetChildren(childrenInsertAsSiblingPrepend1[0]);
            var childrenInsertAsSiblingPrepend3      = GetChildren(childrenInsertAsSiblingPrepend2[0]);

            Assert.True(childrenInsertAsSiblingPrepend3.Count == 5);
            Assert.True(childrenInsertAsSiblingPrepend3[0].Id == "InsertAsSiblingPrepend");
        }
Example #12
0
 protected override void OnSubModuleLoad()
 {
     base.OnSubModuleLoad();
     UIExtender.Register();
 }
        /// <summary>
        /// Transpiler for `LoadFrom` method that apply patches to loaded XML file
        /// </summary>
        /// <param name="moduleName"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public static IEnumerable <CodeInstruction> PrefabLoadTranspiler(string moduleName, IEnumerable <CodeInstruction> input)
        {
            var nopMarkName = "UIExtenderLib";

            var loadXmlMethod = typeof(UIExtenderRuntimeLib).GetMethod(nameof(UIExtenderRuntimeLib.LoadXmlDocument));
            var list          = new List <CodeInstruction>(input);

            DynamicMethod CreateDynamicMethod(MethodInfo parentMethod)
            {
                var method = new DynamicMethod($"{Guid.NewGuid()}", null, new [] { typeof(string), typeof(string), typeof(XmlDocument) });
                var gen    = method.GetILGenerator();

                if (parentMethod != null)
                {
                    gen.Emit(OpCodes.Ldarg_0);
                    gen.Emit(OpCodes.Ldarg_1);
                    gen.Emit(OpCodes.Ldarg_2);
                    gen.EmitCall(OpCodes.Call, parentMethod, null);
                }

                var processMethod = typeof(UIExtenderRuntimeLib).GetMethod(nameof(UIExtenderRuntimeLib.ProcessMovieDocumentIfNeeded));

                Debug.Assert(processMethod != null);
                gen.Emit(OpCodes.Ldstr, moduleName);
                gen.Emit(OpCodes.Ldarg_1);
                gen.Emit(OpCodes.Ldarg_2);
                gen.EmitCall(OpCodes.Call, processMethod, null);
                gen.Emit(OpCodes.Ret);

                return(method);
            }

            int FindAlreadyPatchedIndex()
            {
                for (var i = 0; i < list.Count(); i++)
                {
                    if (list[i].opcode == OpCodes.Nop && (string)list[i].operand == nopMarkName)
                    {
                        return(i);
                    }
                }

                return(-1);
            }

            void ApplyInitialPatch(DynamicMethod method)
            {
                var additions = new List <CodeInstruction>();

                additions.Add(new CodeInstruction(OpCodes.Ldstr, moduleName));
                additions.Add(new CodeInstruction(OpCodes.Ldarg_2));
                additions.Add(new CodeInstruction(OpCodes.Ldloc_0));
                additions.Add(new CodeInstruction(OpCodes.Call, loadXmlMethod));

                // @TODO: replace NOP with Label?
                additions.Add(new CodeInstruction(OpCodes.Nop, nopMarkName));
                additions.Add(new CodeInstruction(OpCodes.Ldstr, moduleName));
                additions.Add(new CodeInstruction(OpCodes.Ldarg_2));
                additions.Add(new CodeInstruction(OpCodes.Ldloc_0));
                additions.Add(new CodeInstruction(OpCodes.Call, method));

                // @TODO: reformat/rewrite
                var from  = list.TakeWhile(i => !(i.opcode == OpCodes.Newobj && (i.operand as ConstructorInfo).DeclaringType == typeof(XmlReaderSettings))).Count() + 2;
                var to    = from + list.Skip(from).TakeWhile(i => !(i.opcode == OpCodes.Newobj && (i.operand as ConstructorInfo).DeclaringType == typeof(WidgetPrefab))).Count();
                var count = to - from;

                list.RemoveRange(from, count);
                list.InsertRange(from, additions);

                from  = from + additions.Count;
                count = to - from;

                if (Utils.SoftAssert(count > 0, "Don't have enought NOP space in the IL!"))
                {
                    list.InsertRange(from, Enumerable.Repeat(new CodeInstruction(OpCodes.Nop), count));
                }
                else
                {
                    UIExtender.RuntimeFor(moduleName).AddUserError($"Failed to patch {moduleName} (outdated).");
                }
            }

            var index = FindAlreadyPatchedIndex();

            if (index == -1)
            {
                var method = CreateDynamicMethod(null);
                ApplyInitialPatch(method);
            }
            else
            {
                var offset      = list.Skip(index).TakeWhile(i => i.opcode != OpCodes.Call).Count();
                var instruction = list[index + offset];

                if (Utils.SoftAssert(instruction.opcode == OpCodes.Call, $"Invalid instruction found at marker!"))
                {
                    instruction.operand = CreateDynamicMethod((MethodInfo)instruction.operand);
                }
                else
                {
                    UIExtender.RuntimeFor(moduleName).AddUserError($"Failed to patch {moduleName} (outdated).");
                }
            }

            return(list);
        }
 /// <summary>
 /// Postfix that is used to call `OnRefresh()` on attached mixins
 /// </summary>
 /// <param name="moduleName"></param>
 /// <param name="__instance"></param>
 public static void RefreshPostfix(string moduleName, object __instance)
 {
     UIExtender.RuntimeFor(moduleName).ViewModelComponent.RefreshMixinForVMInstance(__instance);
 }
 /// <summary>
 /// Proxy method to ViewModelComponent.FinalizeMixinsForInstance
 /// </summary>
 /// <param name="moduleName"></param>
 /// <param name="instance"></param>
 public static void FinalizeMixinsForVMInstance(string moduleName, object instance)
 {
     UIExtender.RuntimeFor(moduleName).ViewModelComponent.FinalizeMixinForVMInstance(instance);
 }
 /// <summary>
 /// Proxy method to ViewModelComponent.MixinInstanceForObject
 /// </summary>
 /// <param name="moduleName"></param>
 /// <param name="mixinType"></param>
 /// <param name="instance"></param>
 /// <returns></returns>
 public static object MixinInstanceForVMInstance(string moduleName, Type mixinType, object instance)
 {
     return(UIExtender.RuntimeFor(moduleName).ViewModelComponent.MixinInstanceForVMInstance(mixinType, instance));
 }