using Godot; using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; public partial class PackManager : Node { public record PackManifest(int schema, string id, string name, string version, int priority); private readonly List<(string Root, PackManifest Manifest)> _packs = new(); public override void _EnterTree() { LoadPacks("res://packs"); GD.Print($"Loaded packs: {string.Join(", ", _packs.Select(p => $"{p.Manifest.id}(prio={p.Manifest.priority})"))}"); } public override void _Ready() {/* LoadPacks("res://packs"); GD.Print($"Loaded packs: {string.Join(", ", _packs.Select(p => $"{p.Manifest.id}(prio={p.Manifest.priority})"))}");*/ } public void LoadPacks(string packsRoot) { _packs.Clear(); if (!DirAccess.DirExistsAbsolute(packsRoot)) { GD.PrintErr($"Packs root missing: {packsRoot}"); return; } using var dir = DirAccess.Open(packsRoot); dir.ListDirBegin(); while (true) { var entry = dir.GetNext(); if (entry == "") break; if (entry.StartsWith(".")) continue; var packPath = $"{packsRoot}/{entry}"; if (!dir.CurrentIsDir()) continue; var manifestPath = $"{packPath}/pack.json"; if (!FileAccess.FileExists(manifestPath)) continue; var json = FileAccess.GetFileAsString(manifestPath); var manifest = JsonSerializer.Deserialize(json); if (manifest == null) { GD.PrintErr($"Invalid pack.json: {manifestPath}"); continue; } _packs.Add((packPath, manifest)); } dir.ListDirEnd(); // Sort bottom->top by priority (low first, high last) _packs.Sort((a, b) => a.Manifest.priority.CompareTo(b.Manifest.priority)); } /// Returns the best (topmost) existing file path for a relative path inside a pack. public string? Resolve(string relativePath) { relativePath = relativePath.TrimStart('/'); // Highest priority pack wins -> iterate from end for (int i = _packs.Count - 1; i >= 0; i--) { var candidate = $"{_packs[i].Root}/{relativePath}"; if (FileAccess.FileExists(candidate)) return candidate; } return null; } }