summaryrefslogtreecommitdiff
path: root/src/SMAPI
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2018-08-19 18:28:16 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2018-08-19 18:28:16 -0400
commit826dd53ab550e5b92796c510569118beee6bd044 (patch)
treed6d8ec7f380112e8ed047a932ae02548dc9b8a2b /src/SMAPI
parent417c04076634ea87d7b3030a1acf46825da6e3e6 (diff)
downloadSMAPI-826dd53ab550e5b92796c510569118beee6bd044.tar.gz
SMAPI-826dd53ab550e5b92796c510569118beee6bd044.tar.bz2
SMAPI-826dd53ab550e5b92796c510569118beee6bd044.zip
move most SMAPI files into subfolder (#582)
Diffstat (limited to 'src/SMAPI')
-rw-r--r--src/SMAPI/Constants.cs7
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoader.cs1
-rw-r--r--src/SMAPI/Program.cs85
3 files changed, 63 insertions, 30 deletions
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index bd512fb1..0e0ae239 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -64,11 +64,14 @@ namespace StardewModdingAPI
/// <summary>The URL of the SMAPI home page.</summary>
internal const string HomePageUrl = "https://smapi.io";
+ /// <summary>The absolute path to the folder containing SMAPI's internal files.</summary>
+ internal static readonly string InternalFilesPath = Program.DllSearchPath;
+
/// <summary>The file path for the SMAPI configuration file.</summary>
- internal static string ApiConfigPath => Path.Combine(Constants.ExecutionPath, $"{typeof(Program).Assembly.GetName().Name}.config.json");
+ internal static string ApiConfigPath => Path.Combine(Constants.InternalFilesPath, "StardewModdingAPI.config.json");
/// <summary>The file path for the SMAPI metadata file.</summary>
- internal static string ApiMetadataPath => Path.Combine(Constants.ExecutionPath, $"{typeof(Program).Assembly.GetName().Name}.metadata.json");
+ internal static string ApiMetadataPath => Path.Combine(Constants.InternalFilesPath, "StardewModdingAPI.metadata.json");
/// <summary>The filename prefix used for all SMAPI logs.</summary>
internal static string LogNamePrefix { get; } = "SMAPI-";
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
index 37b1a378..e750c659 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -45,6 +45,7 @@ namespace StardewModdingAPI.Framework.ModLoading
this.AssemblyMap = this.TrackForDisposal(Constants.GetAssemblyMap(targetPlatform));
this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver());
this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.ExecutionPath);
+ this.AssemblyDefinitionResolver.AddSearchDirectory(Constants.InternalFilesPath);
// generate type => assembly lookup for types which should be rewritten
this.TypeAssemblies = new Dictionary<string, Assembly>();
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index c40d2ff6..64eeb45a 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -45,6 +45,11 @@ namespace StardewModdingAPI
/*********
** Properties
*********/
+ /// <summary>The absolute path to search for SMAPI's internal DLLs.</summary>
+ /// <remarks>We can't use <see cref="Constants.ExecutionPath"/> directly, since <see cref="Constants"/> depends on DLLs loaded from this folder.</remarks>
+ [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute", Justification = "The assembly location is never null in this context.")]
+ internal static readonly string DllSearchPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "smapi-internal");
+
/// <summary>The log file to which to write messages.</summary>
private readonly LogFileManager LogFile;
@@ -111,6 +116,8 @@ namespace StardewModdingAPI
/// <param name="args">The command-line arguments.</param>
public static void Main(string[] args)
{
+ // initial setup
+ AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
Program.AssertMinimumCompatibility();
// get flags from arguments
@@ -135,10 +142,48 @@ namespace StardewModdingAPI
program.RunInteractively();
}
+ /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+ public void Dispose()
+ {
+ // skip if already disposed
+ if (this.IsDisposed)
+ return;
+ this.IsDisposed = true;
+ this.Monitor.Log("Disposing...", LogLevel.Trace);
+
+ // dispose mod data
+ foreach (IModMetadata mod in this.ModRegistry.GetAll())
+ {
+ try
+ {
+ (mod.Mod as IDisposable)?.Dispose();
+ }
+ catch (Exception ex)
+ {
+ mod.LogAsMod($"Mod failed during disposal: {ex.GetLogSummary()}.", LogLevel.Warn);
+ }
+ }
+
+ // dispose core components
+ this.IsGameRunning = false;
+ this.ConsoleManager?.Dispose();
+ this.ContentCore?.Dispose();
+ this.CancellationTokenSource?.Dispose();
+ this.GameInstance?.Dispose();
+ this.LogFile?.Dispose();
+
+ // end game (moved from Game1.OnExiting to let us clean up first)
+ Process.GetCurrentProcess().Kill();
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
/// <summary>Construct an instance.</summary>
/// <param name="modsPath">The path to search for mods.</param>
/// <param name="writeToConsole">Whether to output log messages to the console.</param>
- public Program(string modsPath, bool writeToConsole)
+ private Program(string modsPath, bool writeToConsole)
{
// init paths
this.VerifyPath(modsPath);
@@ -189,7 +234,7 @@ namespace StardewModdingAPI
/// <summary>Launch SMAPI.</summary>
[HandleProcessCorruptedStateExceptions, SecurityCritical] // let try..catch handle corrupted state exceptions
- public void RunInteractively()
+ private void RunInteractively()
{
// initialise SMAPI
try
@@ -320,44 +365,28 @@ namespace StardewModdingAPI
}
}
- /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
- public void Dispose()
+ /// <summary>Method called when assembly resolution fails, which may return a manually resolved assembly.</summary>
+ /// <param name="sender">The event sender.</param>
+ /// <param name="e">The event arguments.</param>
+ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs e)
{
- // skip if already disposed
- if (this.IsDisposed)
- return;
- this.IsDisposed = true;
- this.Monitor.Log("Disposing...", LogLevel.Trace);
-
- // dispose mod data
- foreach (IModMetadata mod in this.ModRegistry.GetAll())
+ AssemblyName name = new AssemblyName(e.Name);
+ foreach (FileInfo dll in new DirectoryInfo(Program.DllSearchPath).EnumerateFiles("*.dll"))
{
try
{
- (mod.Mod as IDisposable)?.Dispose();
+ if (name.Name.Equals(AssemblyName.GetAssemblyName(dll.FullName).Name, StringComparison.InvariantCultureIgnoreCase))
+ return Assembly.LoadFrom(dll.FullName);
}
catch (Exception ex)
{
- mod.LogAsMod($"Mod failed during disposal: {ex.GetLogSummary()}.", LogLevel.Warn);
+ throw new InvalidOperationException($"Could not load dependency 'smapi-lib/{dll.Name}'. Consider deleting the smapi-lib folder and reinstalling SMAPI.", ex);
}
}
- // dispose core components
- this.IsGameRunning = false;
- this.ConsoleManager?.Dispose();
- this.ContentCore?.Dispose();
- this.CancellationTokenSource?.Dispose();
- this.GameInstance?.Dispose();
- this.LogFile?.Dispose();
-
- // end game (moved from Game1.OnExiting to let us clean up first)
- Process.GetCurrentProcess().Kill();
+ return null;
}
-
- /*********
- ** Private methods
- *********/
/// <summary>Assert that the minimum conditions are present to initialise SMAPI without type load exceptions.</summary>
private static void AssertMinimumCompatibility()
{