summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'src/StardewModdingAPI/Framework')
-rw-r--r--src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs19
-rw-r--r--src/StardewModdingAPI/Framework/InternalExtensions.cs21
-rw-r--r--src/StardewModdingAPI/Framework/ModAssemblyLoader.cs7
-rw-r--r--src/StardewModdingAPI/Framework/ModRegistry.cs40
-rw-r--r--src/StardewModdingAPI/Framework/Models/IncompatibleMod.cs22
-rw-r--r--src/StardewModdingAPI/Framework/Monitor.cs33
6 files changed, 119 insertions, 23 deletions
diff --git a/src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs b/src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs
index a747eaa8..4c3b86fe 100644
--- a/src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs
+++ b/src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs
@@ -1,4 +1,5 @@
using System.IO;
+using StardewModdingAPI.AssemblyRewriters;
namespace StardewModdingAPI.Framework.AssemblyRewriting
{
@@ -14,6 +15,12 @@ namespace StardewModdingAPI.Framework.AssemblyRewriting
/// <summary>The SMAPI version used to rewrite the assembly.</summary>
public readonly string ApiVersion;
+ /// <summary>The target platform.</summary>
+ public readonly Platform Platform;
+
+ /// <summary>The <see cref="System.Environment.MachineName"/> value for the machine used to rewrite the assembly.</summary>
+ public readonly string MachineName;
+
/// <summary>Whether to use the cached assembly instead of the original assembly.</summary>
public readonly bool UseCachedAssembly;
@@ -24,11 +31,15 @@ namespace StardewModdingAPI.Framework.AssemblyRewriting
/// <summary>Construct an instance.</summary>
/// <param name="hash">The MD5 hash for the original assembly.</param>
/// <param name="apiVersion">The SMAPI version used to rewrite the assembly.</param>
+ /// <param name="platform">The target platform.</param>
+ /// <param name="machineName">The <see cref="System.Environment.MachineName"/> value for the machine used to rewrite the assembly.</param>
/// <param name="useCachedAssembly">Whether to use the cached assembly instead of the original assembly.</param>
- public CacheEntry(string hash, string apiVersion, bool useCachedAssembly)
+ public CacheEntry(string hash, string apiVersion, Platform platform, string machineName, bool useCachedAssembly)
{
this.Hash = hash;
this.ApiVersion = apiVersion;
+ this.Platform = platform;
+ this.MachineName = machineName;
this.UseCachedAssembly = useCachedAssembly;
}
@@ -36,10 +47,14 @@ namespace StardewModdingAPI.Framework.AssemblyRewriting
/// <param name="paths">The paths for the cached assembly.</param>
/// <param name="hash">The MD5 hash of the original assembly.</param>
/// <param name="currentVersion">The current SMAPI version.</param>
- public bool IsUpToDate(CachePaths paths, string hash, ISemanticVersion currentVersion)
+ /// <param name="platform">The target platform.</param>
+ /// <param name="machineName">The <see cref="System.Environment.MachineName"/> value for the machine reading the assembly.</param>
+ public bool IsUpToDate(CachePaths paths, string hash, ISemanticVersion currentVersion, Platform platform, string machineName)
{
return hash == this.Hash
&& this.ApiVersion == currentVersion.ToString()
+ && this.Platform == platform
+ && this.MachineName == machineName
&& (!this.UseCachedAssembly || File.Exists(paths.Assembly));
}
}
diff --git a/src/StardewModdingAPI/Framework/InternalExtensions.cs b/src/StardewModdingAPI/Framework/InternalExtensions.cs
index 415785d9..c4bd2d35 100644
--- a/src/StardewModdingAPI/Framework/InternalExtensions.cs
+++ b/src/StardewModdingAPI/Framework/InternalExtensions.cs
@@ -86,5 +86,26 @@ namespace StardewModdingAPI.Framework
// anything else
return exception.ToString();
}
+
+ /****
+ ** Deprecation
+ ****/
+ /// <summary>Log a deprecation warning for mods using an event.</summary>
+ /// <param name="deprecationManager">The deprecation manager to extend.</param>
+ /// <param name="handlers">The event handlers.</param>
+ /// <param name="nounPhrase">A noun phrase describing what is deprecated.</param>
+ /// <param name="version">The SMAPI version which deprecated it.</param>
+ /// <param name="severity">How deprecated the code is.</param>
+ public static void WarnForEvent(this DeprecationManager deprecationManager, Delegate[] handlers, string nounPhrase, string version, DeprecationLevel severity)
+ {
+ if (handlers == null || !handlers.Any())
+ return;
+
+ foreach (Delegate handler in handlers)
+ {
+ string modName = Program.ModRegistry.GetModFrom(handler) ?? "an unknown mod"; // suppress stack trace for unknown mods, not helpful here
+ deprecationManager.Warn(modName, nounPhrase, version, severity);
+ }
+ }
}
}
diff --git a/src/StardewModdingAPI/Framework/ModAssemblyLoader.cs b/src/StardewModdingAPI/Framework/ModAssemblyLoader.cs
index a2c4ac23..e4760398 100644
--- a/src/StardewModdingAPI/Framework/ModAssemblyLoader.cs
+++ b/src/StardewModdingAPI/Framework/ModAssemblyLoader.cs
@@ -29,6 +29,8 @@ namespace StardewModdingAPI.Framework
/// <summary>Encapsulates monitoring and logging.</summary>
private readonly IMonitor Monitor;
+ /// <summary>The current game platform.</summary>
+ private readonly Platform TargetPlatform;
/*********
** Public methods
@@ -40,6 +42,7 @@ namespace StardewModdingAPI.Framework
public ModAssemblyLoader(string cacheDirName, Platform targetPlatform, IMonitor monitor)
{
this.CacheDirName = cacheDirName;
+ this.TargetPlatform = targetPlatform;
this.Monitor = monitor;
this.AssemblyMap = Constants.GetAssemblyMap(targetPlatform);
this.AssemblyTypeRewriter = new AssemblyTypeRewriter(this.AssemblyMap, monitor);
@@ -58,7 +61,7 @@ namespace StardewModdingAPI.Framework
CachePaths cachePaths = this.GetCachePaths(assemblyPath);
{
CacheEntry cacheEntry = File.Exists(cachePaths.Metadata) ? JsonConvert.DeserializeObject<CacheEntry>(File.ReadAllText(cachePaths.Metadata)) : null;
- if (cacheEntry != null && cacheEntry.IsUpToDate(cachePaths, hash, Constants.ApiVersion))
+ if (cacheEntry != null && cacheEntry.IsUpToDate(cachePaths, hash, Constants.ApiVersion, this.TargetPlatform, Environment.MachineName))
return new RewriteResult(assemblyPath, cachePaths, assemblyBytes, cacheEntry.Hash, cacheEntry.UseCachedAssembly, isNewerThanCache: false); // no rewrite needed
}
this.Monitor.Log($"Preprocessing {Path.GetFileName(assemblyPath)} for compatibility...", LogLevel.Trace);
@@ -99,7 +102,7 @@ namespace StardewModdingAPI.Framework
// cache all results
foreach (RewriteResult result in results)
{
- CacheEntry cacheEntry = new CacheEntry(result.Hash, Constants.ApiVersion.ToString(), forceCacheAssemblies || result.UseCachedAssembly);
+ CacheEntry cacheEntry = new CacheEntry(result.Hash, Constants.ApiVersion.ToString(), this.TargetPlatform, Environment.MachineName, forceCacheAssemblies || result.UseCachedAssembly);
File.WriteAllText(result.CachePaths.Metadata, JsonConvert.SerializeObject(cacheEntry));
if (forceCacheAssemblies || result.UseCachedAssembly)
File.WriteAllBytes(result.CachePaths.Assembly, result.AssemblyBytes);
diff --git a/src/StardewModdingAPI/Framework/ModRegistry.cs b/src/StardewModdingAPI/Framework/ModRegistry.cs
index b593142d..51ec7123 100644
--- a/src/StardewModdingAPI/Framework/ModRegistry.cs
+++ b/src/StardewModdingAPI/Framework/ModRegistry.cs
@@ -36,6 +36,34 @@ namespace StardewModdingAPI.Framework
return (from mod in this.Mods select mod);
}
+ /// <summary>Get the friendly mod name which handles a delegate.</summary>
+ /// <param name="delegate">The delegate to follow.</param>
+ /// <returns>Returns the mod name, or <c>null</c> if the delegate isn't implemented by a known mod.</returns>
+ public string GetModFrom(Delegate @delegate)
+ {
+ return @delegate?.Target != null
+ ? this.GetModFrom(@delegate.Target.GetType())
+ : null;
+ }
+
+ /// <summary>Get the friendly mod name which defines a type.</summary>
+ /// <param name="type">The type to check.</param>
+ /// <returns>Returns the mod name, or <c>null</c> if the type isn't part of a known mod.</returns>
+ public string GetModFrom(Type type)
+ {
+ // null
+ if (type == null)
+ return null;
+
+ // known type
+ string assemblyName = type.Assembly.FullName;
+ if (this.ModNamesByAssembly.ContainsKey(assemblyName))
+ return this.ModNamesByAssembly[assemblyName];
+
+ // not found
+ return null;
+ }
+
/// <summary>Get the friendly name for the closest assembly registered as a source of deprecation warnings.</summary>
/// <returns>Returns the source name, or <c>null</c> if no registered assemblies were found.</returns>
public string GetModFromStack()
@@ -49,16 +77,10 @@ namespace StardewModdingAPI.Framework
// search stack for a source assembly
foreach (StackFrame frame in frames)
{
- // get assembly name
MethodBase method = frame.GetMethod();
- Type type = method.ReflectedType;
- if (type == null)
- continue;
- string assemblyName = type.Assembly.FullName;
-
- // get name if it's a registered source
- if (this.ModNamesByAssembly.ContainsKey(assemblyName))
- return this.ModNamesByAssembly[assemblyName];
+ string name = this.GetModFrom(method.ReflectedType);
+ if (name != null)
+ return name;
}
// no known assembly found
diff --git a/src/StardewModdingAPI/Framework/Models/IncompatibleMod.cs b/src/StardewModdingAPI/Framework/Models/IncompatibleMod.cs
index 9bf06552..bcf5639c 100644
--- a/src/StardewModdingAPI/Framework/Models/IncompatibleMod.cs
+++ b/src/StardewModdingAPI/Framework/Models/IncompatibleMod.cs
@@ -14,8 +14,11 @@ namespace StardewModdingAPI.Framework.Models
/// <summary>The mod name.</summary>
public string Name { get; set; }
+ /// <summary>The oldest incompatible mod version, or <c>null</c> for all past versions.</summary>
+ public string LowerVersion { get; set; }
+
/// <summary>The most recent incompatible mod version.</summary>
- public string Version { get; set; }
+ public string UpperVersion { get; set; }
/// <summary>The URL the user can check for an official updated version.</summary>
public string UpdateUrl { get; set; }
@@ -23,9 +26,13 @@ namespace StardewModdingAPI.Framework.Models
/// <summary>The URL the user can check for an unofficial updated version.</summary>
public string UnofficialUpdateUrl { get; set; }
- /// <summary>A regular expression matching version strings to consider compatible, even if they technically precede <see cref="Version"/>.</summary>
+ /// <summary>A regular expression matching version strings to consider compatible, even if they technically precede <see cref="UpperVersion"/>.</summary>
public string ForceCompatibleVersion { get; set; }
+ /// <summary>The reason phrase to show in the warning, or <c>null</c> to use the default value.</summary>
+ /// <example>"this version is incompatible with the latest version of the game"</example>
+ public string ReasonPhrase { get; set; }
+
/*********
** Public methods
@@ -34,14 +41,17 @@ namespace StardewModdingAPI.Framework.Models
/// <param name="version">The current version of the matching mod.</param>
public bool IsCompatible(ISemanticVersion version)
{
- ISemanticVersion incompatibleVersion = new SemanticVersion(this.Version);
+ ISemanticVersion lowerVersion = this.LowerVersion != null ? new SemanticVersion(this.LowerVersion) : null;
+ ISemanticVersion upperVersion = new SemanticVersion(this.UpperVersion);
- // allow newer versions
- if (version.IsNewerThan(incompatibleVersion))
+ // ignore versions not in range
+ if (lowerVersion != null && version.IsOlderThan(lowerVersion))
+ return true;
+ if (version.IsNewerThan(upperVersion))
return true;
// allow versions matching override
return !string.IsNullOrWhiteSpace(this.ForceCompatibleVersion) && Regex.IsMatch(version.ToString(), this.ForceCompatibleVersion, RegexOptions.IgnoreCase);
}
}
-} \ No newline at end of file
+}
diff --git a/src/StardewModdingAPI/Framework/Monitor.cs b/src/StardewModdingAPI/Framework/Monitor.cs
index cf46a474..39b567d8 100644
--- a/src/StardewModdingAPI/Framework/Monitor.cs
+++ b/src/StardewModdingAPI/Framework/Monitor.cs
@@ -34,9 +34,15 @@ namespace StardewModdingAPI.Framework
/*********
** Accessors
*********/
+ /// <summary>Whether the current console supports color codes.</summary>
+ internal static readonly bool ConsoleSupportsColor = Monitor.GetConsoleSupportsColor();
+
/// <summary>Whether to show trace messages in the console.</summary>
internal bool ShowTraceInConsole { get; set; }
+ /// <summary>Whether to write anything to the console. This should be disabled if no console is available.</summary>
+ internal bool WriteToConsole { get; set; } = true;
+
/*********
** Public methods
@@ -108,13 +114,32 @@ namespace StardewModdingAPI.Framework
message = $"[{DateTime.Now:HH:mm:ss} {levelStr} {source}] {message}";
// log
- if (this.ShowTraceInConsole || level != LogLevel.Trace)
+ if (this.WriteToConsole && (this.ShowTraceInConsole || level != LogLevel.Trace))
{
- Console.ForegroundColor = color;
- Console.WriteLine(message);
- Console.ResetColor();
+ if (Monitor.ConsoleSupportsColor)
+ {
+ Console.ForegroundColor = color;
+ Console.WriteLine(message);
+ Console.ResetColor();
+ }
+ else
+ Console.WriteLine(message);
}
this.LogFile.WriteLine(message);
}
+
+ /// <summary>Test whether the current console supports color formatting.</summary>
+ private static bool GetConsoleSupportsColor()
+ {
+ try
+ {
+ Console.ForegroundColor = Console.ForegroundColor;
+ return true;
+ }
+ catch (Exception)
+ {
+ return false; // Mono bug
+ }
+ }
}
} \ No newline at end of file