Browse Source

Fix BG2 props and add BG2 prop caching

On version 1.48, if the MenuDataBase is not finished doing its job,
MultipleMaids has to wait for it before it can start populating the BG2
props. Waiting is asynchronous so users can still use MultipleMaids
while it waits for the job to finish. Versions below 1.48 do not have
this problem.

A new folder will be added to config called 'MultipleMaids' and it will
contain a file called 'cache.json' when the user interacts with the BG2
accessory prop dropdown and selects a slot that is below
'My Room Custom'
habeebweeb 3 years ago
parent
commit
f3fd9ebe4d

+ 13 - 98
MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.Gui.cs

@@ -664,7 +664,7 @@ namespace CM3D2.MultipleMaids.Plugin
             if (!modItemsOnly)
             {
                 bool previousGUIState = GUI.enabled;
-                GUI.enabled = slotIndex > 1;
+                GUI.enabled = slotIndex > 1 || !bg2Busy;
                 modItemsToggle = GUI.Toggle(new Rect(GetPix(156),
                     GetPix(115),
                     GetPix(50),
@@ -676,7 +676,12 @@ namespace CM3D2.MultipleMaids.Plugin
                 GUI.enabled = previousGUIState;
             }
 
-
+            if (slotIndex > 0)
+            {
+                if (bg2Busy && slotIndex > 1)
+                {
+                    GUI.Label(new Rect(GetPix(15), GetPix(130), GetPix(90), GetPix(25)), "Initializing...");
+                }
 
             int iconSize = GetPix(45);
             Rect position1;
@@ -803,6 +808,8 @@ namespace CM3D2.MultipleMaids.Plugin
             }
 
             GUI.EndScrollView();
+            }
+
             GUI.enabled = true;
             GUI.Label(new Rect(GetPix(3), GetPix(108), GetPix(100), GetPix(25)),
                       "服装",
@@ -823,6 +830,7 @@ namespace CM3D2.MultipleMaids.Plugin
                 GUI.enabled = false;
             }
 
+            GUI.enabled = !bg2Busy;
             int num1 =
                     slotCombo.List(new Rect(GetPix(51),
                                              GetPix(111),
@@ -1132,9 +1140,7 @@ namespace CM3D2.MultipleMaids.Plugin
                 return;
             }
 
-            numberOfModItems = 0;
             slotIndex = num1;
-            sortList.Clear();
             bg2ScrollPos = new Vector2(0.0f, 0.0f);
 
             if (slotIndex == 0)
@@ -1165,107 +1171,16 @@ namespace CM3D2.MultipleMaids.Plugin
             }
             else
             {
-                if (itemDataList.Count == 0)
+                if (!bg2Initialized)
                 {
-                    if (GameUty.MenuFiles?.Length > 0)
-                    {
-                        HashSet<string> modMenus = null;
-                        if (!modItemsOnly) modMenus = new HashSet<string>(GameUty.ModOnlysMenuFiles);
-
-                        string[] menuFiles = modItemsOnly ? GameUty.ModOnlysMenuFiles : GameUty.MenuFiles;
-                        foreach (string menuFile in menuFiles)
-                        {
-                            ItemData item = new ItemData()
-                            {
-                                menu = menuFile,
-                                isMod = modItemsOnly || modMenus.Contains(menuFile)
-                            };
-                            if (ParseMenuFile(menuFile, item)) itemDataList.Add(item);
-                        }
+                    GameMain.Instance.StartCoroutine(InitializeBG2());
                     }
                     else
                     {
-                    if (!modItemsOnly)
-                    {
-                        for (int i = 0; i < GameMain.Instance.MenuDataBase.GetDataSize(); i++)
-                    {
-                            ItemData item = new ItemData();
-                            if (ParseNativeMenuFile(i, item)) itemDataList.Add(item);
-                        }
-                            }
-                    foreach (string modMenuFile in GameUty.ModOnlysMenuFiles)
-                    {
-                        ItemData item = new ItemData()
-                        {
-                            menu = modMenuFile,
-                            isMod = true
-                        };
-                        if (ParseMenuFile(modMenuFile, item)) itemDataList.Add(item);
-                    }
-                    }
-
-                    foreach (string modFile in Menu.GetModFiles())
-                    {
-                        ItemData item = new ItemData()
-                        {
-                            isMod = true,
-                            isOfficialMod = true,
-                            menu = modFile,
-                            priority = 1000
-                        };
-                        if (ParseModMenuFile(modFile, item)) itemDataList.Add(item);
-                        }
-                                }
-
-                foreach (ItemData itemData in itemDataList)
-                {
-                    if (itemData.category == slotArray[slotIndex])
-                    {
-                        sortList.Add(new SortItem()
-                        {
-                            category = itemData.category,
-                            priority = itemData.priority,
-                            name = itemData.name,
-                            icon = itemData.icon,
-                            menu = itemData.menu,
-                            tex = itemData.tex,
-                            isMod = itemData.isMod,
-                            isOfficialMod = itemData.isOfficialMod,
-                            baseMenu = itemData.baseItem
-                        });
+                    SetBG2Props();
                     }
                 }
-
-                IOrderedEnumerable<SortItem> sortedItemList = sortList
-                    .OrderBy(item => item.priority)
-                    .ThenBy(item => item.name);
-
-                List<SortItem> sortItemList = new List<SortItem>();
-
-                string previousMenu = "";
-                foreach (SortItem item in sortedItemList)
-                {
-                    try
-                    {
-                        if (item.menu != previousMenu)
-                        {
-                            if (!modItemsOnly && item.isMod) numberOfModItems++;
-                            if (item.tex == null)
-                            {
-                                byte[] data = ImportCM.LoadTexture(GameUty.FileSystem, item.icon, false).data;
-                                Texture2D texture2D = new Texture2D(50, 50, TextureFormat.RGB565, false);
-                                texture2D.LoadImage(data);
-                                item.tex = texture2D;
                             }
-                            previousMenu = item.menu;
-                            sortItemList.Add(item);
-                        }
-                    }
-                    catch { }
-                }
-                sortList = sortItemList;
-            }
-        }
 
         private void BGSelectWindow(int winID)
         {

+ 1 - 3
MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.Init.cs

@@ -1,4 +1,4 @@
-using ExIni;
+using ExIni;
 using MyRoomCustom;
 using System;
 using System.Collections;
@@ -441,7 +441,6 @@ namespace CM3D2.MultipleMaids.Plugin
             kankyoCombo.selectedItemIndex = 0;
             itemCombo2.selectedItemIndex = 0;
             slotCombo.selectedItemIndex = 0;
-            sortList.Clear();
             maidCallScrollPos = new Vector2(0.0f, 0.0f);
             poseScrollPos = new Vector2(0.0f, 0.0f);
             faceScrollPos = new Vector2(0.0f, 0.0f);
@@ -965,7 +964,6 @@ namespace CM3D2.MultipleMaids.Plugin
             kankyoCombo.selectedItemIndex = 0;
             itemCombo2.selectedItemIndex = 0;
             slotCombo.selectedItemIndex = 0;
-            sortList.Clear();
             maidCallScrollPos = new Vector2(0.0f, 0.0f);
             poseScrollPos = new Vector2(0.0f, 0.0f);
             faceScrollPos = new Vector2(0.0f, 0.0f);

+ 0 - 1
MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.Update.cs

@@ -6248,7 +6248,6 @@ namespace CM3D2.MultipleMaids.Plugin
                         bgCombo2.selectedItemIndex = 0;
                         itemCombo2.selectedItemIndex = 0;
                         slotCombo.selectedItemIndex = 0;
-                        sortList.Clear();
                         maidCallScrollPos = new Vector2(0.0f, 0.0f);
                         poseScrollPos = new Vector2(0.0f, 0.0f);
                         faceScrollPos = new Vector2(0.0f, 0.0f);

+ 277 - 22
MultipleMaids/CM3D2/MultipleMaids/Plugin/MultipleMaids.cs

@@ -3,8 +3,10 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
+using System.Linq;
 using System.Reflection;
 using System.Text;
+using Newtonsoft.Json;
 using UnityEngine;
 using UnityEngine.SceneManagement;
 using UnityInjector;
@@ -5180,6 +5182,19 @@ namespace CM3D2.MultipleMaids.Plugin
         private readonly bool[] zFlg = new bool[maxMaidCnt];
         private readonly bool[] zurasi = new bool[maxMaidCnt];
         private readonly bool[] zurasin = new bool[maxMaidCnt];
+        private static readonly string configPath;
+
+        static MultipleMaids()
+        {
+            configPath = Path.Combine(
+                Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
+                    @"Config\MultipleMaids"
+                );
+            if (!Directory.Exists(configPath))
+            {
+                Directory.CreateDirectory(configPath);
+            }
+        }
 
         public void Awake()
         {
@@ -5577,22 +5592,257 @@ namespace CM3D2.MultipleMaids.Plugin
             }
         }
 
-        private static bool ParseNativeMenuFile(int menuIndex, ItemData itemData)
+        private static readonly HashSet<string> accMpn = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
+        {
+            "accashi", "acchana", "acchat", "acchead", "accheso", "acckami", "acckamisub", "acckubi", "acckubiwa",
+            "accmimi", "accnip", "accsenaka", "accshippo", "accude", "accxxx", "bra", "glove", "headset", "megane",
+            "mizugi", "onepiece", "panz", "shoes", "skirt", "stkg", "wear",
+        };
+
+        private class BG2Cache
+        {
+            private static JsonSerializerSettings serializationSettings;
+            public static readonly string cachePath = Path.Combine(configPath, "cache.json");
+            private static Dictionary<string, SortItem> bg2Props;
+            public int Size => bg2Props.Count();
+            private bool rebuild = false;
+            public SortItem this[string menu]
+            {
+                get => bg2Props[menu];
+                set
+                {
+                    rebuild = true;
+                    bg2Props[menu] = value;
+                }
+            }
+
+            static BG2Cache()
+            {
+                serializationSettings = new JsonSerializerSettings
+                {
+                    NullValueHandling = NullValueHandling.Ignore,
+                    DefaultValueHandling = DefaultValueHandling.Ignore
+                };
+            }
+
+            public BG2Cache()
+            {
+                if (bg2Props != null) return;
+
+                if (!File.Exists(cachePath))
+                {
+                    bg2Props = new Dictionary<string, SortItem>();
+                }
+                else
+                {
+                    string cacheJson = File.ReadAllText(cachePath);
+                    bg2Props = JsonConvert.DeserializeObject<Dictionary<string, SortItem>>(cacheJson);
+                }
+            }
+
+            public bool Has(string menu)
+            {
+                return bg2Props.ContainsKey(menu);
+            }
+
+            public void WriteToFile()
+            {
+                if (!rebuild) return;
+                string cacheJson = JsonConvert.SerializeObject(bg2Props, serializationSettings);
+                File.WriteAllText(cachePath, cacheJson);
+            }
+        }
+
+        private bool bg2Initialized = false;
+        private bool bg2Busy = false;
+        private IEnumerator InitializeBG2()
+        {
+            foreach (string category in accMpn)
+            {
+                BG2Acc[category] = new List<SortItem>();
+                BG2AccInit[category] = false;
+            }
+
+            bg2Initialized = true;
+            bg2Busy = true;
+
+            BG2Cache cache = new BG2Cache();
+
+            if (GameUty.MenuFiles?.Length > 0)
+            {
+                HashSet<string> modMenus = null;
+                if (!modItemsOnly) modMenus = new HashSet<string>(GameUty.ModOnlysMenuFiles);
+
+                string[] menuFiles = modItemsOnly ? GameUty.ModOnlysMenuFiles : GameUty.MenuFiles;
+                foreach (string menuFile in menuFiles)
+                {
+                    SortItem item;
+                    bool add = false;
+                    if (!cache.Has(menuFile))
+                    {
+                        item = new SortItem()
+                        {
+                            menu = menuFile,
+                            isMod = modItemsOnly || modMenus.Contains(menuFile)
+                        };
+                        add = ParseMenuFile(menuFile, item);
+                        cache[menuFile] = item;
+                    }
+                    else
+                    {
+                        item = cache[menuFile];
+                        add = validBG2Menu(item);
+                    }
+                    if (add) BG2Acc[item.category].Add(item);
+                }
+            }
+            else
+            {
+                while (!GameMain.Instance.MenuDataBase.JobFinished()) yield return null;
+
+                if (!modItemsOnly)
+                {
+                    for (int i = 0; i < GameMain.Instance.MenuDataBase.GetDataSize(); i++)
+                    {
+                        SortItem item = new SortItem();
+                        if (ParseNativeMenuFile(i, item)) BG2Acc[item.category].Add(item);
+                    }
+                }
+
+                foreach (string modMenuFile in GameUty.ModOnlysMenuFiles)
+                {
+                    SortItem item;
+                    bool add = false;
+                    if (!cache.Has(modMenuFile))
+                    {
+                        item = new SortItem()
+                        {
+                            menu = modMenuFile,
+                            isMod = true
+                        };
+                        add = ParseMenuFile(modMenuFile, item);
+                        cache[modMenuFile] = item;
+                    }
+                    else
+                    {
+                        item = cache[modMenuFile];
+                        add = validBG2Menu(item);
+                    }
+                    if (add) BG2Acc[item.category].Add(item);
+                }
+            }
+
+            cache.WriteToFile();
+
+            foreach (string modFile in Menu.GetModFiles())
+            {
+                SortItem item = new SortItem()
+                {
+                    menu = modFile,
+                    isMod = true,
+                    isOfficialMod = true,
+                    priority = 1000
+                };
+                if (ParseModMenuFile(modFile, item))
+                {
+                    BG2Acc[item.category].Add(item);
+                }
+            }
+            bg2Busy = false;
+            SetBG2Props();
+        }
+
+        private static void WriteToFile(string name, IEnumerable<string> list)
+        {
+            string modsPath = Path.Combine(Path.GetFullPath(".\\"), @"Mod");
+            if (Path.GetExtension(name) != ".txt") name += ".txt";
+            File.WriteAllLines(Path.Combine(modsPath, name), list.ToArray());
+        }
+
+        private Dictionary<string, List<SortItem>> BG2Acc
+            = new Dictionary<string, List<SortItem>>(StringComparer.InvariantCultureIgnoreCase);
+        private Dictionary<string, bool> BG2AccInit
+            = new Dictionary<string, bool>(StringComparer.InvariantCultureIgnoreCase);
+
+        private void SetBG2Props()
+        {
+            string category = slotArray[slotIndex];
+
+            sortList = BG2Acc[category];
+
+            if (!BG2AccInit[category])
+            {
+                BG2AccInit[category] = true;
+
+                sortList.Sort((x, y) =>
+                {
+                    int res = x.priority.CompareTo(y.priority);
+                    if (res == 0) res = x.name.CompareTo(y.name);
+                    return res;
+                });
+            }
+
+            numberOfModItems = 0;
+
+            string previousMenu = String.Empty;
+            sortList.RemoveAll(item =>
+            {
+                if (item.menu != previousMenu)
+                {
+                    if (!modItemsOnly && item.isMod) numberOfModItems++;
+
+                    if (item.tex == null)
+                    {
+                        Texture2D texture2D;
+                        if (string.IsNullOrEmpty(item.icon) || !GameUty.FileSystem.IsExistentFile(item.icon))
+                        {
+                            return true;
+                        }
+                        else
+                        {
+                            byte[] data = ImportCM.LoadTexture(GameUty.FileSystem, item.icon, false).data;
+                            texture2D = new Texture2D(50, 50, TextureFormat.RGB565, false);
+                            texture2D.LoadImage(data);
+                        }
+                        item.tex = texture2D;
+                    }
+                    previousMenu = item.menu;
+                    return false;
+                }
+                return true;
+            });
+        }
+
+        private static bool validBG2Menu(string menu)
+        {
+            menu = Path.GetFileNameWithoutExtension(menu).ToLower();
+            return !(menu.EndsWith("_del") || menu.Contains("zurashi") || menu.Contains("mekure")
+                || menu.Contains("porori") || menu.Contains("moza"));
+        }
+
+        private static bool validBG2Menu(SortItem item)
+        {
+            return accMpn.Contains(item.category) && validBG2Menu(item.menu);
+        }
+
+        private static bool ParseNativeMenuFile(int menuIndex, SortItem sortItem)
         {
             MenuDataBase menuDataBase = GameMain.Instance.MenuDataBase;
             menuDataBase.SetIndex(menuIndex);
             if (menuDataBase.GetBoDelOnly()) return false;
-            itemData.menu = menuDataBase.GetMenuFileName().ToLower();
-            itemData.name = menuDataBase.GetMenuName();
-            itemData.category = menuDataBase.GetCategoryMpnText();
-            itemData.icon = menuDataBase.GetIconS();
-            itemData.priority = (int)menuDataBase.GetPriority();
+            sortItem.category = menuDataBase.GetCategoryMpnText();
+            if (!accMpn.Contains(sortItem.category)) return false;
+            sortItem.menu = menuDataBase.GetMenuFileName().ToLower();
+            if (!validBG2Menu(sortItem.menu)) return false;
+            sortItem.name = menuDataBase.GetMenuName();
+            sortItem.icon = menuDataBase.GetIconS();
+            sortItem.priority = (int)menuDataBase.GetPriority();
             return true;
         }
 
-        private static bool ParseMenuFile(string menuFile, ItemData itemData)
+        private static bool ParseMenuFile(string menuFile, SortItem sortItem)
         {
-            if (menuFile.EndsWith("_del.menu")) return false;
+            if (!validBG2Menu(menuFile)) return false;
 
             byte[] buf;
             using (AFileBase aFileBase = GameUty.FileOpen(menuFile))
@@ -5610,8 +5860,9 @@ namespace CM3D2.MultipleMaids.Plugin
                     {
                         binaryReader.ReadInt32();
                         binaryReader.ReadString();
-                        itemData.name = binaryReader.ReadString();
-                        itemData.category = binaryReader.ReadString();
+                        sortItem.name = binaryReader.ReadString();
+                        sortItem.category = binaryReader.ReadString();
+                        if (!accMpn.Contains(sortItem.category)) return false;
                         binaryReader.ReadString();
                         binaryReader.ReadInt32();
                         bool run = true;
@@ -5629,13 +5880,13 @@ namespace CM3D2.MultipleMaids.Plugin
                                 if (header == "icons" || header == "icon")
                                 {
                                     run = false;
-                                    itemData.icon = binaryReader.ReadString();
+                                    sortItem.icon = binaryReader.ReadString();
                                     break;
                                 }
 
                                 if (header == "priority")
                                 {
-                                    int.TryParse(binaryReader.ReadString(), out itemData.priority);
+                                    int.TryParse(binaryReader.ReadString(), out sortItem.priority);
                                     break;
                                 }
                             }
@@ -5650,8 +5901,10 @@ namespace CM3D2.MultipleMaids.Plugin
             return true;
         }
 
-        private static bool ParseModMenuFile(string modMenuFile, ItemData modItemData)
+        private static bool ParseModMenuFile(string modMenuFile, SortItem sortItem)
         {
+            if (!validBG2Menu(modMenuFile)) return false;
+
             byte[] buf = null;
             try
             {
@@ -5676,9 +5929,10 @@ namespace CM3D2.MultipleMaids.Plugin
                         binaryReader.ReadInt32();
                         string iconName = binaryReader.ReadString();
                         string baseItemPath = binaryReader.ReadString().Replace(":", " ");
-                        modItemData.baseItem = Path.GetFileName(baseItemPath);
-                        modItemData.name = binaryReader.ReadString();
-                        modItemData.category = binaryReader.ReadString();
+                        sortItem.baseMenu = Path.GetFileName(baseItemPath);
+                        sortItem.name = binaryReader.ReadString();
+                        sortItem.category = binaryReader.ReadString();
+                        if (!accMpn.Contains(sortItem.category)) return false;
                         binaryReader.ReadString();
                         string mpnValue = binaryReader.ReadString();
                         MPN mpn = MPN.null_mpn;
@@ -5705,7 +5959,7 @@ namespace CM3D2.MultipleMaids.Plugin
                             {
                                 Texture2D tex = new Texture2D(1, 1, TextureFormat.RGBA32, false);
                                 tex.LoadImage(data);
-                                modItemData.tex = tex;
+                                sortItem.tex = tex;
                                 break;
                             }
                         }
@@ -5718,6 +5972,7 @@ namespace CM3D2.MultipleMaids.Plugin
             }
             return true;
         }
+
         private static string PluginString()
         {
             return $"{PluginName} {PluginVersion}";
@@ -5747,11 +6002,11 @@ namespace CM3D2.MultipleMaids.Plugin
 
         private class SortItem
         {
-            public string category = String.Empty;
-            public string menu = string.Empty;
-            public string baseMenu = string.Empty;
-            public string name = string.Empty;
-            public string icon = string.Empty;
+            public string category;
+            public string menu;
+            public string baseMenu;
+            public string name;
+            public string icon;
             public bool isCreative = false;
             public bool isOfficialMod = false;
             public bool isMod = false;