Map zones follow load order and mod reloads; mod unit tests
This commit is contained in:
1 parent
fa734b7edc
commit
fda254a698
5 files changed
+92
-47
No files matched your search
+37
-37
@@ -1,37 +1,37 @@
|
||||
# Godot 4.x
|
||||
.godot/
|
||||
.idea/
|
||||
sdk/
|
||||
bin/
|
||||
obj/
|
||||
|
||||
#ai
|
||||
.omo/
|
||||
.opencode/
|
||||
|
||||
gen/
|
||||
obj/
|
||||
bin/
|
||||
|
||||
*.tres~
|
||||
*.tscn~
|
||||
|
||||
# GDScript LSP
|
||||
.godot/
|
||||
|
||||
# Export
|
||||
*.exe
|
||||
*.dmg
|
||||
*.apk
|
||||
*.aab
|
||||
*.pck
|
||||
*.zip
|
||||
*.log
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Editor
|
||||
*.import
|
||||
*.godot.uid
|
||||
# Godot 4.x
|
||||
.godot/
|
||||
.idea/
|
||||
sdk/
|
||||
bin/
|
||||
obj/
|
||||
|
||||
#ai
|
||||
.omo/
|
||||
.opencode/
|
||||
|
||||
gen/
|
||||
obj/
|
||||
bin/
|
||||
|
||||
*.tres~
|
||||
*.tscn~
|
||||
|
||||
# GDScript LSP
|
||||
.godot/
|
||||
|
||||
# Export
|
||||
*.exe
|
||||
*.dmg
|
||||
*.apk
|
||||
*.aab
|
||||
*.pck
|
||||
*.zip
|
||||
*.log
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Editor
|
||||
*.import
|
||||
*.godot.uid
|
||||
@@ -8,6 +8,7 @@
|
||||
"sources": [
|
||||
"src/**/*.cs"
|
||||
],
|
||||
"test_sources": ["tests/*.cs"],
|
||||
"depends": ["ui_base>=1"],
|
||||
"ui": [
|
||||
{
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace Cthangover.Mods.Map
|
||||
public void OnModResourcesReady()
|
||||
{
|
||||
var service = MapZoneService.Instance;
|
||||
// Runs again after every mod reload; the service outlives it when map_base stays loaded.
|
||||
service.MarkStale();
|
||||
MapStateBridge.Register(service.ExportState, service.ImportState, service.ResetOverrides);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-10
@@ -67,8 +67,10 @@ namespace Cthangover.Mods.Map
|
||||
|
||||
/// <summary>
|
||||
/// Loads the zone list from the given data, replacing the previous list
|
||||
/// but preserving runtime overrides. This is the testable entry point;
|
||||
/// runtime code uses <see cref="ReloadFromMods"/>.
|
||||
/// but preserving runtime overrides. Zones come in mod load order: a
|
||||
/// zone with an id seen before replaces it in place, so a dependent mod
|
||||
/// overrides a zone the way it overrides any other resource. This is the
|
||||
/// testable entry point; runtime code uses <see cref="ReloadFromMods"/>.
|
||||
/// </summary>
|
||||
public void LoadZones(IEnumerable<MapZone> newZones)
|
||||
{
|
||||
@@ -82,13 +84,13 @@ namespace Cthangover.Mods.Map
|
||||
if (zone == null || string.IsNullOrEmpty(zone.ID))
|
||||
continue;
|
||||
|
||||
if (byId.ContainsKey(zone.ID))
|
||||
if (byId.TryGetValue(zone.ID, out var replaced))
|
||||
{
|
||||
GameLogger.Log("MAP", $"duplicate zone id '{zone.ID}' ignored", LogLevel.Warning);
|
||||
continue;
|
||||
GameLogger.Log("MAP", $"zone '{zone.ID}' replaced by a later definition", LogLevel.Warning);
|
||||
zones[zones.IndexOf(replaced)] = zone;
|
||||
}
|
||||
|
||||
zones.Add(zone);
|
||||
else
|
||||
zones.Add(zone);
|
||||
byId[zone.ID] = zone;
|
||||
}
|
||||
}
|
||||
@@ -114,10 +116,11 @@ namespace Cthangover.Mods.Map
|
||||
var result = new List<MapZone>();
|
||||
var options = JsonOptionsProvider.ReadOptions;
|
||||
|
||||
foreach (var kvp in ModManager.Instance.Mods)
|
||||
foreach (var modId in ModRegistry.Instance.GetOrderedModIds())
|
||||
{
|
||||
var modId = kvp.Key;
|
||||
var provider = kvp.Value.FileProvider;
|
||||
var provider = ModManager.Instance.GetMod(modId)?.FileProvider;
|
||||
if (provider == null)
|
||||
continue;
|
||||
|
||||
foreach (var entry in provider.ListFiles("map"))
|
||||
{
|
||||
@@ -157,6 +160,9 @@ namespace Cthangover.Mods.Map
|
||||
/// Loads zones on first use and re-reads nothing afterwards; call
|
||||
/// <see cref="Reload"/> when the mod set or the map files changed.
|
||||
/// </summary>
|
||||
/// <summary>Drops the loaded zones so the next <see cref="EnsureLoaded"/> reads the current mod set (called after every mod set load).</summary>
|
||||
public void MarkStale() => loaded = false;
|
||||
|
||||
public void EnsureLoaded()
|
||||
{
|
||||
if (!loaded)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace Cthangover.Mods.Map.Tests
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Cthangover.Core.Mods.Testing;
|
||||
|
||||
/// <summary>
|
||||
/// Zone lists arrive in mod load order, and a mod that ships a zone with an
|
||||
/// id already defined replaces it — the same rule as every other mod
|
||||
/// resource, so a dependent mod can reshape a zone of the mod it builds on.
|
||||
/// </summary>
|
||||
public class MapZoneServiceTests
|
||||
{
|
||||
[ModTest("zones/later_definition_replaces_earlier")]
|
||||
static void LaterDefinitionReplacesEarlier(ModTestContext ctx)
|
||||
{
|
||||
var service = MapZoneService.Instance;
|
||||
try
|
||||
{
|
||||
service.LoadZones(new List<MapZone>
|
||||
{
|
||||
new() { ID = "test_zone", Title = "owner" },
|
||||
new() { ID = "test_other", Title = "other" },
|
||||
new() { ID = "TEST_ZONE", Title = "dependent" },
|
||||
});
|
||||
|
||||
ModAssert.Equal(2, service.Zones.Count, "zone count");
|
||||
ModAssert.Equal("dependent", service.Get("test_zone").Title, "the later definition wins");
|
||||
ModAssert.Equal("dependent", service.Zones[0].Title, "it keeps the replaced zone's place");
|
||||
}
|
||||
finally
|
||||
{
|
||||
service.ReloadFromMods();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user