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); }
//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); }
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"); }
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))); }
protected override void OnSubModuleLoad() { base.OnSubModuleLoad(); _extender = new UIExtender("CampMod"); _extender.Register(); var harmony = new Harmony("net.shdwp.CampMod"); harmony.PatchAll(); }
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); }
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(); }
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"); }
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)); }