/********* ** Public methods *********/ /// <summary>Get manifest metadata for each folder in the given root path.</summary> /// <param name="toolkit">The mod toolkit.</param> /// <param name="rootPath">The root path to search for mods.</param> /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param> /// <returns>Returns the manifests by relative folder.</returns> public IEnumerable <IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase) { foreach (ModFolder folder in toolkit.GetModFolders(rootPath)) { Manifest manifest = folder.Manifest; // parse internal data record (if any) ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest); // apply defaults if (manifest != null && dataRecord != null) { if (dataRecord.UpdateKey != null) { manifest.UpdateKeys = new[] { dataRecord.UpdateKey } } ; } // build metadata bool shouldIgnore = folder.Type == ModType.Ignored; ModMetadataStatus status = folder.ManifestParseError == ModParseError.None || shouldIgnore ? ModMetadataStatus.Found : ModMetadataStatus.Failed; yield return(new ModMetadata(folder.DisplayName, folder.Directory.FullName, rootPath, manifest, dataRecord, isIgnored: shouldIgnore) .SetStatus(status, shouldIgnore ? "disabled by dot convention" : folder.ManifestParseErrorText)); } }
/********* ** Public methods *********/ /// <summary>Get manifest metadata for each folder in the given root path.</summary> /// <param name="toolkit">The mod toolkit.</param> /// <param name="rootPath">The root path to search for mods.</param> /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param> /// <returns>Returns the manifests by relative folder.</returns> public IEnumerable <IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase) { foreach (ModFolder folder in toolkit.GetModFolders(rootPath)) { Manifest manifest = folder.Manifest; // parse internal data record (if any) ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest); // apply defaults if (manifest != null && dataRecord != null) { if (dataRecord.UpdateKey != null) { manifest.UpdateKeys = new[] { dataRecord.UpdateKey } } ; } // build metadata ModMetadataStatus status = folder.ManifestParseError == null || !folder.ShouldBeLoaded ? ModMetadataStatus.Found : ModMetadataStatus.Failed; string relativePath = PathUtilities.GetRelativePath(rootPath, folder.Directory.FullName); yield return(new ModMetadata(folder.DisplayName, folder.Directory.FullName, relativePath, manifest, dataRecord, isIgnored: !folder.ShouldBeLoaded) .SetStatus(status, !folder.ShouldBeLoaded ? "disabled by dot convention" : folder.ManifestParseError)); } }
/********* ** Public methods *********/ /// <summary>Get manifest metadata for each folder in the given root path.</summary> /// <param name="toolkit">The mod toolkit.</param> /// <param name="rootPath">The root path to search for mods.</param> /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param> /// <returns>Returns the manifests by relative folder.</returns> public IEnumerable <IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase) { foreach (ModFolder folder in toolkit.GetModFolders(rootPath)) { Manifest manifest = folder.Manifest; // parse internal data record (if any) ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest); // apply defaults if (manifest != null && dataRecord != null) { if (dataRecord.UpdateKey != null) { manifest.UpdateKeys = new[] { dataRecord.UpdateKey } } ; } // build metadata ModMetadataStatus status = folder.ManifestParseError == null ? ModMetadataStatus.Found : ModMetadataStatus.Failed; yield return(new ModMetadata(folder.DisplayName, folder.Directory.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError)); } }
/// <summary>Set up a mock mod metadata for <see cref="ModResolver.ValidateManifests"/>.</summary> /// <param name="mod">The mock mod metadata.</param> /// <param name="modRecord">The extra metadata about the mod from SMAPI's internal data (if any).</param> private void SetupMetadataForValidation(Mock <IModMetadata> mod, ModDataRecordVersionedFields modRecord = null) { mod.Setup(p => p.Status).Returns(ModMetadataStatus.Found); mod.Setup(p => p.DataRecord).Returns(() => null); mod.Setup(p => p.Manifest).Returns(this.GetManifest()); mod.Setup(p => p.DirectoryPath).Returns(Path.GetTempPath()); mod.Setup(p => p.DataRecord).Returns(modRecord); }
/// <summary>Set up a mock mod metadata for <see cref="ModResolver.ValidateManifests"/>.</summary> /// <param name="mod">The mock mod metadata.</param> /// <param name="modRecord">The extra metadata about the mod from SMAPI's internal data (if any).</param> private void SetupMetadataForValidation(Mock <IModMetadata> mod, ModDataRecordVersionedFields modRecord = null) { mod.Setup(p => p.Status).Returns(ModMetadataStatus.Found); mod.Setup(p => p.DataRecord).Returns(() => null); mod.Setup(p => p.Manifest).Returns(this.GetManifest()); mod.Setup(p => p.DirectoryPath).Returns(Path.GetTempPath()); mod.Setup(p => p.DataRecord).Returns(modRecord); mod.Setup(p => p.GetUpdateKeys(It.IsAny <bool>())).Returns(Enumerable.Empty <UpdateKey>()); }
/********* ** Public methods *********/ /// <summary>Get manifest metadata for each folder in the given root path.</summary> /// <param name="toolkit">The mod toolkit.</param> /// <param name="rootPath">The root path to search for mods.</param> /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param> /// <returns>Returns the manifests by relative folder.</returns> public IEnumerable <IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase) { foreach (ModFolder folder in toolkit.GetModFolders(rootPath)) { Manifest manifest = folder.Manifest; // parse internal data record (if any) ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest); // get display name string displayName = manifest?.Name; if (string.IsNullOrWhiteSpace(displayName)) { displayName = dataRecord?.DisplayName; } if (string.IsNullOrWhiteSpace(displayName)) { displayName = PathUtilities.GetRelativePath(rootPath, folder.ActualDirectory?.FullName ?? folder.SearchDirectory.FullName); } // apply defaults if (manifest != null && dataRecord != null) { if (dataRecord.UpdateKey != null) { manifest.UpdateKeys = new[] { dataRecord.UpdateKey } } ; } // build metadata ModMetadataStatus status = folder.ManifestParseError == null ? ModMetadataStatus.Found : ModMetadataStatus.Failed; yield return(new ModMetadata(displayName, folder.ActualDirectory?.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError)); } }
public bool GetUpdateStatuses(out IList <ModStatus> statuses) { statuses = new List <ModStatus>(); object registry = this.helper.ModRegistry.GetType() .GetField("Registry", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this.helper.ModRegistry); bool addedNonSkippedStatus = false; foreach (object modMetaData in (IEnumerable <object>)registry.GetType().GetField("Mods", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(registry)) { ModEntryModel result = GetInstanceProperty <ModEntryModel>(modMetaData, "UpdateCheckData"); IManifest manifest = GetInstanceProperty <IManifest>(modMetaData, "Manifest"); if (result == null) { statuses.Add(new ModStatus(UpdateStatus.Skipped, manifest, "", null, "SMAPI didn't check for an update")); continue; } if (!(bool)modMetaData.GetType().GetMethod("HasValidUpdateKeys").Invoke(modMetaData, null)) { statuses.Add(new ModStatus(UpdateStatus.Skipped, manifest, "", null, "Mod has no update keys")); continue; } ModDataRecordVersionedFields dataRecord = GetInstanceProperty <ModDataRecordVersionedFields>(modMetaData, "DataRecord"); //This section largely taken from https://github.com/Pathoschild/SMAPI/blob/924c3a5d3fe6bfad483834112883156bdf202b57/src/SMAPI/Framework/SCore.cs#L618-L630 bool useBetaInfo = result.HasBetaInfo && Constants.ApiVersion.IsPrerelease(); ISemanticVersion localVersion = dataRecord?.GetLocalVersionForUpdateChecks(manifest.Version) ?? manifest.Version; ISemanticVersion latestVersion = dataRecord?.GetRemoteVersionForUpdateChecks(result.Main?.Version) ?? result.Main?.Version; ISemanticVersion optionalVersion = dataRecord?.GetRemoteVersionForUpdateChecks(result.Optional?.Version) ?? result.Optional?.Version; ISemanticVersion unofficialVersion = useBetaInfo ? result.UnofficialForBeta?.Version : result.Unofficial?.Version; if (this.IsValidUpdate(localVersion, latestVersion, useBetaChannel: true)) { statuses.Add(new ModStatus(UpdateStatus.OutOfDate, manifest, result.Main?.Url, latestVersion.ToString())); } else if (this.IsValidUpdate(localVersion, optionalVersion, useBetaChannel: localVersion.IsPrerelease())) { statuses.Add(new ModStatus(UpdateStatus.OutOfDate, manifest, result.Optional?.Url, optionalVersion.ToString())); } else if (this.IsValidUpdate(localVersion, unofficialVersion, useBetaChannel: GetEnumName(modMetaData, "Status") == "Failed")) { statuses.Add(new ModStatus(UpdateStatus.OutOfDate, manifest, useBetaInfo ? result.UnofficialForBeta?.Url : result.Unofficial?.Url, unofficialVersion.ToString())); } else { string updateURL = null; UpdateStatus updateStatus = UpdateStatus.UpToDate; if (localVersion.Equals(latestVersion)) { updateURL = result.Main?.Url; } else if (localVersion.Equals(optionalVersion)) { updateURL = result.Optional?.Url; } else if (localVersion.Equals(unofficialVersion)) { updateURL = useBetaInfo ? result.UnofficialForBeta?.Url : result.Unofficial?.Url; } else if (latestVersion != null && this.IsValidUpdate(latestVersion, localVersion, useBetaChannel: true)) { updateURL = result.Main?.Url; updateStatus = UpdateStatus.VeryNew; } else if (optionalVersion != null && this.IsValidUpdate(optionalVersion, localVersion, useBetaChannel: localVersion.IsPrerelease())) { updateURL = result.Optional?.Url; updateStatus = UpdateStatus.VeryNew; } else if (unofficialVersion != null && this.IsValidUpdate(unofficialVersion, localVersion, useBetaChannel: GetEnumName(modMetaData, "Status") == "Failed")) { updateURL = useBetaInfo ? result.UnofficialForBeta?.Url : result.Unofficial?.Url; updateStatus = UpdateStatus.VeryNew; } if (updateURL != null) { statuses.Add(new ModStatus(updateStatus, manifest, updateURL)); } else if (result.Errors != null && result.Errors.Any()) { statuses.Add(new ModStatus(UpdateStatus.Error, manifest, "", "", result.Errors[0])); } else { statuses.Add(new ModStatus(UpdateStatus.Error, manifest, "", "", "Unknown Error")); } } addedNonSkippedStatus = true; } return(addedNonSkippedStatus); }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="displayName">The mod's display name.</param> /// <param name="directoryPath">The mod's full directory path within the <paramref name="rootPath"/>.</param> /// <param name="rootPath">The root path containing mods.</param> /// <param name="manifest">The mod manifest.</param> /// <param name="dataRecord">Metadata about the mod from SMAPI's internal data (if any).</param> /// <param name="isIgnored">Whether the mod folder should be ignored. This should be <c>true</c> if it was found within a folder whose name starts with a dot.</param> public ModMetadata(string displayName, string directoryPath, string rootPath, IManifest manifest, ModDataRecordVersionedFields dataRecord, bool isIgnored) { this.DisplayName = displayName; this.DirectoryPath = directoryPath; this.RootPath = rootPath; this.RelativeDirectoryPath = PathUtilities.GetRelativePath(this.RootPath, this.DirectoryPath); this.Manifest = manifest; this.DataRecord = dataRecord; this.IsIgnored = isIgnored; }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="displayName">The mod's display name.</param> /// <param name="directoryPath">The mod's full directory path.</param> /// <param name="manifest">The mod manifest.</param> /// <param name="dataRecord">Metadata about the mod from SMAPI's internal data (if any).</param> public ModMetadata(string displayName, string directoryPath, IManifest manifest, ModDataRecordVersionedFields dataRecord) { this.DisplayName = displayName; this.DirectoryPath = directoryPath; this.Manifest = manifest; this.DataRecord = dataRecord; }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="displayName">The mod's display name.</param> /// <param name="directoryPath">The mod's full directory path within the <paramref name="rootPath"/>.</param> /// <param name="rootPath">The root path containing mods.</param> /// <param name="manifest">The mod manifest.</param> /// <param name="dataRecord">Metadata about the mod from SMAPI's internal data (if any).</param> /// <param name="isIgnored">Whether the mod folder should be ignored. This should be <c>true</c> if it was found within a folder whose name starts with a dot.</param> public ModMetadata(string displayName, string directoryPath, string rootPath, IManifest manifest, ModDataRecordVersionedFields dataRecord, bool isIgnored) { this.DisplayName = displayName; this.DirectoryPath = directoryPath; this.RootPath = rootPath; this.RelativeDirectoryPath = PathUtilities.GetRelativePath(this.RootPath, this.DirectoryPath); this.Manifest = manifest; this.DataRecord = dataRecord; this.IsIgnored = isIgnored; this.Dependencies = new Lazy <IDictionary <string, bool> >(this.ExtractDependencies); }