// Validate ModHelperDefs, CCL load order, CCL versioning
        public override bool Validate()
        {
            // Hopefully...
            var stringBuilder = new StringBuilder();
            var rVal          = true;

            CCL_Log.CaptureBegin(stringBuilder);

            // Limit one ModHelperDef per mod
            // Create the ordered list by inserting dummies for mods which don't have one
            var allMods = LoadedModManager.RunningMods.ToList();

            // Find Core and CCL in the mod order
            coreModIndex = -1;
            cclModIndex  = -1;
            for (int i = 0; i < allMods.Count; ++i)
            {
                ModContentPack mod = allMods[i];
                if (mod.Identifier == ModContentPack.CoreModIdentifier)
                {
                    coreModIndex = i;
                }
                if (mod.Name == Controller.Data.UnityObjectName)
                {
                    Controller.Data.cclModIdentifier = mod.Identifier;
                    cclModIndex = i;
                }
            }
            if (coreModIndex == -1)
            {
                LongEventHandler.ExecuteWhenFinished(ShowLoadOrderWindow);
                CCL_Log.Error(string.Format("Unable to find '{0}' in mod load order!", ModContentPack.CoreModIdentifier));
                //rVal = false; // Don't throw as an error, will be caught special
            }
            else if (coreModIndex != 0)
            {
                LongEventHandler.ExecuteWhenFinished(ShowLoadOrderWindow);
                CCL_Log.Error(string.Format("'{0}' must be first in mod load order!", ModContentPack.CoreModIdentifier));
                //rVal = false; // Don't throw as an error, will be caught special
            }
            if (cclModIndex == -1)
            {
                LongEventHandler.ExecuteWhenFinished(ShowLoadOrderWindow);
                CCL_Log.Error(string.Format("Unable to find '{0}' in mod load order!", Controller.Data.UnityObjectName));
                //rVal = false; // Don't throw as an error, will be caught special
            }
            else if (cclModIndex != 1)
            {
                LongEventHandler.ExecuteWhenFinished(ShowLoadOrderWindow);
                CCL_Log.Error(string.Format("'{0}' must be second in mod load order, immediately after '{1}'! :: Current position is #{2}", Controller.Data.UnityObjectName, ModContentPack.CoreModIdentifier, (cclModIndex + 1).ToString()));
                //rVal = false; // Don't throw as an error, will be caught special
            }
            else if (Controller.Data.cclModIdentifier == string.Empty)
            {
                LongEventHandler.ExecuteWhenFinished(ShowLoadOrderWindow);
                CCL_Log.Error(string.Format("Unable to identify '{0}' in mod load order!", Controller.Data.UnityObjectName));
                //rVal = false; // Don't throw as an error, will be caught special
            }
            if (rVal)
            {
                for (int i = 0; i < allMods.Count; i++)
                {
                    var modHelperDef  = (ModHelperDef)null;
                    var mod           = allMods[i];
                    var modHelperDefs = Find_Extensions.DefListOfTypeForMod <ModHelperDef>(mod);
                    if (!modHelperDefs.NullOrEmpty())
                    {
                        if (modHelperDefs.Count > 1)
                        {
                            CCL_Log.Error(string.Format("'{0}' has multiple ModHelperDefs!", mod.Name));
                            rVal = false;
                        }
                        else
                        {
                            // Validate the def
                            modHelperDef = modHelperDefs.First();
                            if (!modHelperDef.IsValid)
                            {
                                // Don't do anything special with broken mods
                                CCL_Log.Error(string.Format("ModHelperDef for '{0}' is invalid!", mod.Name));
                                rVal = false;
                            }
                            else if (!modHelperDef.dummy)
                            {
                                // Don't show validation message for dummy defs
                                CCL_Log.Message(string.Format("{0} :: Passed validation, requesting v{1}", mod.Name, modHelperDef.minCCLVersion), "ModHelperDef");
                            }
                        }
                    }
                    else if (rVal == true)
                    {
                        // Doesn't exist, create a dummy for logging but only
                        // create if we're not just checking for remaining errors
                        modHelperDef               = new ModHelperDef();
                        modHelperDef.defName       = mod.Name + "_ModHelperDef";
                        modHelperDef.minCCLVersion = Version.Minimum.ToString();
                        modHelperDef.ModName       = mod.Name;
                        modHelperDef.Verbosity     = Verbosity.NonFatalErrors;
                        modHelperDef.dummy         = true;
                    }
                    if (rVal == true)
                    {
                        // No errors, def is valid or a dummy
                        // Associate the def with the mod (the dictionary is to go the other way)
                        modHelperDef.mod = mod;
                        // Insert it into it's ordered place in the lists
                        Controller.Data.Mods.Insert(i, mod);
                        Controller.Data.ModHelperDefs.Insert(i, modHelperDef);
                        // Add a dictionary entry
                        Controller.Data.DictModHelperDefs.Add(mod, modHelperDef);
                    }
                }
                // Should now be a complete pair of lists in mod load order
                // as well as a dictionary of mods and their defs

#if DEVELOPER
                //Dump ordered list of mods and their defs
                string dump = "Mod load order:\n";
                for (int i = 0; i < Controller.Data.Mods.Count; i++)
                {
                    dump += string.Format("\t[{0} - {1}] - {2} - {3}{4}\n", i, Controller.Data.Mods[i].Identifier, Controller.Data.Mods[i].Name, Controller.Data.ModHelperDefs[i].defName, (Controller.Data.ModHelperDefs[i].dummy ? " - dummy" : ""));
                }
                CCL_Log.Write(dump);
#endif
                if (rVal)
                {
                    ModContentPack CCL_Mod       = Controller.Data.Mods[cclModIndex];
                    ModHelperDef   CCL_HelperDef = Find_Extensions.ModHelperDefForMod(CCL_Mod);

                    // Validate xml version with assembly version
                    var vc = Version.Compare(CCL_HelperDef.minCCLVersion);
                    if (vc != Version.VersionCompare.ExactMatch)
                    {
                        CCL_Log.Error(string.Format("Version mismatch for {0}!", Controller.Data.UnityObjectName), "ModHelperDef");
                        rVal = false;
                    }

                    // CCL rank is #2 in load order and def version matches library
                    Controller.Data.cclMod       = CCL_Mod;
                    Controller.Data.cclHelperDef = CCL_HelperDef;
                }
            }

            // Should be all good or up until the first error encountered
            CCL_Log.CaptureEnd(
                stringBuilder,
                rVal ? "Validated" : "Errors during validation"
                );
            strReturn = stringBuilder.ToString();
            // Return true if all mods OK, false if any failed validation
            State = rVal ? SubControllerState.Validated : SubControllerState.ValidationError;
            return(rVal);
        }
Exemple #2
0
        private static void                 PreLoad()
        {
            // This is a pre-start sequence to hook some deeper level functions.
            // These functions can be hooked later but it would be after the sequence
            // of operations which call them is complete.

            // Log CCL version
            Version.Log();

            bool          InjectionsOk  = true;
            StringBuilder stringBuilder = new StringBuilder();

            CCL_Log.CaptureBegin(stringBuilder);

            // Find all sub-controllers
            var subControllerClasses = typeof(SubController).AllSubclasses();
            var subControllerCount   = subControllerClasses.Count();

            if (subControllerCount == 0)
            {
                InjectionsOk = false;
                CCL_Log.Error(
                    "Unable to find sub-controllers",
                    "PreLoader"
                    );
            }

            // Create sub-controllers
            if (InjectionsOk)
            {
                var subControllers = new SubController[subControllerCount];
                for (int index = 0; index < subControllerCount; ++index)
                {
                    var subControllerType = subControllerClasses.ElementAt(index);
                    var subController     = (SubController)Activator.CreateInstance(subControllerType);
                    if (subController == null)
                    {
                        CCL_Log.Error(
                            string.Format("Unable to create sub-controller {0}", subControllerType.Name),
                            "PreLoader"
                            );
                        InjectionsOk = false;
                        break;
                    }
                    else
                    {
                        subControllers[index] = subController;
                    }
                }
                if (InjectionsOk)
                {
                    Controller.Data.SubControllers = subControllers;
                }
            }

            // Detour Verse.PlayDataLoader.LoadAllPlayData
            if (InjectionsOk)
            {
                MethodInfo Verse_PlayDataLoader_LoadAllPlayData = typeof(PlayDataLoader).GetMethod("LoadAllPlayData", BindingFlags.Static | BindingFlags.Public);
                MethodInfo CCL_PlayDataLoader_LoadAllPlayData   = typeof(Detour._PlayDataLoader).GetMethod("_LoadAllPlayData", BindingFlags.Static | BindingFlags.NonPublic);
                InjectionsOk &= Detours.TryDetourFromTo(Verse_PlayDataLoader_LoadAllPlayData, CCL_PlayDataLoader_LoadAllPlayData);
            }

            // Detour Verse.PlayDataLoader.ClearAllPlayData
            if (InjectionsOk)
            {
                MethodInfo Verse_PlayDataLoader_ClearAllPlayData = typeof(PlayDataLoader).GetMethod("ClearAllPlayData", BindingFlags.Static | BindingFlags.Public);
                MethodInfo CCL_PlayDataLoader_ClearAllPlayData   = typeof(Detour._PlayDataLoader).GetMethod("_ClearAllPlayData", BindingFlags.Static | BindingFlags.NonPublic);
                InjectionsOk &= Detours.TryDetourFromTo(Verse_PlayDataLoader_ClearAllPlayData, CCL_PlayDataLoader_ClearAllPlayData);
            }

            // Detour Verse.UIRoot_Entry.ShouldShowMainMenuGUI_get
            if (InjectionsOk)
            {
                PropertyInfo Verse_UIRoot_Entry_ShouldShowMainMenuGUI     = typeof(UIRoot_Entry).GetProperty("ShouldShowMainMenuGUI", BindingFlags.Instance | BindingFlags.NonPublic);
                MethodInfo   Verse_UIRoot_Entry_ShouldShowMainMenuGUI_get = Verse_UIRoot_Entry_ShouldShowMainMenuGUI.GetGetMethod(true);
                MethodInfo   CCL_UIRoot_Entry_ShouldShowMainMenuGUI_get   = typeof(Detour._UIRoot_Entry).GetMethod("_ShouldShowMainMenuGUI_get", BindingFlags.Static | BindingFlags.NonPublic);
                InjectionsOk &= Detours.TryDetourFromTo(Verse_UIRoot_Entry_ShouldShowMainMenuGUI_get, CCL_UIRoot_Entry_ShouldShowMainMenuGUI_get);
            }

            // Detour RimWorld.MainMenuDrawer.MainMenuOnGUI
            if (InjectionsOk)
            {
                MethodInfo RimWorld_MainMenuDrawer_MainMenuOnGUI = typeof(MainMenuDrawer).GetMethod("MainMenuOnGUI", BindingFlags.Static | BindingFlags.Public);
                MethodInfo CCL_MainMenuDrawer_MainMenuOnGUI      = typeof(Detour._MainMenuDrawer).GetMethod("_MainMenuOnGUI", BindingFlags.Static | BindingFlags.NonPublic);
                InjectionsOk &= Detours.TryDetourFromTo(RimWorld_MainMenuDrawer_MainMenuOnGUI, CCL_MainMenuDrawer_MainMenuOnGUI);
            }

            // Detour RimWorld.MainMenuDrawer.DoMainMenuButtons
            if (InjectionsOk)
            {
                MethodInfo RimWorld_MainMenuDrawer_DoMainMenuButtons = typeof(MainMenuDrawer).GetMethod("DoMainMenuButtons", BindingFlags.Static | BindingFlags.Public);
                MethodInfo CCL_MainMenuDrawer_DoMainMenuButtons      = typeof(Detour._MainMenuDrawer).GetMethod("_DoMainMenuButtons", BindingFlags.Static | BindingFlags.NonPublic);
                InjectionsOk &= Detours.TryDetourFromTo(RimWorld_MainMenuDrawer_DoMainMenuButtons, CCL_MainMenuDrawer_DoMainMenuButtons);
            }

            // Detour RimWorld.BiomeDef.CommonalityOfAnimal
            if (InjectionsOk)
            {
                MethodInfo RimwWorld_BiomeDef_CommonalityOfAnimal = typeof(BiomeDef).GetMethod("CommonalityOfAnimal", BindingFlags.Instance | BindingFlags.Public);
                MethodInfo CCL_BiomeDef_CommonalityOfAnimal       = typeof(Detour._BiomeDef).GetMethod("_CommonalityOfAnimal", BindingFlags.Static | BindingFlags.NonPublic);
                InjectionsOk &= Detours.TryDetourFromTo(RimwWorld_BiomeDef_CommonalityOfAnimal, CCL_BiomeDef_CommonalityOfAnimal);
            }

            // Detour RimWorld.BiomeDef.CommonalityOfPlant
            if (InjectionsOk)
            {
                MethodInfo RimwWorld_BiomeDef_CommonalityOfPlant = typeof(BiomeDef).GetMethod("CommonalityOfPlant", BindingFlags.Instance | BindingFlags.Public);
                MethodInfo CCL_BiomeDef_CommonalityOfPlant       = typeof(Detour._BiomeDef).GetMethod("_CommonalityOfPlant", BindingFlags.Static | BindingFlags.NonPublic);
                InjectionsOk &= Detours.TryDetourFromTo(RimwWorld_BiomeDef_CommonalityOfPlant, CCL_BiomeDef_CommonalityOfPlant);
            }
            // Detour RimWorld.BiomeDef.MTBDaysOfDisease
            if (InjectionsOk)
            {
                MethodInfo RimWorld_BiomeDef_MTBDaysOfDisease = typeof(BiomeDef).GetMethod("MTBDaysOfDisease", BindingFlags.Instance | BindingFlags.Public);
                MethodInfo CCL_BiomeDef_MTBDaysOfDisease      = typeof(Detour._BiomeDef).GetMethod("_MTBDaysOfDisease", BindingFlags.Static | BindingFlags.NonPublic);
                InjectionsOk &= Detours.TryDetourFromTo(RimWorld_BiomeDef_MTBDaysOfDisease, CCL_BiomeDef_MTBDaysOfDisease);
            }

            // Detour Verse.PostLoadInitter.DoAllPostLoadInits

            /*
             * if( InjectionsOk )
             * {
             *  MethodInfo Verse_PostLoadInitter_DoAllPostLoadInits = typeof( PostLoadInitter ).GetMethod( "DoAllPostLoadInits", BindingFlags.Static | BindingFlags.Public );
             *  MethodInfo CCL_PostLoadInitter_DoAllPostLoadInits = typeof( Detour._PostLoadInitter ).GetMethod( "_DoAllPostLoadInits", BindingFlags.Static | BindingFlags.NonPublic );
             *  InjectionsOk &= Detours.TryDetourFromTo( Verse_PostLoadInitter_DoAllPostLoadInits, CCL_PostLoadInitter_DoAllPostLoadInits );
             * }
             */

            if (InjectionsOk)
            {
                var gameObject = new GameObject(Controller.Data.UnityObjectName);
                if (gameObject == null)
                {
                    InjectionsOk = false;
                    CCL_Log.Error(
                        "Unable to create GameObject",
                        "PreLoader"
                        );
                }
                else
                {
                    if (gameObject.AddComponent <Controller.MainMonoBehaviour>() == null)
                    {
                        InjectionsOk = false;
                        CCL_Log.Error(
                            "Unable to create MonoBehaviour",
                            "PreLoader"
                            );
                    }
                    else
                    {
                        UnityEngine.Object.DontDestroyOnLoad(gameObject);
                        Controller.Data.UnityObject = gameObject;
                    }
                }
            }

            if (InjectionsOk)
            {
                CCL_Log.Message(
                    "Queueing Library Initialization",
                    "PreLoader"
                    );
                LongEventHandler.QueueLongEvent(Initialize, "LibraryStartup", true, null);
            }

            CCL_Log.CaptureEnd(
                stringBuilder,
                InjectionsOk ? "Initialized" : "Errors during injection"
                );
            CCL_Log.Trace(
                Verbosity.Injections,
                stringBuilder.ToString(),
                "PreLoader");
        }
        // Validate ...research...?
        public override bool                Validate()
        {
            // Hopefully...
            var stringBuilder = new StringBuilder();
            var rVal          = true;

            CCL_Log.CaptureBegin(stringBuilder);

            var AdvancedResearchDefs = Controller.Data.AdvancedResearchDefs;

            // Make sure the hidden research exists
            if (CommunityCoreLibrary.Research.Locker == null)
            {
                CCL_Log.Trace(Verbosity.FatalErrors, "Missing research locker!");
                rVal = false;
            }

            // Validate each advanced research def
            for (int index = AdvancedResearchDefs.Count - 1; index >= 0; index--)
            {
                var advancedResearchDef = AdvancedResearchDefs[index];

                if (!advancedResearchDef.IsValid())
                {
                    // Remove projects with errors from list of usable projects
                    AdvancedResearchDefs.Remove(advancedResearchDef);
                    rVal = false;
                    continue;
                }

                if (advancedResearchDef.IsLockedOut())
                {
                    // Remove locked out projects
                    CCL_Log.TraceMod(
                        advancedResearchDef,
                        Verbosity.Warnings,
                        "Def is locked out by one or more research prerequisites");
                    AdvancedResearchDefs.Remove(advancedResearchDef);
                    continue;
                }
            }

#if DEBUG
            if (rVal == true)
            {
                var allMods = Controller.Data.Mods;
                foreach (var mod in allMods)
                {
                    if (!Find_Extensions.DefListOfTypeForMod <AdvancedResearchDef>(mod).NullOrEmpty())
                    {
                        CCL_Log.TraceMod(
                            mod,
                            Verbosity.Validation,
                            "Passed validation"
                            );
                    }
                }
            }
#endif

            // Should be empty or a laundry list
            CCL_Log.CaptureEnd(
                stringBuilder,
                rVal ? "Validated" : "Errors during validation"
                );
            strReturn = stringBuilder.ToString();

            // Return true if all mods OK, false if any failed validation
            State = rVal ? SubControllerState.Validated : SubControllerState.ValidationError;
            return(rVal);
        }