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}."); }
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); } } }