summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/GlobalAssemblyInfo.cs4
-rw-r--r--src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj10
-rw-r--r--src/StardewModdingAPI.Installer/InteractiveInstaller.cs299
-rw-r--r--src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj1
-rw-r--r--src/StardewModdingAPI.sln1
-rw-r--r--src/StardewModdingAPI/Advanced/ConfigFile.cs2
-rw-r--r--src/StardewModdingAPI/Advanced/IConfigFile.cs2
-rw-r--r--src/StardewModdingAPI/Constants.cs2
-rw-r--r--src/StardewModdingAPI/Framework/AssemblyRewriting/AssemblyTypeRewriter.cs6
-rw-r--r--src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs46
-rw-r--r--src/StardewModdingAPI/Framework/AssemblyRewriting/CachePaths.cs10
-rw-r--r--src/StardewModdingAPI/Framework/AssemblyRewriting/RewriteResult.cs49
-rw-r--r--src/StardewModdingAPI/Framework/InternalExtensions.cs10
-rw-r--r--src/StardewModdingAPI/Framework/ModAssemblyLoader.cs120
-rw-r--r--src/StardewModdingAPI/Framework/Reflection/PrivateField.cs93
-rw-r--r--src/StardewModdingAPI/Framework/Reflection/PrivateMethod.cs99
-rw-r--r--src/StardewModdingAPI/Framework/Reflection/ReflectionHelper.cs239
-rw-r--r--src/StardewModdingAPI/IModHelper.cs5
-rw-r--r--src/StardewModdingAPI/IPrivateField.cs26
-rw-r--r--src/StardewModdingAPI/IPrivateMethod.cs27
-rw-r--r--src/StardewModdingAPI/IReflectionHelper.cs53
-rw-r--r--src/StardewModdingAPI/Inheritance/SGame.cs42
-rw-r--r--src/StardewModdingAPI/Mod.cs7
-rw-r--r--src/StardewModdingAPI/ModHelper.cs8
-rw-r--r--src/StardewModdingAPI/Program.cs41
-rw-r--r--src/StardewModdingAPI/StardewModdingAPI.csproj13
-rw-r--r--src/StardewModdingAPI/unix-launcher.sh33
-rw-r--r--src/TrainerMod/ItemData/ISearchItem.cs21
-rw-r--r--src/TrainerMod/ItemData/ItemType.cs15
-rw-r--r--src/TrainerMod/ItemData/SearchableObject.cs48
-rw-r--r--src/TrainerMod/ItemData/SearchableRing.cs48
-rw-r--r--src/TrainerMod/ItemData/SearchableWeapon.cs48
-rw-r--r--src/TrainerMod/TrainerMod.cs137
-rw-r--r--src/TrainerMod/TrainerMod.csproj11
-rw-r--r--src/crossplatform.targets (renamed from src/dependencies.targets)2
35 files changed, 1317 insertions, 261 deletions
diff --git a/src/GlobalAssemblyInfo.cs b/src/GlobalAssemblyInfo.cs
index 239c5eba..0dfb42bb 100644
--- a/src/GlobalAssemblyInfo.cs
+++ b/src/GlobalAssemblyInfo.cs
@@ -2,5 +2,5 @@
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
-[assembly: AssemblyVersion("1.3.0.0")]
-[assembly: AssemblyFileVersion("1.3.0.0")] \ No newline at end of file
+[assembly: AssemblyVersion("1.4.0.0")]
+[assembly: AssemblyFileVersion("1.4.0.0")] \ No newline at end of file
diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj
index 51a49da0..1e6caacc 100644
--- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj
+++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj
@@ -47,7 +47,6 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
- <Import Project="$(SolutionDir)\dependencies.targets" />
<ItemGroup>
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
@@ -82,13 +81,6 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
- <ItemGroup />
+ <Import Project="$(SolutionDir)\crossplatform.targets" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
- Other similar extension points exist, see Microsoft.Common.targets.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
</Project> \ No newline at end of file
diff --git a/src/StardewModdingAPI.Installer/InteractiveInstaller.cs b/src/StardewModdingAPI.Installer/InteractiveInstaller.cs
index 1d3802ab..9c8f8af9 100644
--- a/src/StardewModdingAPI.Installer/InteractiveInstaller.cs
+++ b/src/StardewModdingAPI.Installer/InteractiveInstaller.cs
@@ -1,7 +1,11 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
+#if SMAPI_FOR_WINDOWS
+using Microsoft.Win32;
+#endif
using StardewModdingApi.Installer.Enums;
namespace StardewModdingApi.Installer
@@ -14,21 +18,40 @@ namespace StardewModdingApi.Installer
*********/
/// <summary>The default file paths where Stardew Valley can be installed.</summary>
/// <remarks>Derived from the crossplatform mod config: https://github.com/Pathoschild/Stardew.ModBuildConfig. </remarks>
- private readonly string[] DefaultInstallPaths = {
- // Linux
- $"{Environment.GetEnvironmentVariable("HOME")}/GOG Games/Stardew Valley/game",
- $"{Environment.GetEnvironmentVariable("HOME")}/.local/share/Steam/steamapps/common/Stardew Valley",
+ private IEnumerable<string> DefaultInstallPaths
+ {
+ get
+ {
+ // Linux
+ yield return $"{Environment.GetEnvironmentVariable("HOME")}/GOG Games/Stardew Valley/game";
+ yield return $"{Environment.GetEnvironmentVariable("HOME")}/.local/share/Steam/steamapps/common/Stardew Valley";
- // Mac
- $"{Environment.GetEnvironmentVariable("HOME")}/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS",
+ // Mac
+ yield return $"{Environment.GetEnvironmentVariable("HOME")}/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS";
- // Windows
- @"C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley",
- @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley"
- };
+ // Windows
+ yield return @"C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley";
+ yield return @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley";
- /// <summary>The files to remove when uninstalling SMAPI.</summary>
- private readonly string[] UninstallFiles =
+ // Windows registry
+#if SMAPI_FOR_WINDOWS
+ IDictionary<string, string> registryKeys = new Dictionary<string, string>
+ {
+ [@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150"] = "InstallLocation", // Steam
+ [@"SOFTWARE\WOW6432Node\GOG.com\Games\1453375253"] = "PATH", // GOG on 64-bit Windows
+ };
+ foreach (var pair in registryKeys)
+ {
+ string path = this.GetLocalMachineRegistryValue(pair.Key, pair.Value);
+ if (!string.IsNullOrWhiteSpace(path))
+ yield return path;
+ }
+#endif
+ }
+ }
+
+ /// <summary>The directory or file paths to remove when uninstalling SMAPI, relative to the game directory.</summary>
+ private readonly string[] UninstallPaths =
{
// common
"StardewModdingAPI.exe",
@@ -45,7 +68,10 @@ namespace StardewModdingApi.Installer
"System.Numerics.dll",
// Windows only
- "StardewModdingAPI.pdb"
+ "StardewModdingAPI.pdb",
+
+ // obsolete
+ "Mods/.cache"
};
@@ -59,16 +85,17 @@ namespace StardewModdingApi.Installer
/// 1. Collect information (mainly OS and install path) and validate it.
/// 2. Ask the user whether to install or uninstall.
///
- /// Install flow:
- /// 1. Copy the SMAPI files from package/Windows or package/Mono into the game directory.
- /// 2. On Linux/Mac: back up the game launcher and replace it with the SMAPI launcher. (This isn't possible on Windows, so the user needs to configure it manually.)
- /// 3. Create the 'Mods' directory.
- /// 4. Copy the bundled mods into the 'Mods' directory (deleting any existing versions).
- /// 5. Move any mods from app data into game's mods directory.
- ///
/// Uninstall logic:
/// 1. On Linux/Mac: if a backup of the launcher exists, delete the launcher and restore the backup.
- /// 2. Delete all files in the game directory matching one of the <see cref="UninstallFiles"/>.
+ /// 2. Delete all files and folders in the game directory matching one of the <see cref="UninstallPaths"/>.
+ ///
+ /// Install flow:
+ /// 1. Run the uninstall flow.
+ /// 2. Copy the SMAPI files from package/Windows or package/Mono into the game directory.
+ /// 3. On Linux/Mac: back up the game launcher and replace it with the SMAPI launcher. (This isn't possible on Windows, so the user needs to configure it manually.)
+ /// 4. Create the 'Mods' directory.
+ /// 5. Copy the bundled mods into the 'Mods' directory (deleting any existing versions).
+ /// 6. Move any mods from app data into game's mods directory.
/// </remarks>
public void Run(string[] args)
{
@@ -85,7 +112,6 @@ namespace StardewModdingApi.Installer
unixLauncher = Path.Combine(installDir.FullName, "StardewValley"),
unixLauncherBackup = Path.Combine(installDir.FullName, "StardewValley-original")
};
-
this.PrintDebug($"Detected {(platform == Platform.Windows ? "Windows" : "Linux or Mac")} with game in {installDir}.");
/****
@@ -115,7 +141,7 @@ namespace StardewModdingApi.Installer
ScriptAction action;
{
- string choice = this.InteractivelyChoose("What do you want to do?", "1", "2");
+ string choice = this.InteractivelyChoose("What do you want to do? Type 1 or 2, then press enter.", "1", "2");
switch (choice)
{
case "1":
@@ -131,90 +157,93 @@ namespace StardewModdingApi.Installer
Console.WriteLine();
/****
- ** Perform action
+ ** Always uninstall old files
****/
- switch (action)
+ // restore game launcher
+ if (platform == Platform.Mono && File.Exists(paths.unixLauncherBackup))
{
- case ScriptAction.Uninstall:
- {
- // restore game launcher
- if (platform == Platform.Mono && File.Exists(paths.unixLauncherBackup))
- {
- this.PrintDebug("Restoring game launcher...");
- if (File.Exists(paths.unixLauncher))
- File.Delete(paths.unixLauncher);
- File.Move(paths.unixLauncherBackup, paths.unixLauncher);
- }
-
- // remove SMAPI files
- this.PrintDebug("Removing SMAPI files...");
- foreach (string filename in this.UninstallFiles)
- {
- string targetPath = Path.Combine(installDir.FullName, filename);
- if (File.Exists(targetPath))
- File.Delete(targetPath);
- }
- }
- break;
+ this.PrintDebug("Removing SMAPI launcher...");
+ if (File.Exists(paths.unixLauncher))
+ File.Delete(paths.unixLauncher);
+ File.Move(paths.unixLauncherBackup, paths.unixLauncher);
+ }
+
+ // remove old files
+ string[] removePaths = this.UninstallPaths
+ .Select(path => Path.Combine(installDir.FullName, path))
+ .Where(path => Directory.Exists(path) || File.Exists(path))
+ .ToArray();
+ if (removePaths.Any())
+ {
+ this.PrintDebug(action == ScriptAction.Install ? "Removing previous SMAPI files..." : "Removing SMAPI files...");
+ foreach (string path in removePaths)
+ {
+ if (Directory.Exists(path))
+ Directory.Delete(path, recursive: true);
+ else
+ File.Delete(path);
+ }
+ }
+
+ /****
+ ** Install new files
+ ****/
+ if (action == ScriptAction.Install)
+ {
+ // copy SMAPI files to game dir
+ this.PrintDebug("Adding SMAPI files...");
+ foreach (FileInfo sourceFile in packageDir.EnumerateFiles())
+ {
+ string targetPath = Path.Combine(installDir.FullName, sourceFile.Name);
+ if (File.Exists(targetPath))
+ File.Delete(targetPath);
+ sourceFile.CopyTo(targetPath);
+ }
+
+ // replace mod launcher (if possible)
+ if (platform == Platform.Mono)
+ {
+ this.PrintDebug("Safely replacing game launcher...");
+ if (!File.Exists(paths.unixLauncherBackup))
+ File.Move(paths.unixLauncher, paths.unixLauncherBackup);
+ else if (File.Exists(paths.unixLauncher))
+ File.Delete(paths.unixLauncher);
+
+ File.Move(paths.unixSmapiLauncher, paths.unixLauncher);
+ }
+
+ // create mods directory (if needed)
+ DirectoryInfo modsDir = new DirectoryInfo(Path.Combine(installDir.FullName, "Mods"));
+ if (!modsDir.Exists)
+ {
+ this.PrintDebug("Creating mods directory...");
+ modsDir.Create();
+ }
- case ScriptAction.Install:
+ // add or replace bundled mods
+ Directory.CreateDirectory(Path.Combine(installDir.FullName, "Mods"));
+ DirectoryInfo packagedModsDir = new DirectoryInfo(Path.Combine(packageDir.FullName, "Mods"));
+ if (packagedModsDir.Exists && packagedModsDir.EnumerateDirectories().Any())
+ {
+ this.PrintDebug("Adding bundled mods...");
+ foreach (DirectoryInfo sourceDir in packagedModsDir.EnumerateDirectories())
{
- // copy SMAPI files to game dir
- this.PrintDebug("Copying SMAPI files to game directory...");
- foreach (FileInfo sourceFile in packageDir.EnumerateFiles())
- {
- string targetPath = Path.Combine(installDir.FullName, sourceFile.Name);
- if (File.Exists(targetPath))
- File.Delete(targetPath);
- sourceFile.CopyTo(targetPath);
- }
-
- // replace mod launcher (if possible)
- if (platform == Platform.Mono)
- {
- this.PrintDebug("Safely replacing game launcher...");
- if (!File.Exists(paths.unixLauncherBackup))
- File.Move(paths.unixLauncher, paths.unixLauncherBackup);
- else if (File.Exists(paths.unixLauncher))
- File.Delete(paths.unixLauncher);
-
- File.Move(paths.unixSmapiLauncher, paths.unixLauncher);
- }
-
- // create mods directory (if needed)
- DirectoryInfo modsDir = new DirectoryInfo(Path.Combine(installDir.FullName, "Mods"));
- if (!modsDir.Exists)
- {
- this.PrintDebug("Creating mods directory...");
- modsDir.Create();
- }
-
- // add or replace bundled mods
- Directory.CreateDirectory(Path.Combine(installDir.FullName, "Mods"));
- DirectoryInfo packagedModsDir = new DirectoryInfo(Path.Combine(packageDir.FullName, "Mods"));
- if (packagedModsDir.Exists && packagedModsDir.EnumerateDirectories().Any())
- {
- this.PrintDebug("Adding bundled mods...");
- foreach (DirectoryInfo sourceDir in packagedModsDir.EnumerateDirectories())
- {
- this.PrintDebug($" adding {sourceDir.Name}...");
-
- // initialise target dir
- DirectoryInfo targetDir = new DirectoryInfo(Path.Combine(modsDir.FullName, sourceDir.Name));
- if (targetDir.Exists)
- targetDir.Delete(recursive: true);
- targetDir.Create();
-
- // copy files
- foreach (FileInfo sourceFile in sourceDir.EnumerateFiles())
- sourceFile.CopyTo(Path.Combine(targetDir.FullName, sourceFile.Name));
- }
- }
-
- // remove obsolete appdata mods
- this.InteractivelyRemoveAppDataMods(platform, modsDir);
+ this.PrintDebug($" adding {sourceDir.Name}...");
+
+ // initialise target dir
+ DirectoryInfo targetDir = new DirectoryInfo(Path.Combine(modsDir.FullName, sourceDir.Name));
+ if (targetDir.Exists)
+ targetDir.Delete(recursive: true);
+ targetDir.Create();
+
+ // copy files
+ foreach (FileInfo sourceFile in sourceDir.EnumerateFiles())
+ sourceFile.CopyTo(Path.Combine(targetDir.FullName, sourceFile.Name));
}
- break;
+ }
+
+ // remove obsolete appdata mods
+ this.InteractivelyRemoveAppDataMods(platform, modsDir, packagedModsDir);
}
Console.WriteLine();
@@ -255,6 +284,21 @@ namespace StardewModdingApi.Installer
}
}
+#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>
+ /// <param name="name">The name of the value.</param>
+ private string GetLocalMachineRegistryValue(string key, string name)
+ {
+ RegistryKey localMachine = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : Registry.LocalMachine;
+ RegistryKey openKey = localMachine.OpenSubKey(key);
+ if (openKey == null)
+ return null;
+ using (openKey)
+ return (string)openKey.GetValue(name);
+ }
+#endif
+
/// <summary>Print a debug message.</summary>
/// <param name="text">The text to print.</param>
private void PrintDebug(string text)
@@ -313,12 +357,11 @@ namespace StardewModdingApi.Installer
}
// ask user
- Console.WriteLine("Oops, couldn't find your Stardew Valley install path automatically. You'll need to specify where the game is installed (or install SMAPI manually).");
+ Console.WriteLine("Oops, couldn't find the game automatically.");
while (true)
{
// get path from user
- Console.WriteLine(" Enter the game's full directory path (the one containing 'StardewValley.exe' or 'Stardew Valley.exe').");
- Console.Write(" > ");
+ Console.WriteLine($"Type the file path to the game directory (the one containing '{(platform == Platform.Mono ? "StardewValley.exe" : "Stardew Valley.exe")}'), then press enter.");
string path = Console.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(path))
{
@@ -356,8 +399,12 @@ namespace StardewModdingApi.Installer
/// <summary>Interactively move mods out of the appdata directory.</summary>
/// <param name="platform">The current platform.</param>
/// <param name="properModsDir">The directory which should contain all mods.</param>
- private void InteractivelyRemoveAppDataMods(Platform platform, DirectoryInfo properModsDir)
+ /// <param name="packagedModsDir">The installer directory containing packaged mods.</param>
+ private void InteractivelyRemoveAppDataMods(Platform platform, DirectoryInfo properModsDir, DirectoryInfo packagedModsDir)
{
+ // get packaged mods to delete
+ string[] packagedModNames = packagedModsDir.GetDirectories().Select(p => p.Name).ToArray();
+
// get path
string homePath = platform == Platform.Windows
? Environment.GetEnvironmentVariable("APPDATA")
@@ -379,6 +426,14 @@ namespace StardewModdingApi.Installer
if (!isDir && !(entry is FileInfo))
continue; // should never happen
+ // delete packaged mods (newer version bundled into SMAPI)
+ if (isDir && packagedModNames.Contains(entry.Name, StringComparer.InvariantCultureIgnoreCase))
+ {
+ this.PrintDebug($" Deleting {entry.Name} because it's bundled into SMAPI...");
+ entry.Delete();
+ continue;
+ }
+
// check paths
string newPath = Path.Combine(properModsDir.FullName, entry.Name);
if (isDir ? Directory.Exists(newPath) : File.Exists(newPath))
@@ -389,10 +444,7 @@ namespace StardewModdingApi.Installer
// move into mods
this.PrintDebug($" Moving {entry.Name} into the game's mod directory...");
- if (isDir)
- (entry as DirectoryInfo).MoveTo(newPath);
- else
- (entry as FileInfo).MoveTo(newPath);
+ this.Move(entry, newPath);
}
// delete if empty
@@ -404,5 +456,32 @@ namespace StardewModdingApi.Installer
modDir.Delete();
}
}
+
+ /// <summary>Move a filesystem entry to a new parent directory.</summary>
+ /// <param name="entry">The filesystem entry to move.</param>
+ /// <param name="newPath">The destination path.</param>
+ /// <remarks>We can't use <see cref="FileInfo.MoveTo"/> or <see cref="DirectoryInfo.MoveTo"/>, because those don't work across partitions.</remarks>
+ private void Move(FileSystemInfo entry, string newPath)
+ {
+ // file
+ if (entry is FileInfo)
+ {
+ FileInfo file = (FileInfo)entry;
+ file.CopyTo(newPath);
+ file.Delete();
+ }
+
+ // directory
+ else
+ {
+ Directory.CreateDirectory(newPath);
+
+ DirectoryInfo directory = (DirectoryInfo)entry;
+ foreach (FileSystemInfo child in directory.EnumerateFileSystemInfos())
+ this.Move(child, Path.Combine(newPath, child.Name));
+
+ directory.Delete();
+ }
+ }
}
}
diff --git a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj
index 0a33cd57..9baa0d14 100644
--- a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj
+++ b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj
@@ -50,6 +50,7 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
+ <Import Project="$(SolutionDir)\crossplatform.targets" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- package files -->
<Target Name="AfterBuild">
diff --git a/src/StardewModdingAPI.sln b/src/StardewModdingAPI.sln
index d97e4645..6e13b16b 100644
--- a/src/StardewModdingAPI.sln
+++ b/src/StardewModdingAPI.sln
@@ -11,6 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "metadata", "metadata", "{86
ProjectSection(SolutionItems) = preProject
..\.gitattributes = ..\.gitattributes
..\.gitignore = ..\.gitignore
+ crossplatform.targets = crossplatform.targets
GlobalAssemblyInfo.cs = GlobalAssemblyInfo.cs
..\LICENSE = ..\LICENSE
..\README.md = ..\README.md
diff --git a/src/StardewModdingAPI/Advanced/ConfigFile.cs b/src/StardewModdingAPI/Advanced/ConfigFile.cs
index 1aba2f2c..1a2e6618 100644
--- a/src/StardewModdingAPI/Advanced/ConfigFile.cs
+++ b/src/StardewModdingAPI/Advanced/ConfigFile.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Advanced
/*********
** Accessors
*********/
- /// <summary>Provides methods for interacting with the mod directory, including read/writing the config file.</summary>
+ /// <summary>Provides simplified APIs for writing mods.</summary>
public IModHelper ModHelper { get; set; }
/// <summary>The file path from which the model was loaded, relative to the mod directory.</summary>
diff --git a/src/StardewModdingAPI/Advanced/IConfigFile.cs b/src/StardewModdingAPI/Advanced/IConfigFile.cs
index 841f4c58..5bc31a88 100644
--- a/src/StardewModdingAPI/Advanced/IConfigFile.cs
+++ b/src/StardewModdingAPI/Advanced/IConfigFile.cs
@@ -6,7 +6,7 @@
/*********
** Accessors
*********/
- /// <summary>Provides methods for interacting with the mod directory, including read/writing the config file.</summary>
+ /// <summary>Provides simplified APIs for writing mods.</summary>
IModHelper ModHelper { get; set; }
/// <summary>The file path from which the model was loaded, relative to the mod directory.</summary>
diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs
index 3feb0830..f5b9e70a 100644
--- a/src/StardewModdingAPI/Constants.cs
+++ b/src/StardewModdingAPI/Constants.cs
@@ -26,7 +26,7 @@ namespace StardewModdingAPI
** Accessors
*********/
/// <summary>SMAPI's current semantic version.</summary>
- public static readonly Version Version = new Version(1, 3, 0, null);
+ public static readonly Version Version = new Version(1, 4, 0, null);
/// <summary>The minimum supported version of Stardew Valley.</summary>
public const string MinimumGameVersion = "1.1";
diff --git a/src/StardewModdingAPI/Framework/AssemblyRewriting/AssemblyTypeRewriter.cs b/src/StardewModdingAPI/Framework/AssemblyRewriting/AssemblyTypeRewriter.cs
index 3459488e..9d4d6b11 100644
--- a/src/StardewModdingAPI/Framework/AssemblyRewriting/AssemblyTypeRewriter.cs
+++ b/src/StardewModdingAPI/Framework/AssemblyRewriting/AssemblyTypeRewriter.cs
@@ -54,7 +54,8 @@ namespace StardewModdingAPI.Framework.AssemblyRewriting
/// <summary>Rewrite the types referenced by an assembly.</summary>
/// <param name="assembly">The assembly to rewrite.</param>
- public void RewriteAssembly(AssemblyDefinition assembly)
+ /// <returns>Returns whether the assembly was modified.</returns>
+ public bool RewriteAssembly(AssemblyDefinition assembly)
{
ModuleDefinition module = assembly.Modules.Single(); // technically an assembly can have multiple modules, but none of the build tools (including MSBuild) support it; simplify by assuming one module
@@ -71,7 +72,7 @@ namespace StardewModdingAPI.Framework.AssemblyRewriting
}
}
if (!shouldRewrite)
- return;
+ return false;
// add target assembly references
foreach (AssemblyNameReference target in this.AssemblyMap.TargetReferences.Values)
@@ -117,6 +118,7 @@ namespace StardewModdingAPI.Framework.AssemblyRewriting
}
method.Body.OptimizeMacros();
}
+ return true;
}
diff --git a/src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs b/src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs
new file mode 100644
index 00000000..3dfbc78c
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/AssemblyRewriting/CacheEntry.cs
@@ -0,0 +1,46 @@
+using System.IO;
+
+namespace StardewModdingAPI.Framework.AssemblyRewriting
+{
+ /// <summary>Represents cached metadata for a rewritten assembly.</summary>
+ internal class CacheEntry
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The MD5 hash for the original assembly.</summary>
+ public readonly string Hash;
+
+ /// <summary>The SMAPI version used to rewrite the assembly.</summary>
+ public readonly string ApiVersion;
+
+ /// <summary>Whether to use the cached assembly instead of the original assembly.</summary>
+ public readonly bool UseCachedAssembly;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <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="useCachedAssembly">Whether to use the cached assembly instead of the original assembly.</param>
+ public CacheEntry(string hash, string apiVersion, bool useCachedAssembly)
+ {
+ this.Hash = hash;
+ this.ApiVersion = apiVersion;
+ this.UseCachedAssembly = useCachedAssembly;
+ }
+
+ /// <summary>Get whether the cache entry is up-to-date for the given assembly hash.</summary>
+ /// <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, Version currentVersion)
+ {
+ return hash == this.Hash
+ && this.ApiVersion == currentVersion.ToString()
+ && (!this.UseCachedAssembly || File.Exists(paths.Assembly));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/StardewModdingAPI/Framework/AssemblyRewriting/CachePaths.cs b/src/StardewModdingAPI/Framework/AssemblyRewriting/CachePaths.cs
index 17c4d188..18861873 100644
--- a/