Integrate Fluffy's Animal Tab mod

Started by VouLT, October 12, 2016, 06:00:43 PM

Previous topic - Next topic

VouLT

So, I did this mod and the only missing feature is integrating it with Animal Tab mod (if possible).

My idea is to use Reflection and just try to grab the Fluffy's MainTabWindow_Animals class instance. Basically something like this:

My MOD's class

[StaticConstructorOnStartup]
public class MainTabWindow_Animals_Policies : MainTabWindow_Animals
{
    public MainTabWindow_Animals_Policies()
    {
         AnimalPolicyManager.InitMapComponent();
    }

    public override void DoWindowContents(Rect fillRect)
    {
        if (Widgets_AnimalTab.AnimalTabAvailable)
        {
            Widgets_AnimalTab.DoWindowContents(fillRect);
        }
        else
        {
            base.DoWindowContents(fillRect);
        }
        ..


Widgets_AnimalTab

    [StaticConstructorOnStartup]
    public class Widgets_AnimalTab
    {
        private const string PF_MOD_NAME = "Animals Tab";
        private const string PF_TYPE_UTILS = "Fluffy.MainTabWindow_Animals";
        private const string PF_METHOD_TABWINDOW = "DoWindowContents";
        private const BindingFlags BINDINGDLAGS_ALL = (BindingFlags)60; // public + private + static + instance;

        private static bool _anyError = false;
        private static bool _initialized = false;
        private static bool _available = false;

        private static MethodInfo _hasMainTabWindow_Animals;

        public static bool AnimalTabAvailable
        {
            get
            {
                if (!_initialized)
                    Initialize();
                return (_available && !_anyError);
            }
        }

        private static void Initialize()
        {
            _available = LoadedModManager.RunningMods.Any(mod => mod.Name == PF_MOD_NAME);
            _initialized = true;
            if (_available)
            {
                try
                {
                    // get the assembly
                    var PF_assembly = LoadedModManager
                                        .RunningMods.First(mod => mod.Name == PF_MOD_NAME)
                                        .assemblies.loadedAssemblies.First();

                    if (PF_assembly == null)
                    {
                        throw new Exception("Animal Tab assembly not found.");
                    }

                    var PF_UtilsType = PF_assembly.GetType(PF_TYPE_UTILS);
                    if (PF_UtilsType == null)
                        throw new Exception("Animal Tab type not found.");

                    _hasMainTabWindow_Animals = PF_UtilsType.GetMethod(PF_METHOD_TABWINDOW, BINDINGDLAGS_ALL);
                    if (_hasMainTabWindow_Animals == null)
                    {
                        throw new Exception(" method not found: " + PF_METHOD_TABWINDOW);
                    }
                }
                catch
                {
                    _anyError = true;
                    Log.Error("Better Pawn Control :: Error in Animal Tab integration - functionality disabled");
                    throw;
                }
            }
        }

        public static void DoWindowContents(Rect fillRect)
        {
            _hasMainTabWindow_Animals.Invoke(<fluffy's class instance here>, new object[] { fillRect });
        }
    }
}


On the output_log

Exception filling window for BetterPawnControl.MainTabWindow_Animals_Policies: System.Reflection.TargetException: Non-static method requires a target.
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <filename unknown>:0
  at BetterPawnControl.Widgets_AnimalTab.DoWindowContents (Rect fillRect) [0x00000] in <filename unknown>:0
  at BetterPawnControl.MainTabWindow_Animals_Policies.DoWindowContents (Rect fillRect) [0x00000] in <filename unknown>:0
  at Verse.Window+<WindowOnGUI>c__AnonStorey25B.<>m__183 (Int32 x) [0x00000] in <filename unknown>:0


If instead of <fluffy's class instance here> I put null, i get the error above.
The target should be an instance of Fluffy's MainTabWindow_Animals class. How can I get it? It is allowed?


RawCode

try using keyword "new", in case of latebind Activator.NewInstance

VouLT

Thanks RawCode! That helped!!

The last line became:

_hasMainTabWindow_Animals.Invoke(animalTabInstance, new object[] { fillRect });


where

      private static object animalTabInstance;
....
                    animalTabInstance = Activator.CreateInstance(PF_UtilsType);
                    if (animalTabInstance == null)
                    {
                        throw new Exception("AnimalTab instance not found: " + PF_METHOD_TABWINDOW);
                    }


This code integrated Fluffy's mod but I still have one issue.
When the saved game loads, the Animal Tab on the first render is messed up. Only the top Fluffy's mod buttons appear (plus my mod buttons).  Closing and opening the Animal tab fixes the bad render. However, when I press the Filter button, the new window flashes but does no stay open. Logs don't show anything.

This is what happens after game load, and after the very first Animal tab window open
http://i.imgur.com/z0wLfGo.jpg

After closing and opening the Animal Tab
http://i.imgur.com/Foss6Bz.png

The pawns will also only appear after I press one of Fluffy's mod buttons at least once.

I will probably need to contact fluffy to tackle this one but, anyone has any idea?



RawCode

add Log.Warning messages into each section of your code, include section name and section related variables into output.

compare output from loaded and fresh games, this may allow you to detect issue, if all fields inside your code are OK in both cases - yep, you will need to dig 3rd party classes.

VouLT

After many hours I've concluded it is not possible to integrate it. Both mods implement the same vanilla class and although I'm able to use Reflection to instance Fluffy class, the next piece of code kills me.


namespace Fluffy
{
    public class Dialog_FilterAnimals : Window
    ...
            public override void DoWindowContents( Rect inRect )           
            // Close if animals tab closed
            if ( Find.WindowStack.WindowOfType<MainTabWindow_Animals>() == null )
            {
                Find.WindowStack.TryRemove( this );
            }


This code closes the dialog window if Fluffy.MainTabWindow_Animals class isn't present.
If I force this instance to the window stack then I break the vanilla window manager.

There is another issue where Fluffy's code overwrites the Window Size and window scroll and I haven't found a way to integrate both.

Well, I think this is the end of the road but I think it was fun anyway :-) Learned a lot!
I reckon the only way to fully integrate this would require changes on Fluffy mod which I is exactly what I want to avoid.

Thanks for the help

RawCode

well, as long as you keep "no code modification" rule, you will not get viable results in reasonable time.

you should not waste time trying to overcome barriers in "specific" manner, just do what is required without any restrictions.

in your case proper way to implement things is:
copy fluffy code
modify it
include into your mod
everything that done via overrides and super classes should be implemented via hooks\detours\injections\name_as_you_like

well, you are done, just add credit to original author of code you "burrowed"

VouLT

I understand what you are saying but that's a maintenance nightmare. Imagine when Fluffy updates his mod. So, I'll stand with my "no code modification" rule and state my mod is not compatible.

I appreciate your reply and all the help provided. Thank you!