From b78f004bfbfbad9e0be5a3940767db1e5b6a53b5 Mon Sep 17 00:00:00 2001 From: RayTracerX Date: Sun, 18 Oct 2026 13:27:12 -0600 Subject: [PATCH 1/3] Fixed potential errors that copilot caught when reviewing code --- api/AbstractListWindow.cs | 1 + resources/commit | 2 +- services/ModDownloadService.cs | 30 +++++++++----- services/ModManagerService.cs | 1 + services/ModWorkshopServiceUnix.cs | 4 +- services/ModWorkshopServiceWindows.cs | 4 +- ui/ModBrowserWindow.cs | 17 +++++++- ui/prefabs/ModBrowserRow.cs | 2 +- utils/WorkshopThumbnails.cs | 59 ++++++++++++++++++++++++--- 9 files changed, 98 insertions(+), 22 deletions(-) diff --git a/api/AbstractListWindow.cs b/api/AbstractListWindow.cs index 417cfee..e1dd999 100644 --- a/api/AbstractListWindow.cs +++ b/api/AbstractListWindow.cs @@ -98,6 +98,7 @@ protected virtual void ClearList() /// public static new T CreateAndInit(string pWindowId) { + WindowId = pWindowId; ScrollWindow scroll_window = WindowCreator.CreateEmptyWindow(pWindowId, pWindowId + " Title"); GameObject window_object = scroll_window.gameObject; diff --git a/resources/commit b/resources/commit index ae7ea58..82c5a71 100644 --- a/resources/commit +++ b/resources/commit @@ -1 +1 @@ -4773c590604d5af711ff15ddc21b18f092234163 \ No newline at end of file +7638405b0ef105101a13d0bab0c91633fb3bc10f \ No newline at end of file diff --git a/services/ModDownloadService.cs b/services/ModDownloadService.cs index 6fce374..5c29966 100644 --- a/services/ModDownloadService.cs +++ b/services/ModDownloadService.cs @@ -146,10 +146,6 @@ internal static string StripBBCode(string pText) private static WorkshopDownloadQueue CreateQueue() { var download_queue = new WorkshopDownloadQueue(Steam, Install, Task.Delay, () => DateTime.UtcNow); - download_queue.Changed += entry => - { - if (entry.State is DownloadState.Cancelled or DownloadState.Failed) SetPending(entry.Listing.Id, false); - }; return download_queue; } @@ -169,23 +165,37 @@ private static void SetPending(ulong pFileId, bool pPending) /// private static string Install(string pDirectory) { - if (ulong.TryParse(Path.GetFileName(pDirectory), out ulong file_id)) SetPending(file_id, false); + ulong.TryParse(Path.GetFileName(pDirectory), out ulong file_id); ModDeclare mod = ModInfoUtils.recogMod(pDirectory, false); if (mod == null) return ErrorNotAMod; + + if (mod.ModType != ModTypeEnum.NEOMOD) return ErrorNotAMod; - if (ModInfoUtils.TryGetRecognizedMod(mod.UID, out _)) + if (ModInfoUtils.TryGetRecognizedMod(mod.UID, out ModDeclare recognized)) { - if (ModCompileLoadService.IsModLoaded(mod.UID)) restart_pending.Add(mod.UID); + if (ModCompileLoadService.IsModLoaded(mod.UID)) + { + restart_pending.Add(mod.UID); + } + else + { + ModState state = WorldBoxMod.AllRecognizedMods.TryGetValue(recognized, out ModState current) + ? current + : ModState.FAILED; + WorldBoxMod.AllRecognizedMods.Remove(recognized); + WorldBoxMod.AllRecognizedMods[mod] = ModInfoUtils.isModDisabled(mod.UID) ? ModState.DISABLED : state; + ModDepenSolveService.EnsureNode(mod); + } + + if (file_id != 0) SetPending(file_id, false); return null; } // New to this computer: saved as disabled, so neither this session nor the next start loads it. ModInfoUtils.setModDisabled(mod.UID, true); - - // The startup scan only lists NeoModLoader-type Workshop mods; do the same here. - if (mod.ModType != ModTypeEnum.NEOMOD) return null; if (string.IsNullOrEmpty(mod.RepoUrl)) mod.SetRepoUrlToWorkshopPage(Path.GetFileName(pDirectory)); ModInfoUtils.EnsureRecognizedMod(mod); + if (file_id != 0) SetPending(file_id, false); return null; } } diff --git a/services/ModManagerService.cs b/services/ModManagerService.cs index 95a6d5d..b694e2c 100644 --- a/services/ModManagerService.cs +++ b/services/ModManagerService.cs @@ -181,6 +181,7 @@ public static async Task DeleteAsync(InstalledMod pMod) } WorldBoxMod.AllRecognizedMods.Remove(declaration); + ModDepenSolveService.InitializeGraph(WorldBoxMod.AllRecognizedMods.Keys); return true; } diff --git a/services/ModWorkshopServiceUnix.cs b/services/ModWorkshopServiceUnix.cs index a8f7fbc..755d345 100644 --- a/services/ModWorkshopServiceUnix.cs +++ b/services/ModWorkshopServiceUnix.cs @@ -379,6 +379,8 @@ public async Task GetPersonaNameAsync(ulong pSteamId) public async Task GetDescriptionAsync(ulong pFileId) { ResultPage? page = await Query.Items.WithFileId(pFileId).WithLongDescription(true).GetPageAsync(1); - return page?.Entries.Select(item => item.Description).FirstOrDefault(); + if (!page.HasValue) return null; + using ResultPage result_page = page.Value; + return result_page.Entries.Select(item => item.Description).FirstOrDefault(); } } \ No newline at end of file diff --git a/services/ModWorkshopServiceWindows.cs b/services/ModWorkshopServiceWindows.cs index 6b0830c..fcc0e58 100644 --- a/services/ModWorkshopServiceWindows.cs +++ b/services/ModWorkshopServiceWindows.cs @@ -379,6 +379,8 @@ public async Task GetPersonaNameAsync(ulong pSteamId) public async Task GetDescriptionAsync(ulong pFileId) { ResultPage? page = await Query.Items.WithFileId(pFileId).WithLongDescription(true).GetPageAsync(1); - return page?.Entries.Select(item => item.Description).FirstOrDefault(); + if (!page.HasValue) return null; + using ResultPage result_page = page.Value; + return result_page.Entries.Select(item => item.Description).FirstOrDefault(); } } \ No newline at end of file diff --git a/ui/ModBrowserWindow.cs b/ui/ModBrowserWindow.cs index 24ae2e9..31e8de5 100644 --- a/ui/ModBrowserWindow.cs +++ b/ui/ModBrowserWindow.cs @@ -128,7 +128,7 @@ private async Task LoadAsync() { WorkshopCatalog catalog = ModDownloadService.Catalog; await catalog.LoadAsync(); - if (query.Sort == ListingSort.Trending) await catalog.LoadTrendingAsync(); + if (catalog.Status == CatalogStatus.Ready && query.Sort == ListingSort.Trending) await catalog.LoadTrendingAsync(); await ModDownloadService.Authors.RequestAsync(catalog.Items.Select(listing => listing.AuthorId)); } @@ -199,11 +199,24 @@ private void UpdateBottomBar() private void OnDownloadChanged(DownloadEntry pEntry) { - if (ItemMap.TryGetValue(pEntry.Listing, out AbstractListWindowItem row)) + AbstractListWindowItem row = null; + if (!ItemMap.TryGetValue(pEntry.Listing, out row)) + { + row = ItemMap.Where(pair => pair.Key.Id == pEntry.Listing.Id).Select(pair => pair.Value).FirstOrDefault(); + } + + if (row != null) { ((ModBrowserRow)row).UpdateStatus(); } + vb if (pEntry.State is DownloadState.Completed or DownloadState.Failed or DownloadState.Cancelled && + query.Install != InstallFilter.All) + { + if (IsOpened) Refresh(); + else needs_refresh = true; + } + if (IsOpened) UpdateBottomBar(); } diff --git a/ui/prefabs/ModBrowserRow.cs b/ui/prefabs/ModBrowserRow.cs index dfb4a8a..f1b1eca 100644 --- a/ui/prefabs/ModBrowserRow.cs +++ b/ui/prefabs/ModBrowserRow.cs @@ -42,7 +42,7 @@ public override void Setup(ModListing pListing) title.text = pListing.Title; age.text = DescribeAge(DateTime.UtcNow - pListing.Updated); thumbnail.sprite = InternalResourcesGetter.GetIcon(); - WorkshopThumbnails.Request(pListing.PreviewUrl, sprite => + WorkshopThumbnails.Request(pListing.PreviewUrl, this, sprite => { // The pooled row may show another listing by the time the image arrives. if (listing == pListing) thumbnail.sprite = sprite; diff --git a/utils/WorkshopThumbnails.cs b/utils/WorkshopThumbnails.cs index e76d177..2faffff 100644 --- a/utils/WorkshopThumbnails.cs +++ b/utils/WorkshopThumbnails.cs @@ -26,6 +26,8 @@ internal static class WorkshopThumbnails private static readonly Dictionary sprites = new(); private static readonly Queue load_order = new(); private static readonly Dictionary>> waiting = new(); + private static readonly Dictionary> owners_by_key = new(); + private static readonly Dictionary key_by_owner = new(); private static readonly SemaphoreSlim download_slots = new(max_parallel_downloads); private static HttpClient http_client; @@ -35,8 +37,9 @@ internal static class WorkshopThumbnails /// Call with the preview at : at once when it is /// cached, otherwise once it has loaded. It is not called when the image cannot be loaded. /// - public static void Request(string pUrl, Action pOnLoaded) + public static void Request(string pUrl, object pOwner, Action pOnLoaded) { + if (pOwner != null) AssignOwner(pOwner, pUrl == null ? null : UrlKey(pUrl)); if (string.IsNullOrEmpty(pUrl)) return; string key = UrlKey(pUrl); @@ -153,12 +156,28 @@ private static void Store(string pKey, Sprite pSprite) { sprites[pKey] = pSprite; load_order.Enqueue(pKey); - if (load_order.Count <= memory_limit) return; + int inspected = 0; + while (load_order.Count > memory_limit && inspected < load_order.Count) + { + string oldest = load_order.Dequeue(); + if (!sprites.TryGetValue(oldest, out Sprite oldest_sprite)) + { + continue; + } + + if (owners_by_key.TryGetValue(oldest, out HashSet owners) && owners.Count > 0) + { + load_order.Enqueue(oldest); + inspected++; + continue; + } - string oldest = load_order.Dequeue(); - UnityEngine.Object.Destroy(sprites[oldest].texture); - UnityEngine.Object.Destroy(sprites[oldest]); - sprites.Remove(oldest); + UnityEngine.Object.Destroy(oldest_sprite.texture); + UnityEngine.Object.Destroy(oldest_sprite); + sprites.Remove(oldest); + owners_by_key.Remove(oldest); + inspected = 0; + } } /// @@ -176,4 +195,32 @@ private static string UrlKey(string pUrl) return hash.ToString("x16"); } + + private static void AssignOwner(object pOwner, string pKey) + { + if (key_by_owner.TryGetValue(pOwner, out string current)) + { + if (current == pKey) return; + + if (owners_by_key.TryGetValue(current, out HashSet current_owners)) + { + current_owners.Remove(pOwner); + } + } + + if (pKey == null) + { + key_by_owner.Remove(pOwner); + return; + } + + key_by_owner[pOwner] = pKey; + if (!owners_by_key.TryGetValue(pKey, out HashSet owners)) + { + owners = new HashSet(); + owners_by_key[pKey] = owners; + } + + owners.Add(pOwner); + } } From a90d561872531c94b571a064d2b866a62c8a71c6 Mon Sep 17 00:00:00 2001 From: RayTracerX Date: Sun, 18 Oct 2026 13:59:15 -0600 Subject: [PATCH 2/3] fixed typo --- ui/ModBrowserWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/ModBrowserWindow.cs b/ui/ModBrowserWindow.cs index 31e8de5..bc88d8a 100644 --- a/ui/ModBrowserWindow.cs +++ b/ui/ModBrowserWindow.cs @@ -210,7 +210,7 @@ private void OnDownloadChanged(DownloadEntry pEntry) ((ModBrowserRow)row).UpdateStatus(); } - vb if (pEntry.State is DownloadState.Completed or DownloadState.Failed or DownloadState.Cancelled && + if (pEntry.State is DownloadState.Completed or DownloadState.Failed or DownloadState.Cancelled && query.Install != InstallFilter.All) { if (IsOpened) Refresh(); From 19a7bcfa7cddb6dcd9391cde22f59c2d0189acb7 Mon Sep 17 00:00:00 2001 From: RayTracerX Date: Sun, 18 Oct 2026 14:04:40 -0600 Subject: [PATCH 3/3] Mini tweaks to the ReadMe Updated README to emphasize subscribing for updates. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ff1aa0..d43d1bd 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ for short. Detailed introduction look [Gitbook](https://markamibox.github.io/wor 2. Delete NCMS_memload.dll (They can be installed together but not suggested) 3. Start the game with experimental mode enabled -It is recommended to subscribe to the NML item on the Steam Workshop to receive automatic updates +It is recommended to subscribe to the NML item on the Steam Workshop to receive automatic updates ### Android Installation @@ -47,6 +47,9 @@ It is recommended to subscribe to the NML item on the Steam Workshop to receive

Report BUG + +
+

New to Modding?