Merge pull request #543 from danvolchek/optional-update-checking
Add version info to IModMetadata, Change update checks to consider preview versions
This commit is contained in:
commit
4a05cd09b6
|
@ -1,5 +1,6 @@
|
||||||
using StardewModdingAPI.Framework.ModData;
|
using StardewModdingAPI.Framework.ModData;
|
||||||
using StardewModdingAPI.Framework.ModLoading;
|
using StardewModdingAPI.Framework.ModLoading;
|
||||||
|
using StardewModdingAPI.Framework.ModUpdateChecking;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework
|
namespace StardewModdingAPI.Framework
|
||||||
{
|
{
|
||||||
|
@ -45,6 +46,11 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>Whether the mod is a content pack.</summary>
|
/// <summary>Whether the mod is a content pack.</summary>
|
||||||
bool IsContentPack { get; }
|
bool IsContentPack { get; }
|
||||||
|
|
||||||
|
/// <summary>The update status of this mod (if any).</summary>
|
||||||
|
ModUpdateStatus UpdateStatus { get; }
|
||||||
|
|
||||||
|
/// <summary>The preview update status of this mod (if any).</summary>
|
||||||
|
ModUpdateStatus PreviewUpdateStatus { get; }
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
|
@ -72,6 +78,14 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <param name="api">The mod-provided API.</param>
|
/// <param name="api">The mod-provided API.</param>
|
||||||
IModMetadata SetApi(object api);
|
IModMetadata SetApi(object api);
|
||||||
|
|
||||||
|
/// <summary>Set the update status.</summary>
|
||||||
|
/// <param name="updateStatus">The mod update status.</param>
|
||||||
|
IModMetadata SetUpdateStatus(ModUpdateStatus updateStatus);
|
||||||
|
|
||||||
|
/// <summary>Set the preview update status.</summary>
|
||||||
|
/// <param name="previewUpdateStatus">The mod preview update status.</param>
|
||||||
|
IModMetadata SetPreviewUpdateStatus(ModUpdateStatus previewUpdateStatus);
|
||||||
|
|
||||||
/// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
|
/// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
|
||||||
bool HasManifest();
|
bool HasManifest();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using StardewModdingAPI.Framework.ModData;
|
using StardewModdingAPI.Framework.ModData;
|
||||||
|
using StardewModdingAPI.Framework.ModUpdateChecking;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework.ModLoading
|
namespace StardewModdingAPI.Framework.ModLoading
|
||||||
{
|
{
|
||||||
|
@ -43,6 +44,12 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
/// <summary>The mod-provided API (if any).</summary>
|
/// <summary>The mod-provided API (if any).</summary>
|
||||||
public object Api { get; private set; }
|
public object Api { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>The update status of this mod (if any).</summary>
|
||||||
|
public ModUpdateStatus UpdateStatus { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>The preview update status of this mod (if any).</summary>
|
||||||
|
public ModUpdateStatus PreviewUpdateStatus { get; private set; }
|
||||||
|
|
||||||
/// <summary>Whether the mod is a content pack.</summary>
|
/// <summary>Whether the mod is a content pack.</summary>
|
||||||
public bool IsContentPack => this.Manifest?.ContentPackFor != null;
|
public bool IsContentPack => this.Manifest?.ContentPackFor != null;
|
||||||
|
|
||||||
|
@ -115,6 +122,22 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Set the update status.</summary>
|
||||||
|
/// <param name="updateStatus">The mod update status.</param>
|
||||||
|
public IModMetadata SetUpdateStatus(ModUpdateStatus updateStatus)
|
||||||
|
{
|
||||||
|
this.UpdateStatus = updateStatus;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Set the preview update status.</summary>
|
||||||
|
/// <param name="previewUpdateStatus">The mod preview update status.</param>
|
||||||
|
public IModMetadata SetPreviewUpdateStatus(ModUpdateStatus previewUpdateStatus)
|
||||||
|
{
|
||||||
|
this.PreviewUpdateStatus = previewUpdateStatus;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
|
/// <summary>Whether the mod manifest was loaded (regardless of whether the mod itself was loaded).</summary>
|
||||||
public bool HasManifest()
|
public bool HasManifest()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
namespace StardewModdingAPI.Framework.ModUpdateChecking
|
||||||
|
{
|
||||||
|
/// <summary>Update status for a mod.</summary>
|
||||||
|
internal class ModUpdateStatus
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Accessors
|
||||||
|
*********/
|
||||||
|
/// <summary>The version that this mod can be updated to (if any).</summary>
|
||||||
|
public ISemanticVersion Version { get; }
|
||||||
|
|
||||||
|
/// <summary>The error checking for updates of this mod (if any).</summary>
|
||||||
|
public string Error { get; }
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="version">The version that this mod can be update to.</param>
|
||||||
|
public ModUpdateStatus(ISemanticVersion version)
|
||||||
|
{
|
||||||
|
this.Version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="error">The error checking for updates of this mod.</param>
|
||||||
|
public ModUpdateStatus(string error)
|
||||||
|
{
|
||||||
|
this.Error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
public ModUpdateStatus()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ using StardewModdingAPI.Framework.ModData;
|
||||||
using StardewModdingAPI.Framework.Models;
|
using StardewModdingAPI.Framework.Models;
|
||||||
using StardewModdingAPI.Framework.ModHelpers;
|
using StardewModdingAPI.Framework.ModHelpers;
|
||||||
using StardewModdingAPI.Framework.ModLoading;
|
using StardewModdingAPI.Framework.ModLoading;
|
||||||
|
using StardewModdingAPI.Framework.ModUpdateChecking;
|
||||||
using StardewModdingAPI.Framework.Patching;
|
using StardewModdingAPI.Framework.Patching;
|
||||||
using StardewModdingAPI.Framework.Reflection;
|
using StardewModdingAPI.Framework.Reflection;
|
||||||
using StardewModdingAPI.Framework.Serialisation;
|
using StardewModdingAPI.Framework.Serialisation;
|
||||||
|
@ -662,7 +663,7 @@ namespace StardewModdingAPI
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
// extract latest versions
|
// extract latest versions
|
||||||
IDictionary<IModMetadata, ModInfoModel> updatesByMod = new Dictionary<IModMetadata, ModInfoModel>();
|
IDictionary<IModMetadata, Tuple<ModInfoModel, bool>> updatesByMod = new Dictionary<IModMetadata, Tuple<ModInfoModel, bool>>();
|
||||||
foreach (var result in results)
|
foreach (var result in results)
|
||||||
{
|
{
|
||||||
IModMetadata mod = result.Mod;
|
IModMetadata mod = result.Mod;
|
||||||
|
@ -671,35 +672,65 @@ namespace StardewModdingAPI
|
||||||
// handle error
|
// handle error
|
||||||
if (remoteInfo.Error != null)
|
if (remoteInfo.Error != null)
|
||||||
{
|
{
|
||||||
|
if (mod.UpdateStatus?.Version == null)
|
||||||
|
mod.SetUpdateStatus(new ModUpdateStatus(remoteInfo.Error));
|
||||||
|
if (mod.PreviewUpdateStatus?.Version == null)
|
||||||
|
mod.SetUpdateStatus(new ModUpdateStatus(remoteInfo.Error));
|
||||||
|
|
||||||
this.Monitor.Log($" {mod.DisplayName} ({result.Key}): update error: {remoteInfo.Error}", LogLevel.Trace);
|
this.Monitor.Log($" {mod.DisplayName} ({result.Key}): update error: {remoteInfo.Error}", LogLevel.Trace);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalise versions
|
// normalise versions
|
||||||
ISemanticVersion localVersion = mod.DataRecord?.GetLocalVersionForUpdateChecks(mod.Manifest.Version) ?? mod.Manifest.Version;
|
ISemanticVersion localVersion = mod.DataRecord?.GetLocalVersionForUpdateChecks(mod.Manifest.Version) ?? mod.Manifest.Version;
|
||||||
if (!SemanticVersion.TryParse(mod.DataRecord?.GetRemoteVersionForUpdateChecks(remoteInfo.Version) ?? remoteInfo.Version, out ISemanticVersion remoteVersion))
|
bool validVersion = SemanticVersion.TryParse(mod.DataRecord?.GetRemoteVersionForUpdateChecks(remoteInfo.Version) ?? remoteInfo.Version, out ISemanticVersion remoteVersion);
|
||||||
|
bool validPreviewVersion = SemanticVersion.TryParse(remoteInfo.PreviewVersion, out ISemanticVersion remotePreviewVersion);
|
||||||
|
|
||||||
|
if (!validVersion && mod.UpdateStatus?.Version == null)
|
||||||
|
mod.SetUpdateStatus(new ModUpdateStatus($"Version is invalid: {remoteInfo.Version}"));
|
||||||
|
if (!validPreviewVersion && mod.PreviewUpdateStatus?.Version == null)
|
||||||
|
mod.SetPreviewUpdateStatus(new ModUpdateStatus($"Version is invalid: {remoteInfo.PreviewVersion}"));
|
||||||
|
|
||||||
|
if (!validVersion && !validPreviewVersion)
|
||||||
{
|
{
|
||||||
this.Monitor.Log($" {mod.DisplayName} ({result.Key}): update error: Mod has invalid version {remoteInfo.Version}", LogLevel.Trace);
|
this.Monitor.Log($" {mod.DisplayName} ({result.Key}): update error: Mod has invalid versions. version: {remoteInfo.Version}, preview version: {remoteInfo.PreviewVersion}", LogLevel.Trace);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare versions
|
// compare versions
|
||||||
bool isUpdate = remoteVersion.IsNewerThan(localVersion);
|
bool isPreviewUpdate = validPreviewVersion && localVersion.IsNewerThan(remoteVersion) && remotePreviewVersion.IsNewerThan(localVersion);
|
||||||
this.VerboseLog($" {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {remoteInfo.Version}" : "okay")}.");
|
bool isUpdate = (validVersion && remoteVersion.IsNewerThan(localVersion)) || isPreviewUpdate;
|
||||||
|
|
||||||
|
this.VerboseLog($" {mod.DisplayName} ({result.Key}): {(isUpdate ? $"{mod.Manifest.Version}{(!localVersion.Equals(mod.Manifest.Version) ? $" [{localVersion}]" : "")} => {(isPreviewUpdate ? remoteInfo.PreviewVersion : remoteInfo.Version)}" : "okay")}.");
|
||||||
if (isUpdate)
|
if (isUpdate)
|
||||||
{
|
{
|
||||||
if (!updatesByMod.TryGetValue(mod, out ModInfoModel other) || remoteVersion.IsNewerThan(other.Version))
|
if (!updatesByMod.TryGetValue(mod, out Tuple<ModInfoModel, bool> other) || (isPreviewUpdate ? remotePreviewVersion : remoteVersion).IsNewerThan(other.Item2 ? other.Item1.PreviewVersion : other.Item1.Version))
|
||||||
updatesByMod[mod] = remoteInfo;
|
{
|
||||||
|
updatesByMod[mod] = new Tuple<ModInfoModel, bool>(remoteInfo, isPreviewUpdate);
|
||||||
|
|
||||||
|
if (isPreviewUpdate)
|
||||||
|
mod.SetPreviewUpdateStatus(new ModUpdateStatus(remotePreviewVersion));
|
||||||
|
else
|
||||||
|
mod.SetUpdateStatus(new ModUpdateStatus(remoteVersion));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set mods to have no updates
|
||||||
|
foreach (IModMetadata mod in results.Select(item => item.Mod)
|
||||||
|
.Where(item => !updatesByMod.ContainsKey(item)))
|
||||||
|
{
|
||||||
|
mod.SetUpdateStatus(new ModUpdateStatus());
|
||||||
|
mod.SetPreviewUpdateStatus(new ModUpdateStatus());
|
||||||
|
}
|
||||||
|
|
||||||
// output
|
// output
|
||||||
if (updatesByMod.Any())
|
if (updatesByMod.Any())
|
||||||
{
|
{
|
||||||
this.Monitor.Newline();
|
this.Monitor.Newline();
|
||||||
this.Monitor.Log($"You can update {updatesByMod.Count} mod{(updatesByMod.Count != 1 ? "s" : "")}:", LogLevel.Alert);
|
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))
|
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
|
else
|
||||||
this.Monitor.Log(" All mods up to date.", LogLevel.Trace);
|
this.Monitor.Log(" All mods up to date.", LogLevel.Trace);
|
||||||
|
|
|
@ -110,6 +110,7 @@
|
||||||
<Compile Include="Framework\ContentManagers\IContentManager.cs" />
|
<Compile Include="Framework\ContentManagers\IContentManager.cs" />
|
||||||
<Compile Include="Framework\ContentManagers\ModContentManager.cs" />
|
<Compile Include="Framework\ContentManagers\ModContentManager.cs" />
|
||||||
<Compile Include="Framework\Models\ModFolderExport.cs" />
|
<Compile Include="Framework\Models\ModFolderExport.cs" />
|
||||||
|
<Compile Include="Framework\ModUpdateChecking\ModUpdateStatus.cs" />
|
||||||
<Compile Include="Framework\Patching\GamePatcher.cs" />
|
<Compile Include="Framework\Patching\GamePatcher.cs" />
|
||||||
<Compile Include="Framework\Patching\IHarmonyPatch.cs" />
|
<Compile Include="Framework\Patching\IHarmonyPatch.cs" />
|
||||||
<Compile Include="Framework\Serialisation\ColorConverter.cs" />
|
<Compile Include="Framework\Serialisation\ColorConverter.cs" />
|
||||||
|
|
Loading…
Reference in New Issue