McInstallManifest.cs 4.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. namespace PCLCS;
  2. /// <summary>
  3. /// MC 版本安装清单及 PCL 管理文件清单。
  4. /// </summary>
  5. /// <param name="SchemaVersion"> 清单格式版本;无版本的旧清单为 0。</param>
  6. /// <param name="Components"> 安装目标及受管理组件版本。</param>
  7. /// <param name="LastModifiedUtc"> 最后修改时间(UTC)。</param>
  8. /// <param name="ManagedFiles"> 受管理文件;没有文件时为空列表。</param>
  9. public record McInstallManifest(int SchemaVersion, McComponents Components, DateTime LastModifiedUtc, IReadOnlyList<McInstallManifest.ManagedFile> ManagedFiles) {
  10. /// <summary>
  11. /// 清理以 Mod 形式安装的、已删除或修改的组件的记录,并更新清单。
  12. /// </summary>
  13. public McInstallManifest RefreshModComponents(string gameFolder) {
  14. List<ManagedFile> invalidFiles = [];
  15. foreach (ManagedFile entry in ManagedFiles) {
  16. if (entry.Component is null) continue; // 不检查非组件文件
  17. FileInfo file = new(Path.Combine(gameFolder, entry.RelativePath));
  18. if (!file.Exists || file.LastWriteTimeUtc != entry.LastWriteTimeUtc.ToUniversalTime()) invalidFiles.Add(entry);
  19. }
  20. if (!invalidFiles.Any()) return this;
  21. return this with {
  22. Components = Components with {
  23. OptiFineEntry = invalidFiles.Any(file => file.Component == "OptiFine") ? null : Components.OptiFineEntry,
  24. FabricApiVersion = invalidFiles.Any(file => file.Component == "FabricApi") ? null : Components.FabricApiVersion,
  25. OptiFabricVersion = invalidFiles.Any(file => file.Component == "OptiFabric") ? null : Components.OptiFabricVersion
  26. },
  27. ManagedFiles = ManagedFiles.Where(file => !invalidFiles.Contains(file)).ToList(),
  28. LastModifiedUtc = DateTime.UtcNow
  29. };
  30. }
  31. /// <summary>
  32. /// 由 PCL 管理并记录的版本内文件。
  33. /// </summary>
  34. /// <param name="RelativePath"> 相对于游戏目录的文件路径。确保路径为全小写且可比较。</param>
  35. /// <param name="Sha1"> 小写的 SHA1。</param>
  36. /// <param name="LastWriteTimeUtc"> 目标文件的最后修改时间(UTC)。</param>
  37. /// <param name="Component"> 安装器组件名;为空时表示文件由整合包管理。</param>
  38. public record ManagedFile(string RelativePath, string Sha1, DateTime LastWriteTimeUtc,
  39. [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? Component = null);
  40. }
  41. /// <param name="MinecraftVersion"> 目标 Minecraft 版本。</param>
  42. /// <param name="ModLoaderVersions"> 目标中各个 Mod Loader 的版本;原版为空字典。</param>
  43. /// <param name="OptiFineEntry"> OptiFine 条目信息,不安装时为 <see langword="null"/>。</param>
  44. /// <param name="FabricApiVersion"> 由 PCL 管理的 Fabric API 版本;不安装时为 <see langword="null"/>。</param>
  45. /// <param name="OptiFabricVersion"> 由 PCL 管理的 OptiFabric 版本;不安装时为 <see langword="null"/>。</param>
  46. [method: JsonConstructor]
  47. public record McComponents(string MinecraftVersion, Dictionary<ModLoaders, string> ModLoaderVersions,
  48. [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] JObject? OptiFineEntry = null,
  49. [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? FabricApiVersion = null,
  50. [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? OptiFabricVersion = null) {
  51. [JsonIgnore]
  52. public string? OptiFineVersion => OptiFineEntry?["InstanceName"]?.Value<string>()?.AfterLast("-OptiFine_");
  53. public McComponents(string minecraftVersion, JObject? optiFineEntry, string? forgeVersion,
  54. string? neoForgeVersion, string? fabricVersion, string? fabricApiVersion, string? optiFabricVersion, string? liteLoaderVersion)
  55. : this(minecraftVersion, [], optiFineEntry, fabricApiVersion, optiFabricVersion) {
  56. if (forgeVersion is not null) ModLoaderVersions[ModLoaders.Forge] = forgeVersion;
  57. if (neoForgeVersion is not null) ModLoaderVersions[ModLoaders.NeoForge] = neoForgeVersion;
  58. if (fabricVersion is not null) ModLoaderVersions[ModLoaders.Fabric] = fabricVersion;
  59. if (liteLoaderVersion is not null) ModLoaderVersions[ModLoaders.LiteLoader] = liteLoaderVersion;
  60. }
  61. public bool IsEquivalentTo(McComponents other) =>
  62. MinecraftVersion == other.MinecraftVersion &&
  63. OptiFineVersion == other.OptiFineVersion &&
  64. FabricApiVersion == other.FabricApiVersion &&
  65. OptiFabricVersion == other.OptiFabricVersion &&
  66. ModLoaderVersions.Count == other.ModLoaderVersions.Count &&
  67. ModLoaderVersions.All(entry => other.ModLoaderVersions.TryGetValue(entry.Key, out string? version) && version == entry.Value);
  68. }