private void Awake() { Debug.Log("Starting awake now..."); @this2 = this; //We set our patcher so we can call it back and patch dynamically as needed. harmony = Harmony.CreateAndPatchAll(typeof(Main)); MethodBase menuItemSetConstructor = typeof(SceneEdit) .GetNestedType("MenuItemSet", BindingFlags.NonPublic) .GetConstructor( new Type[] { typeof(GameObject), typeof(SceneEdit.SMenuItem), typeof(string), typeof(bool) }); MethodBase colorItemSetConstructor = typeof(SceneEdit).GetNestedType("ColorItemSet", BindingFlags.NonPublic).GetConstructor(new Type[] { typeof(GameObject), typeof(SceneEdit.SMenuItem) }); harmony.PatchAll(typeof(QuickEdit)); harmony.Patch(menuItemSetConstructor, new HarmonyMethod(typeof(QuickEdit), "MenuItemSet")); harmony.Patch(colorItemSetConstructor, new HarmonyMethod(typeof(QuickEdit), "MenuItemSet")); @this2.StartCoroutine(GSModMenuLoad.LoadCache()); UseVanillaCache = Config.Bind("General", "Use Vanilla Cache", false, "This decides whether a vanilla cache is created, maintained and used on load. Kiss has it's own questionable implementation of a cache, but this cache is questionable in it's own right too."); ChangeModPriority = Config.Bind("General", "Add 10,000 to Mod Item Priority", false, "This option simply adds 10,000 priority to all mod items loaded. Handy if you don't want mod items mix and matching with vanilla stuff or appearing before the remove button."); @this2.StartCoroutine(VanillaMenuLoad.LoadCache()); }
public static bool GetMenuItemSetUP(SceneEdit.SMenuItem mi, string f_strMenuFileName, out string IconTex) { IconTex = null; if (f_strMenuFileName.Contains("_zurashi")) { return(false); } if (f_strMenuFileName.Contains("_mekure")) { return(false); } f_strMenuFileName = Path.GetFileName(f_strMenuFileName); mi.m_strMenuFileName = f_strMenuFileName; mi.m_nMenuFileRID = f_strMenuFileName.ToLower().GetHashCode(); try { if (!GSModMenuLoad.InitMenuItemScript(mi, f_strMenuFileName, out IconTex)) { Main.logger.LogError("(メニュースクリプトが読めませんでした。) The following menu file could not be read and will be skipped: " + f_strMenuFileName); } } catch (Exception ex) { Main.logger.LogError(string.Concat(new string[] { "GetMenuItemSetUP tossed an exception while reading: ", f_strMenuFileName, "\n\n", ex.Message, "\n", ex.StackTrace })); return(false); } return(true); }
private void Awake() { logger = this.Logger; Main.logger.LogDebug("Starting awake now..."); @this2 = this; //We set our patcher so we can call it back and patch dynamically as needed. harmony = Harmony.CreateAndPatchAll(typeof(Main)); MethodBase menuItemSetConstructor = typeof(SceneEdit) .GetNestedType("MenuItemSet", BindingFlags.NonPublic) .GetConstructor( new Type[] { typeof(GameObject), typeof(SceneEdit.SMenuItem), typeof(string), typeof(bool) }); MethodBase colorItemSetConstructor = typeof(SceneEdit).GetNestedType("ColorItemSet", BindingFlags.NonPublic).GetConstructor(new Type[] { typeof(GameObject), typeof(SceneEdit.SMenuItem) }); harmony.PatchAll(typeof(QuickEdit)); harmony.Patch(menuItemSetConstructor, new HarmonyMethod(typeof(QuickEdit), "MenuItemSet")); harmony.Patch(colorItemSetConstructor, new HarmonyMethod(typeof(QuickEdit), "MenuItemSet")); @this2.StartCoroutine(GSModMenuLoad.LoadCache()); BreakInterval = Config.Bind("General", "Time Between Breaks in Seconds", 0.5f, "The break interval is the time between each break that the co-routine takes where it returns processing back to the main thread. After one frame, processing is given back to the co-routine. Higher values can help with low-end processing times but can cause instability if set too high. If after all of this you're still confused, leave it alone."); UseVanillaCache = Config.Bind("General", "Use Vanilla Cache", false, "This decides whether a vanilla cache is created, maintained and used on load. Kiss has it's own questionable implementation of a cache, but this cache is questionable in it's own right too."); ChangeModPriority = Config.Bind("General", "Add 10,000 to Mod Item Priority", false, "This option simply adds 10,000 priority to all mod items loaded. Handy if you don't want mod items mix and matching with vanilla stuff or appearing before the remove button."); @this2.StartCoroutine(VanillaMenuLoad.LoadCache()); }
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 bool GetMenuItemSetUP(SceneEdit.SMenuItem mi, string f_strMenuFileName, out string IconTex) { IconTex = null; if (f_strMenuFileName.Contains("_zurashi")) { return(false); } if (f_strMenuFileName.Contains("_mekure")) { return(false); } f_strMenuFileName = Path.GetFileName(f_strMenuFileName); mi.m_strMenuFileName = f_strMenuFileName; mi.m_nMenuFileRID = f_strMenuFileName.ToLower().GetHashCode(); try { if (!GSModMenuLoad.InitMenuItemScript(mi, f_strMenuFileName, out IconTex)) { NDebug.Assert(false, "メニュースクリプトが読めませんでした。" + f_strMenuFileName); } } catch (Exception ex) { Debug.LogError(string.Concat(new string[] { "GetMenuItemSetUP 例外/", f_strMenuFileName, "/", ex.Message, " StackTrace/", ex.StackTrace })); return(false); } return(true); }
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); } } }