Remove obsolete project files for MrGameEng.AI, MrGameEng.Assets, MrGameEng.Atlases, and MrGameEng.Collisions modules. Introduce new AssetManager and related classes for asset loading and management, including support for texture atlases and mod definitions. Enhance mod loading capabilities with DefDatabase and LanguageManager for JSON-based definitions and localization. Implement a shelf packing algorithm for efficient texture atlas creation.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
using Xunit;
|
||||
|
||||
namespace MrGameEng.Mods.Tests;
|
||||
|
||||
public sealed class ModLoaderTests : IDisposable
|
||||
{
|
||||
private readonly string _root = Directory.CreateTempSubdirectory("mrge-mods-tests-").FullName;
|
||||
|
||||
private string ModsRoot => Path.Combine(_root, "Mods");
|
||||
|
||||
public void Dispose() => Directory.Delete(_root, recursive: true);
|
||||
|
||||
/// <summary>Создаёт мод с About.json; зависимости — id модов, которые должны грузиться раньше.</summary>
|
||||
private void WriteMod(string id, params string[] dependencies)
|
||||
{
|
||||
var aboutDir = Path.Combine(ModsRoot, id, "About");
|
||||
Directory.CreateDirectory(aboutDir);
|
||||
var deps = string.Join(", ", dependencies.Select(d => $"\"{d}\""));
|
||||
File.WriteAllText(
|
||||
Path.Combine(aboutDir, "About.json"),
|
||||
$$"""{ "id": "{{id}}", "name": "{{id}} mod", "version": "1.0", "dependencies": [{{deps}}] }"""
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_OrdersDependenciesFirst_ThenAlphabetically()
|
||||
{
|
||||
WriteMod("zebra", "Core");
|
||||
WriteMod("apple", "Core");
|
||||
WriteMod("Core");
|
||||
|
||||
var mods = ModLoader.Load(ModsRoot);
|
||||
|
||||
Assert.Equal(["Core", "apple", "zebra"], mods.Select(m => m.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_MissingDependency_Throws()
|
||||
{
|
||||
WriteMod("orphan", "NoSuchMod");
|
||||
|
||||
var exception = Assert.Throws<InvalidDataException>(() => ModLoader.Load(ModsRoot));
|
||||
Assert.Contains("NoSuchMod", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_CyclicDependency_Throws()
|
||||
{
|
||||
WriteMod("a", "b");
|
||||
WriteMod("b", "a");
|
||||
|
||||
var exception = Assert.Throws<InvalidDataException>(() => ModLoader.Load(ModsRoot));
|
||||
Assert.Contains("Cyclic", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_ActiveSubset_LoadsOnlyRequested()
|
||||
{
|
||||
WriteMod("Core");
|
||||
WriteMod("extra", "Core");
|
||||
WriteMod("unused");
|
||||
|
||||
var mods = ModLoader.Load(ModsRoot, ["extra", "Core"]);
|
||||
|
||||
Assert.Equal(["Core", "extra"], mods.Select(m => m.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_IgnoresDirectoriesWithoutAbout()
|
||||
{
|
||||
WriteMod("Core");
|
||||
Directory.CreateDirectory(Path.Combine(ModsRoot, "not-a-mod"));
|
||||
|
||||
var mod = Assert.Single(ModLoader.Load(ModsRoot));
|
||||
Assert.Equal("Core", mod.Id);
|
||||
Assert.Equal("Core mod", mod.Info.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindModsRoot_WalksUpFromNestedDirectory()
|
||||
{
|
||||
WriteMod("Core");
|
||||
var nested = Path.Combine(_root, "bin", "Debug", "net8.0");
|
||||
Directory.CreateDirectory(nested);
|
||||
|
||||
Assert.Equal(ModsRoot, ModLoader.FindModsRoot(nested));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContentTree_LaterMod_OverridesSameRelativePath()
|
||||
{
|
||||
WriteMod("Core");
|
||||
WriteMod("patch", "Core");
|
||||
File.WriteAllText(CreateContentFile("Core", "Textures", "things/rock.png"), "core");
|
||||
File.WriteAllText(CreateContentFile("Core", "Textures", "things/tree.png"), "core");
|
||||
File.WriteAllText(CreateContentFile("patch", "Textures", "things/rock.png"), "patched");
|
||||
|
||||
var mods = ModLoader.Load(ModsRoot);
|
||||
var tree = ModContentTree.Build(mods, "Textures", ".png");
|
||||
|
||||
Assert.Equal(2, tree.Files.Count);
|
||||
Assert.True(tree.TryGet("things/rock.png", out var rock));
|
||||
Assert.Equal("patch", rock.Mod.Id);
|
||||
Assert.Equal("patched", File.ReadAllText(rock.FullPath));
|
||||
Assert.True(tree.TryGet("things/tree.png", out var oak));
|
||||
Assert.Equal("Core", oak.Mod.Id);
|
||||
}
|
||||
|
||||
private string CreateContentFile(string modId, string folder, string relativePath)
|
||||
{
|
||||
var fullPath = Path.Combine(ModsRoot, modId, folder, relativePath);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!);
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user