diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index c0d6408d..673c5b2e 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -45,6 +45,14 @@ namespace StardewModdingAPI.Framework
/// Whether the mod is a content pack.
bool IsContentPack { get; }
+ /// The latest version of the mod.
+ ISemanticVersion LatestVersion { get; }
+
+ /// The latest preview version of the mod, if any.
+ ISemanticVersion LatestPreviewVersion { get; }
+
+ /// The error checking for updates for this mod, if any.
+ string UpdateCheckError { get; }
/*********
** Public methods
@@ -72,6 +80,15 @@ namespace StardewModdingAPI.Framework
/// The mod-provided API.
IModMetadata SetApi(object api);
+ /// Set the update status, indicating no errors happened.
+ /// The latest version.
+ /// The latest preview version.
+ IModMetadata SetUpdateStatus(ISemanticVersion latestVersion, ISemanticVersion latestPreviewVersion);
+
+ /// Set the update status, indicating an error happened.
+ /// The error checking for updates, if any.
+ IModMetadata SetUpdateStatus(string updateCheckError);
+
/// Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).
bool HasManifest();
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index e4ec7e3b..8f544da3 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -43,6 +43,15 @@ namespace StardewModdingAPI.Framework.ModLoading
/// The mod-provided API (if any).
public object Api { get; private set; }
+ /// The latest version of the mod.
+ public ISemanticVersion LatestVersion { get; private set; }
+
+ /// The latest preview version of the mod, if any.
+ public ISemanticVersion LatestPreviewVersion { get; private set; }
+
+ /// The error checking for updates for this mod, if any.
+ public string UpdateCheckError { get; private set; }
+
/// Whether the mod is a content pack.
public bool IsContentPack => this.Manifest?.ContentPackFor != null;
@@ -115,6 +124,24 @@ namespace StardewModdingAPI.Framework.ModLoading
return this;
}
+ /// Set the update status.
+ /// The latest version.
+ /// The latest preview version.
+ public IModMetadata SetUpdateStatus(ISemanticVersion latestVersion, ISemanticVersion latestPreviewVersion)
+ {
+ this.LatestVersion = latestVersion;
+ this.LatestPreviewVersion = latestPreviewVersion;
+ return this;
+ }
+
+ // Set the update status, indicating an error happened.
+ /// The error checking for updates, if any.
+ public IModMetadata SetUpdateStatus(string updateCheckError)
+ {
+ this.UpdateCheckError = updateCheckError;
+ return this;
+ }
+
/// Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).
public bool HasManifest()
{
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index 8c6cea1e..dc034331 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -662,7 +662,7 @@ namespace StardewModdingAPI
.ToArray();
// extract latest versions
- IDictionary updatesByMod = new Dictionary();
+ IDictionary> updatesByMod = new Dictionary>();
foreach (var result in results)
{
IModMetadata mod = result.Mod;
@@ -671,6 +671,7 @@ namespace StardewModdingAPI
// handle error
if (remoteInfo.Error != null)
{
+ mod.SetUpdateStatus(remoteInfo.Error);
this.Monitor.Log($" {mod.DisplayName} ({result.Key}): update error: {remoteInfo.Error}", LogLevel.Trace);
continue;
}
@@ -679,17 +680,25 @@ namespace StardewModdingAPI
ISemanticVersion localVersion = mod.DataRecord?.GetLocalVersionForUpdateChecks(mod.Manifest.Version) ?? mod.Manifest.Version;
if (!SemanticVersion.TryParse(mod.DataRecord?.GetRemoteVersionForUpdateChecks(remoteInfo.Version) ?? remoteInfo.Version, out ISemanticVersion remoteVersion))
{
- this.Monitor.Log($" {mod.DisplayName} ({result.Key}): update error: Mod has invalid version {remoteInfo.Version}", LogLevel.Trace);
+ string errorInfo = $"Mod has invalid version {remoteInfo.Version}";
+
+ mod.SetUpdateStatus(errorInfo);
+ this.Monitor.Log($" {mod.DisplayName} ({result.Key}): update error: {errorInfo}", LogLevel.Trace);
continue;
}
+ SemanticVersion.TryParse(remoteInfo.PreviewVersion, out ISemanticVersion remotePreviewVersion);
+ mod.SetUpdateStatus(remoteVersion, remotePreviewVersion);
+
// compare versions
- bool isUpdate = remoteVersion.IsNewerThan(localVersion);
- this.VerboseLog($" {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {remoteInfo.Version}" : "okay")}.");
+ bool isNonPreviewUpdate = remoteVersion.IsNewerThan(localVersion);
+ bool isUpdate = isNonPreviewUpdate ||
+ (localVersion.IsNewerThan(remoteVersion) && remotePreviewVersion.IsNewerThan(localVersion));
+ this.VerboseLog($" {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {(isNonPreviewUpdate ? remoteInfo.Version : remoteInfo.PreviewVersion)}" : "okay")}.");
if (isUpdate)
{
- if (!updatesByMod.TryGetValue(mod, out ModInfoModel other) || remoteVersion.IsNewerThan(other.Version))
- updatesByMod[mod] = remoteInfo;
+ if (!updatesByMod.TryGetValue(mod, out Tuple other) || (isNonPreviewUpdate ? remoteVersion : remotePreviewVersion).IsNewerThan(other.Item2 ? other.Item1.PreviewVersion : other.Item1.Version))
+ updatesByMod[mod] = new Tuple(remoteInfo, !isNonPreviewUpdate);
}
}
@@ -699,7 +708,7 @@ namespace StardewModdingAPI
this.Monitor.Newline();
this.Monitor.Log($"You can update {updatesByMod.Count} mod{(updatesByMod.Count != 1 ? "s" : "")}:", LogLevel.Alert);
foreach (var entry in updatesByMod.OrderBy(p => p.Key.DisplayName))
- this.Monitor.Log($" {entry.Key.DisplayName} {entry.Value.Version}: {entry.Value.Url}", LogLevel.Alert);
+ this.Monitor.Log($" {entry.Key.DisplayName} {(entry.Value.Item2 ? entry.Value.Item1.PreviewVersion : entry.Value.Item1.Version)}: {entry.Value.Item1.Url}", LogLevel.Alert);
}
else
this.Monitor.Log(" All mods up to date.", LogLevel.Trace);