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
New to Modding?
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..bc88d8a 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();
}
+ 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);
+ }
}