summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2017-01-16 16:10:57 -0500
committerJesse Plamondon-Willard <github@jplamondonw.com>2017-01-16 16:10:57 -0500
commit6adf199987a506f8a65f6c1ddfad5aa9fa2a6a9f (patch)
treefaa9155fae99533110853567003325486f208937 /src
parente8825947ca82c8f28ad9bc8a225fb4fb749814cb (diff)
parent1f3d3c8c93c7a427486b60cf649b86cef140e88b (diff)
downloadSMAPI-6adf199987a506f8a65f6c1ddfad5aa9fa2a6a9f.tar.gz
SMAPI-6adf199987a506f8a65f6c1ddfad5aa9fa2a6a9f.tar.bz2
SMAPI-6adf199987a506f8a65f6c1ddfad5aa9fa2a6a9f.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src')
-rw-r--r--src/GlobalAssemblyInfo.cs4
-rw-r--r--src/StardewModdingAPI.Installer/InteractiveInstaller.cs58
-rw-r--r--src/StardewModdingAPI.sln2
-rw-r--r--src/StardewModdingAPI/Constants.cs2
-rw-r--r--src/StardewModdingAPI/Events/PlayerEvents.cs20
-rw-r--r--src/StardewModdingAPI/Events/SaveEvents.cs46
-rw-r--r--src/StardewModdingAPI/Events/TimeEvents.cs10
-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
-rw-r--r--src/StardewModdingAPI/Inheritance/SGame.cs12
-rw-r--r--src/StardewModdingAPI/LogWriter.cs11
-rw-r--r--src/StardewModdingAPI/Manifest.cs1
-rw-r--r--src/StardewModdingAPI/ModHelper.cs17
-rw-r--r--src/StardewModdingAPI/Program.cs28
-rw-r--r--src/StardewModdingAPI/StardewModdingAPI.csproj1
-rw-r--r--src/StardewModdingAPI/StardewModdingAPI.data.json23
-rw-r--r--src/StardewModdingAPI/unix-launcher.sh8
-rw-r--r--src/TrainerMod/TrainerMod.cs20
-rw-r--r--src/TrainerMod/TrainerMod.csproj53
-rw-r--r--src/crossplatform.targets4
24 files changed, 340 insertions, 122 deletions
diff --git a/src/GlobalAssemblyInfo.cs b/src/GlobalAssemblyInfo.cs
index 7f1fa401..3df34a96 100644
--- a/src/GlobalAssemblyInfo.cs
+++ b/src/GlobalAssemblyInfo.cs
@@ -2,5 +2,5 @@
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
-[assembly: AssemblyVersion("1.5.0.0")]
-[assembly: AssemblyFileVersion("1.5.0.0")] \ No newline at end of file
+[assembly: AssemblyVersion("1.6.0.0")]
+[assembly: AssemblyFileVersion("1.6.0.0")] \ No newline at end of file
diff --git a/src/StardewModdingAPI.Installer/InteractiveInstaller.cs b/src/StardewModdingAPI.Installer/InteractiveInstaller.cs
index 5f89caf2..ef813eb3 100644
--- a/src/StardewModdingAPI.Installer/InteractiveInstaller.cs
+++ b/src/StardewModdingAPI.Installer/InteractiveInstaller.cs
@@ -77,6 +77,9 @@ namespace StardewModdingApi.Installer
"StardewModdingAPI-settings.json" // 1.0-1.4
};
+ /// <summary>Whether the current console supports color formatting.</summary>
+ private static readonly bool ConsoleSupportsColor = InteractiveInstaller.GetConsoleSupportsColor();
+
/*********
** Public methods
@@ -253,18 +256,18 @@ namespace StardewModdingApi.Installer
/****
** exit
****/
- Console.ForegroundColor = ConsoleColor.DarkGreen;
- Console.WriteLine("Done!");
+ this.PrintColor("Done!", ConsoleColor.DarkGreen);
if (platform == Platform.Windows)
{
- Console.WriteLine(action == ScriptAction.Install
- ? "Don't forget to launch StardewModdingAPI.exe instead of the normal game executable. See the readme.txt for details."
- : "If you manually changed shortcuts or Steam to launch SMAPI, don't forget to change those back."
+ this.PrintColor(
+ action == ScriptAction.Install
+ ? "Don't forget to launch StardewModdingAPI.exe instead of the normal game executable. See the readme.txt for details."
+ : "If you manually changed shortcuts or Steam to launch SMAPI, don't forget to change those back.",
+ ConsoleColor.DarkGreen
);
}
else if (action == ScriptAction.Install)
- Console.WriteLine("You can launch the game the same way as before to play with mods.");
- Console.ResetColor();
+ this.PrintColor("You can launch the game the same way as before to play with mods.", ConsoleColor.DarkGreen);
Console.ReadKey();
}
@@ -287,6 +290,20 @@ namespace StardewModdingApi.Installer
}
}
+ /// <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
+ }
+ }
+
#if SMAPI_FOR_WINDOWS
/// <summary>Get the value of a key in the Windows registry.</summary>
/// <param name="key">The full path of the registry key relative to HKLM.</param>
@@ -306,30 +323,39 @@ namespace StardewModdingApi.Installer
/// <param name="text">The text to print.</param>
private void PrintDebug(string text)
{
- Console.ForegroundColor = ConsoleColor.DarkGray;
- Console.WriteLine(text);
- Console.ResetColor();
+ this.PrintColor(text, ConsoleColor.DarkGray);
}
/// <summary>Print a warning message.</summary>
/// <param name="text">The text to print.</param>
private void PrintWarning(string text)
{
- Console.ForegroundColor = ConsoleColor.DarkYellow;
- Console.WriteLine(text);
- Console.ResetColor();
+ this.PrintColor(text, ConsoleColor.DarkYellow);
}
/// <summary>Print an error and pause the console if needed.</summary>
/// <param name="error">The error text.</param>
private void ExitError(string error)
{
- Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine(error);
- Console.ResetColor();
+ this.PrintColor(error, ConsoleColor.Red);
Console.ReadLine();
}
+ /// <summary>Print a message to the console.</summary>
+ /// <param name="text">The message text.</param>
+ /// <param name="color">The text foreground color.</param>
+ private void PrintColor(string text, ConsoleColor color)
+ {
+ if (InteractiveInstaller.ConsoleSupportsColor)
+ {
+ Console.ForegroundColor = color;
+ Console.WriteLine(text);
+ Console.ResetColor();
+ }
+ else
+ Console.WriteLine(text);
+ }
+
/// <summary>Interactively ask the user to choose a value.</summary>
/// <param name="message">The message to print.</param>
/// <param name="options">The allowed options (not case sensitive).</param>
diff --git a/src/StardewModdingAPI.sln b/src/StardewModdingAPI.sln
index 6e13b16b..7229cf30 100644
--- a/src/StardewModdingAPI.sln
+++ b/src/StardewModdingAPI.sln
@@ -41,11 +41,13 @@ Global
{28480467-1A48-46A7-99F8-236D95225359}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {28480467-1A48-46A7-99F8-236D95225359}.Debug|x86.Build.0 = Debug|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|Any CPU.Build.0 = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{28480467-1A48-46A7-99F8-236D95225359}.Release|x86.ActiveCfg = Release|Any CPU
+ {28480467-1A48-46A7-99F8-236D95225359}.Release|x86.Build.0 = Release|Any CPU
{F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1A573B0-F436-472C-AE29-0B91EA6B9F8F}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs
index a79fd382..2211e167 100644
--- a/src/StardewModdingAPI/Constants.cs
+++ b/src/StardewModdingAPI/Constants.cs
@@ -30,7 +30,7 @@ namespace StardewModdingAPI
public static readonly Version Version = (Version)Constants.ApiVersion;
/// <summary>SMAPI's current semantic version.</summary>
- public static ISemanticVersion ApiVersion => new Version(1, 5, 0, null, suppressDeprecationWarning: true);
+ public static ISemanticVersion ApiVersion => new Version(1, 6, 0, null, suppressDeprecationWarning: true);
/// <summary>The minimum supported version of Stardew Valley.</summary>
public const string MinimumGameVersion = "1.1";
diff --git a/src/StardewModdingAPI/Events/PlayerEvents.cs b/src/StardewModdingAPI/Events/PlayerEvents.cs
index 3f301b07..dd3ff220 100644
--- a/src/StardewModdingAPI/Events/PlayerEvents.cs
+++ b/src/StardewModdingAPI/Events/PlayerEvents.cs
@@ -14,9 +14,11 @@ namespace StardewModdingAPI.Events
** Events
*********/
/// <summary>Raised after the player loads a saved game.</summary>
+ [Obsolete("Use " + nameof(SaveEvents) + "." + nameof(SaveEvents.AfterLoad) + " instead")]
public static event EventHandler<EventArgsLoadedGameChanged> LoadedGame;
/// <summary>Raised after the game assigns a new player character. This happens just before <see cref="LoadedGame"/>; it's unclear how this would happen any other time.</summary>
+ [Obsolete("should no longer be used")]
public static event EventHandler<EventArgsFarmerChanged> FarmerChanged;
/// <summary>Raised after the player's inventory changes in any way (added or removed item, sorted, etc).</summary>
@@ -34,7 +36,14 @@ namespace StardewModdingAPI.Events
/// <param name="loaded">Whether the save has been loaded. This is always true.</param>
internal static void InvokeLoadedGame(IMonitor monitor, EventArgsLoadedGameChanged loaded)
{
- monitor.SafelyRaiseGenericEvent($"{nameof(PlayerEvents)}.{nameof(PlayerEvents.LoadedGame)}", PlayerEvents.LoadedGame?.GetInvocationList(), null, loaded);
+ if (PlayerEvents.LoadedGame == null)
+ return;
+
+ string name = $"{nameof(PlayerEvents)}.{nameof(PlayerEvents.LoadedGame)}";
+ Delegate[] handlers = PlayerEvents.LoadedGame.GetInvocationList();
+
+ Program.DeprecationManager.WarnForEvent(handlers, name, "1.6", DeprecationLevel.Notice);
+ monitor.SafelyRaiseGenericEvent(name, handlers, null, loaded);
}
/// <summary>Raise a <see cref="FarmerChanged"/> event.</summary>
@@ -43,7 +52,14 @@ namespace StardewModdingAPI.Events
/// <param name="newFarmer">The new player character.</param>
internal static void InvokeFarmerChanged(IMonitor monitor, Farmer priorFarmer, Farmer newFarmer)
{
- monitor.SafelyRaiseGenericEvent($"{nameof(PlayerEvents)}.{nameof(PlayerEvents.FarmerChanged)}", PlayerEvents.FarmerChanged?.GetInvocationList(), null, new EventArgsFarmerChanged(priorFarmer, newFarmer));
+ if (PlayerEvents.FarmerChanged == null)
+ return;
+
+ string name = $"{nameof(PlayerEvents)}.{nameof(PlayerEvents.FarmerChanged)}";
+ Delegate[] handlers = PlayerEvents.FarmerChanged.GetInvocationList();
+
+ Program.DeprecationManager.WarnForEvent(handlers, name, "1.6", DeprecationLevel.Notice);
+ monitor.SafelyRaiseGenericEvent(name, handlers, null, new EventArgsFarmerChanged(priorFarmer, newFarmer));
}
/// <summary>Raise an <see cref="InventoryChanged"/> event.</summary>
diff --git a/src/StardewModdingAPI/Events/SaveEvents.cs b/src/StardewModdingAPI/Events/SaveEvents.cs
new file mode 100644
index 00000000..2921003a
--- /dev/null
+++ b/src/StardewModdingAPI/Events/SaveEvents.cs
@@ -0,0 +1,46 @@
+using System;
+using StardewModdingAPI.Framework;
+
+namespace StardewModdingAPI.Events
+{
+ /// <summary>Events raised before and after the player saves/loads the game.</summary>
+ public static class SaveEvents
+ {
+ /*********
+ ** Events
+ *********/
+ /// <summary>Raised before the game begins writes data to the save file.</summary>
+ public static event EventHandler BeforeSave;
+
+ /// <summary>Raised after the game finishes writing data to the save file.</summary>
+ public static event EventHandler AfterSave;
+
+ /// <summary>Raised after the player loads a save slot.</summary>
+ public static event EventHandler AfterLoad;
+
+
+ /*********
+ ** Internal methods
+ *********/
+ /// <summary>Raise a <see cref="BeforeSave"/> event.</summary>
+ /// <param name="monitor">Encapsulates monitoring and logging.</param>
+ internal static void InvokeBeforeSave(IMonitor monitor)
+ {
+ monitor.SafelyRaisePlainEvent($"{nameof(SaveEvents)}.{nameof(SaveEvents.BeforeSave)}", SaveEvents.BeforeSave?.GetInvocationList(), null, EventArgs.Empty);
+ }
+
+ /// <summary>Raise a <see cref="AfterSave"/> event.</summary>
+ /// <param name="monitor">Encapsulates monitoring and logging.</param>
+ internal static void InvokeAfterSave(IMonitor monitor)
+ {
+ monitor.SafelyRaisePlainEvent($"{nameof(SaveEvents)}.{nameof(SaveEvents.AfterSave)}", SaveEvents.AfterSave?.GetInvocationList(), null, EventArgs.Empty);
+ }
+
+ /// <summary>Raise a <see cref="AfterLoad"/> event.</summary>
+ /// <param name="monitor">Encapsulates monitoring and logging.</param>
+ internal static void InvokeAfterLoad(IMonitor monitor)
+ {
+ monitor.SafelyRaisePlainEvent($"{nameof(SaveEvents)}.{nameof(SaveEvents.AfterLoad)}", SaveEvents.AfterLoad?.GetInvocationList(), null, EventArgs.Empty);
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Events/TimeEvents.cs b/src/StardewModdingAPI/Events/TimeEvents.cs
index 1b367230..dedd7e77 100644
--- a/src/StardewModdingAPI/Events/TimeEvents.cs
+++ b/src/StardewModdingAPI/Events/TimeEvents.cs
@@ -22,6 +22,7 @@ namespace StardewModdingAPI.Events
public static event EventHandler<EventArgsStringChanged> SeasonOfYearChanged;
/// <summary>Raised when the player is transitioning to a new day and the game is performing its day update logic. This event is triggered twice: once after the game starts transitioning, and again after it finishes.</summary>
+ [Obsolete("Use " + nameof(TimeEvents) + "." + nameof(DayOfMonthChanged) + " or " + nameof(SaveEvents) + " instead")]
public static event EventHandler<EventArgsNewDay> OnNewDay;
@@ -71,7 +72,14 @@ namespace StardewModdingAPI.Events
/// <param name="isTransitioning">Whether the game just started the transition (<c>true</c>) or finished it (<c>false</c>).</param>
internal static void InvokeOnNewDay(IMonitor monitor, int priorDay, int newDay, bool isTransitioning)
{
- monitor.SafelyRaiseGenericEvent($"{nameof(TimeEvents)}.{nameof(TimeEvents.OnNewDay)}", TimeEvents.OnNewDay?.GetInvocationList(), null, new EventArgsNewDay(priorDay, newDay, isTransitioning));
+ if (TimeEvents.OnNewDay == null)
+ return;
+
+ string name = $"{nameof(TimeEvents)}.{nameof(TimeEvents.OnNewDay)}";
+ Delegate[] handlers = TimeEvents.OnNewDay.GetInvocationList();
+
+ Program.DeprecationManager.WarnForEvent(handlers, name, "1.6", DeprecationLevel.Notice);
+ monitor.SafelyRaiseGenericEvent(name, handlers, null, new EventArgsNewDay(priorDay, newDay, isTransitioning));
}
}
}
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
diff --git a/src/StardewModdingAPI/Inheritance/SGame.cs b/src/StardewModdingAPI/Inheritance/SGame.cs
index 16803a73..8e87bac6 100644
--- a/src/StardewModdingAPI/Inheritance/SGame.cs
+++ b/src/StardewModdingAPI/Inheritance/SGame.cs
@@ -913,10 +913,17 @@ namespace StardewModdingAPI.Inheritance
// raise menu changed
if (Game1.activeClickableMenu != this.PreviousActiveMenu)
{
- // raise events
IClickableMenu previousMenu = this.PreviousActiveMenu;
IClickableMenu newMenu = Game1.activeClickableMenu;
- if (Game1.activeClickableMenu != null)
+
+ // raise save events
+ if (newMenu is SaveGameMenu)
+ SaveEvents.InvokeBeforeSave(this.Monitor);
+ else if (previousMenu is SaveGameMenu)
+ SaveEvents.InvokeAfterSave(this.Monitor);
+
+ // raise menu events
+ if (newMenu != null)
MenuEvents.InvokeMenuChanged(this.Monitor, previousMenu, newMenu);
else
MenuEvents.InvokeMenuClosed(this.Monitor, previousMenu);
@@ -1024,6 +1031,7 @@ namespace StardewModdingAPI.Inheritance
// raise player loaded save (in the following tick to let the game finish updating first)
if (this.FireLoadedGameEvent)