private static IEnumerator InitMenuNative()
        {
            List <SceneEdit.SMenuItem>    menuList           = new List <SceneEdit.SMenuItem>();
            Dictionary <int, List <int> > menuGroupMemberDic = new Dictionary <int, List <int> >();

            @this.m_menuRidDic = new Dictionary <int, SceneEdit.SMenuItem>();

            //We set time so the coroutines to be called can coordinate themselves.
            time = Time.realtimeSinceStartup;

            AccessTools.Method(typeof(SceneEdit), "InitCategoryList").Invoke(Main.@this, null);

            Main.logger.LogInfo($"Files began loading at: {WatchOverall.Elapsed}");

            //These coroutines hold the loading code for each type of item related file.
            this2.StartCoroutine(GSModMenuLoad.GSMenuLoadStart(menuList, menuGroupMemberDic));
            this2.StartCoroutine(ModMenuLoad.ModMenuLoadStart(menuList, menuGroupMemberDic));
            this2.StartCoroutine(VanillaMenuLoad.VanillaMenuLoadStart(menuList, menuGroupMemberDic));

            //In a sorta async fashion, while the threads are no complete, the coroutine will pass on processing to someone else.
            while (ThreadsDone != 3)
            {
                yield return(null);
            }

            Main.logger.LogInfo($"All loaders finished at: {WatchOverall.Elapsed}.");
            //Setting threads back to 0 for next loads.
            ThreadsDone = 0;

            //Calls the final function to complete setting up menu items.
            yield return(@this.StartCoroutine(Main.FixedInitMenu(menuList, @this.m_menuRidDic, menuGroupMemberDic)));

            //Does something...
            yield return(@this.StartCoroutine(AccessTools.Method(typeof(SceneEdit), "CoLoadWait").Invoke(@this, null) as IEnumerator));

            Main.logger.LogInfo($"Loading completely done at: {WatchOverall.Elapsed}.");
        }
Пример #2
0
        public static IEnumerator GSMenuLoadStart(List <SceneEdit.SMenuItem> menuList, Dictionary <int, List <int> > menuGroupMemberDic)
        {
            HashSet <SceneEdit.SMenuItem> listOfLoads = new HashSet <SceneEdit.SMenuItem>();
            List <string> listOfDuplicates            = new List <string>();

            string path = BepInEx.Paths.GameRootPath;

            FilesToRead.Clear();
            FilesDictionary.Clear();

            DictionaryBuilt = false;

            Main.logger.LogDebug("Started worker...");

            Task loaderWorker = Task.Factory.StartNew(new Action(() =>
            {
                foreach (string s in Directory.GetFiles(path + "\\Mod", "*.menu", SearchOption.AllDirectories))
                {
                    if (!FilesDictionary.ContainsKey(Path.GetFileName(s).ToLower()))
                    {
                        FilesDictionary[Path.GetFileName(s).ToLower()] = s;
                    }
                    else
                    {
                        listOfDuplicates.Add(s);
                    }
                }

                Main.logger.LogDebug("Worker done fetching files...");

                DictionaryBuilt = true;

                while (CacheLoadDone != true)
                {
                    Thread.Sleep(1);
                }

                Mutex dicLock = new Mutex();

                Main.logger.LogDebug("Worker started loading menus into memory...");

                Task servant = Task.Factory.StartNew(new Action(() =>
                {
                    foreach (string s in FilesDictionary.Keys)
                    {
                        try
                        {
                            if (MenuCache.ContainsKey(s.ToLower()))
                            {
                                dicLock.WaitOne();
                                FilesToRead[s.ToLower()] = null;
                                dicLock.ReleaseMutex();
                            }
                            else
                            {
                                dicLock.WaitOne();
                                FilesToRead[s.ToLower()] = new MemoryStream(File.ReadAllBytes(FilesDictionary[s]));
                                dicLock.ReleaseMutex();
                            }
                        }
                        catch
                        {
                            dicLock.WaitOne();
                            FilesToRead[s.ToLower()] = null;
                            dicLock.ReleaseMutex();
                        }
                    }
                }));

                Main.logger.LogDebug("Menu load worker started operating...");

                while (servant.IsCompleted == false || FilesToRead.Count > 0)
                {
                    if (FilesToRead.Count == 0)
                    {
                        Thread.Sleep(1);
                        continue;
                    }

                    dicLock.WaitOne();
                    string strFileName = FilesToRead.FirstOrDefault().Key;
                    dicLock.ReleaseMutex();

                    SceneEdit.SMenuItem mi2 = new SceneEdit.SMenuItem();
                    //SceneEdit.GetMenuItemSetUP causes crash if parallel threaded. Our implementation is thread safe-ish.
                    if (GSModMenuLoad.GetMenuItemSetUP(mi2, strFileName, out string iconLoad))
                    {
                        if (iconLoad != null)
                        {
                            listOfLoads.Add(mi2);
                            QuickEdit.idItemDic[mi2.m_nMenuFileRID]    = mi2;
                            QuickEdit.texFileIDDic[mi2.m_nMenuFileRID] = iconLoad;
                            mi2.m_texIcon = Texture2D.whiteTexture;
                        }
                    }
                    dicLock.WaitOne();
                    FilesToRead.Remove(strFileName);
                    dicLock.ReleaseMutex();
                }

                if (servant.IsFaulted)
                {
                    Main.logger.LogError($"Servant task failed due to an unexpected error!");

                    throw servant.Exception;
                }
            }));

            //We wait until the manager is not busy because starting work while the manager is busy causes egregious bugs.
            while (!loaderWorker.IsCompleted || GameMain.Instance.CharacterMgr.IsBusy())
            {
                yield return(null);
            }

            if (loaderWorker.IsFaulted)
            {
                Main.logger.LogError($"Worker task failed due to an unexpected error! This is considered a full failure: {loaderWorker.Exception.InnerException.Message}\n{loaderWorker.Exception.InnerException.StackTrace}\n\nwe will try restarting the load task...");

                yield return(new WaitForSecondsRealtime(2));

                [email protected](GSModMenuLoad.GSMenuLoadStart(menuList, menuGroupMemberDic));

                yield break;
            }

            Main.logger.LogDebug("Worker finished! Now continuing foreach...");

            foreach (SceneEdit.SMenuItem mi2 in listOfLoads)
            {
                try
                {
                    if (Main.ChangeModPriority.Value)
                    {
                        if (mi2.m_fPriority <= 0)
                        {
                            mi2.m_fPriority = 1f;
                        }

                        mi2.m_fPriority += 10000;
                    }

                    if (!mi2.m_bMan)
                    {
                        AccessTools.Method(typeof(SceneEdit), "AddMenuItemToList").Invoke(Main.@this, new object[] { mi2 });
                        menuList.Add(mi2);
                        [email protected]_menuRidDic[mi2.m_nMenuFileRID] = mi2;
                        string parentMenuName2 = AccessTools.Method(typeof(SceneEdit), "GetParentMenuFileName").Invoke(Main.@this, new object[] { mi2 }) as string;
                        if (!string.IsNullOrEmpty(parentMenuName2))
                        {
                            int hashCode2 = parentMenuName2.GetHashCode();
                            if (!menuGroupMemberDic.ContainsKey(hashCode2))
                            {
                                menuGroupMemberDic.Add(hashCode2, new List <int>());
                            }
                            menuGroupMemberDic[hashCode2].Add(mi2.m_strMenuFileName.ToLower().GetHashCode());
                        }
                        else if (mi2.m_strCateName.IndexOf("set_") != -1 && mi2.m_strMenuFileName.IndexOf("_del") == -1)
                        {
                            mi2.m_bGroupLeader = true;
                            mi2.m_listMember   = new List <SceneEdit.SMenuItem>
                            {
                                mi2
                            };
                        }
                    }
                }
                catch (Exception e)
                {
                    Main.logger.LogError($"We caught the following exception while processing {mi2.m_strMenuFileName}:\n {e.StackTrace}");
                }
                if (Main.BreakInterval.Value < Time.realtimeSinceStartup - Main.time)
                {
                    yield return(null);

                    Main.time = Time.realtimeSinceStartup;
                }
            }

            Main.ThreadsDone++;
            Main.logger.LogInfo($"Standard mods finished loading at: {Main.WatchOverall.Elapsed}");

            [email protected](SaveCache());


            if (listOfDuplicates.Count > 0)
            {
                Main.logger.LogWarning($"There are {listOfDuplicates.Count} duplicate menus in your mod folder!");

                foreach (string s in listOfDuplicates)
                {
                    Main.logger.LogWarning("We found a duplicate that should be corrected immediately in your mod folder at: " + s);
                }
            }
        }