summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Framework')
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs2
-rw-r--r--src/SMAPI/Framework/DeprecationManager.cs27
-rw-r--r--src/SMAPI/Framework/DeprecationWarning.cs7
-rw-r--r--src/SMAPI/Framework/Input/SInputState.cs6
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModHelpers/DataHelper.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs15
-rw-r--r--src/SMAPI/Framework/ModRegistry.cs10
-rw-r--r--src/SMAPI/Framework/SCore.cs3
-rw-r--r--src/SMAPI/Framework/SGame.cs19
10 files changed, 66 insertions, 27 deletions
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index 4dd1b6e1..ee654081 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -228,7 +228,7 @@ namespace StardewModdingAPI.Framework
{
IAssetInfo info = new AssetInfo(locale, assetName, type, this.MainContentManager.AssertAndNormaliseAssetName);
return predicate(info);
- });
+ }, dispose);
}
/// <summary>Purge matched assets from the cache.</summary>
diff --git a/src/SMAPI/Framework/DeprecationManager.cs b/src/SMAPI/Framework/DeprecationManager.cs
index 76c2f616..fcdf722e 100644
--- a/src/SMAPI/Framework/DeprecationManager.cs
+++ b/src/SMAPI/Framework/DeprecationManager.cs
@@ -62,7 +62,7 @@ namespace StardewModdingAPI.Framework
return;
// queue warning
- this.QueuedWarnings.Add(new DeprecationWarning(source, nounPhrase, version, severity));
+ this.QueuedWarnings.Add(new DeprecationWarning(source, nounPhrase, version, severity, Environment.StackTrace));
}
/// <summary>Print any queued messages.</summary>
@@ -78,27 +78,40 @@ namespace StardewModdingAPI.Framework
? $"{warning.ModName ?? "An unknown mod"} uses deprecated code (legacy events are deprecated since SMAPI {warning.Version})."
: $"{warning.ModName ?? "An unknown mod"} uses deprecated code ({warning.NounPhrase} is deprecated since SMAPI {warning.Version}).";
#endif
- if (warning.ModName == null)
- message += $"{Environment.NewLine}{Environment.StackTrace}";
- // log message
+ // get log level
+ LogLevel level;
switch (warning.Level)
{
case DeprecationLevel.Notice:
- this.Monitor.Log(message, LogLevel.Trace);
+ level = LogLevel.Trace;
break;
case DeprecationLevel.Info:
- this.Monitor.Log(message, LogLevel.Debug);
+ level = LogLevel.Debug;
break;
case DeprecationLevel.PendingRemoval:
- this.Monitor.Log(message, LogLevel.Warn);
+ level = LogLevel.Warn;
break;
default:
throw new NotSupportedException($"Unknown deprecation level '{warning.Level}'.");
}
+
+ // log message
+ if (warning.ModName != null)
+ this.Monitor.Log(message, level);
+ else
+ {
+ if (level == LogLevel.Trace)
+ this.Monitor.Log($"{message}\n{warning.StackTrace}", level);
+ else
+ {
+ this.Monitor.Log(message, level);
+ this.Monitor.Log(warning.StackTrace);
+ }
+ }
}
this.QueuedWarnings.Clear();
}
diff --git a/src/SMAPI/Framework/DeprecationWarning.cs b/src/SMAPI/Framework/DeprecationWarning.cs
index 25415012..5201b06c 100644
--- a/src/SMAPI/Framework/DeprecationWarning.cs
+++ b/src/SMAPI/Framework/DeprecationWarning.cs
@@ -18,6 +18,9 @@ namespace StardewModdingAPI.Framework
/// <summary>The deprecation level for the affected code.</summary>
public DeprecationLevel Level { get; }
+ /// <summary>The stack trace when the deprecation warning was raised.</summary>
+ public string StackTrace { get; }
+
/*********
** Public methods
@@ -27,12 +30,14 @@ namespace StardewModdingAPI.Framework
/// <param name="nounPhrase">A noun phrase describing what is deprecated.</param>
/// <param name="version">The SMAPI version which deprecated it.</param>
/// <param name="level">The deprecation level for the affected code.</param>
- public DeprecationWarning(string modName, string nounPhrase, string version, DeprecationLevel level)
+ /// <param name="stackTrace">The stack trace when the deprecation warning was raised.</param>
+ public DeprecationWarning(string modName, string nounPhrase, string version, DeprecationLevel level, string stackTrace)
{
this.ModName = modName;
this.NounPhrase = nounPhrase;
this.Version = version;
this.Level = level;
+ this.StackTrace = stackTrace;
}
}
}
diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs
index 0228db0d..96a7003a 100644
--- a/src/SMAPI/Framework/Input/SInputState.cs
+++ b/src/SMAPI/Framework/Input/SInputState.cs
@@ -20,6 +20,9 @@ namespace StardewModdingAPI.Framework.Input
/// <summary>The cursor position on the screen adjusted for the zoom level.</summary>
private CursorPosition CursorPositionImpl;
+ /// <summary>The player's last known tile position.</summary>
+ private Vector2? LastPlayerTile;
+
/*********
** Accessors
@@ -83,13 +86,14 @@ namespace StardewModdingAPI.Framework.Input
MouseState realMouse = Mouse.GetState();
var activeButtons = this.DeriveStatuses(this.ActiveButtons, realKeyboard, realMouse, realController);
Vector2 cursorAbsolutePos = new Vector2(realMouse.X + Game1.viewport.X, realMouse.Y + Game1.viewport.Y);
+ Vector2? playerTilePos = Context.IsPlayerFree ? Game1.player.getTileLocation() : (Vector2?)null;
// update real states
this.ActiveButtons = activeButtons;
this.RealController = realController;
this.RealKeyboard = realKeyboard;
this.RealMouse = realMouse;
- if (this.CursorPositionImpl?.AbsolutePixels != cursorAbsolutePos)
+ if (cursorAbsolutePos != this.CursorPositionImpl?.AbsolutePixels || playerTilePos != this.LastPlayerTile)
this.CursorPositionImpl = this.GetCursorPosition(realMouse, cursorAbsolutePos);
// update suppressed states
diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
index dac627ba..7c353003 100644
--- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs
@@ -319,7 +319,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
foreach (string candidateKey in new[] { imageSource, $@"Maps\{imageSource}" })
{
string contentKey = candidateKey.EndsWith(".png")
- ? candidateKey.Substring(0, imageSource.Length - 4)
+ ? candidateKey.Substring(0, candidateKey.Length - 4)
: candidateKey;
try
diff --git a/src/SMAPI/Framework/ModHelpers/DataHelper.cs b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
index 2cb886ba..3b5c1752 100644
--- a/src/SMAPI/Framework/ModHelpers/DataHelper.cs
+++ b/src/SMAPI/Framework/ModHelpers/DataHelper.cs
@@ -97,7 +97,7 @@ namespace StardewModdingAPI.Framework.ModHelpers
if (!Game1.hasLoadedGame)
throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} when a save file isn't loaded.");
if (!Game1.IsMasterGame)
- throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.ReadSaveData)} because this isn't the main player. (Save files are stored on the main player's computer.)");
+ throw new InvalidOperationException($"Can't use {nameof(IMod.Helper)}.{nameof(IModHelper.Data)}.{nameof(this.WriteSaveData)} because this isn't the main player. (Save files are stored on the main player's computer.)");
string internalKey = this.GetSaveFileKey(key);
if (data != null)
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index 835b0a54..a8564524 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -137,12 +137,23 @@ namespace StardewModdingAPI.Framework.ModLoading
}
// invalid path
- string assemblyPath = Path.Combine(mod.DirectoryPath, mod.Manifest.EntryDll);
- if (!File.Exists(assemblyPath))
+ if (!File.Exists(Path.Combine(mod.DirectoryPath, mod.Manifest.EntryDll)))
{
mod.SetStatus(ModMetadataStatus.Failed, $"its DLL '{mod.Manifest.EntryDll}' doesn't exist.");
continue;
}
+
+ // invalid capitalisation
+ string actualFilename = new DirectoryInfo(mod.DirectoryPath).GetFiles(mod.Manifest.EntryDll).FirstOrDefault()?.Name;
+ if (actualFilename != mod.Manifest.EntryDll)
+ {
+#if SMAPI_3_0_STRICT
+ mod.SetStatus(ModMetadataStatus.Failed, $"its {nameof(IManifest.EntryDll)} value '{mod.Manifest.EntryDll}' doesn't match the actual file capitalisation '{actualFilename}'. The capitalisation must match for crossplatform compatibility.");
+ continue;
+#else
+ SCore.DeprecationManager.Warn(mod.DisplayName, $"{nameof(IManifest.EntryDll)} value with case-insensitive capitalisation", "2.11", DeprecationLevel.Info);
+#endif
+ }
}
// validate content pack
diff --git a/src/SMAPI/Framework/ModRegistry.cs b/src/SMAPI/Framework/ModRegistry.cs
index e9ceb66e..5be33cb4 100644
--- a/src/SMAPI/Framework/ModRegistry.cs
+++ b/src/SMAPI/Framework/ModRegistry.cs
@@ -33,8 +33,14 @@ namespace StardewModdingAPI.Framework
public void Add(IModMetadata metadata)
{
this.Mods.Add(metadata);
- if (!metadata.IsContentPack)
- this.ModNamesByAssembly[metadata.Mod.GetType().Assembly.FullName] = metadata;
+ }
+
+ /// <summary>Track a mod's assembly for use via <see cref="GetFrom"/>.</summary>
+ /// <param name="metadata">The mod metadata.</param>
+ /// <param name="modAssembly">The mod assembly.</param>
+ public void TrackAssemblies(IModMetadata metadata, Assembly modAssembly)
+ {
+ this.ModNamesByAssembly[modAssembly.FullName] = metadata;
}
/// <summary>Get metadata for all loaded mods.</summary>
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 27c0c40b..ec3e9f72 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -977,11 +977,12 @@ namespace StardewModdingAPI.Framework
try
{
modAssembly = assemblyLoader.Load(mod, assemblyPath, assumeCompatible: mod.DataRecord?.Status == ModStatus.AssumeCompatible);
+ this.ModRegistry.TrackAssemblies(mod, modAssembly);
}
catch (IncompatibleInstructionException) // details already in trace logs
{
string[] updateUrls = new[] { modDatabase.GetModPageUrlFor(manifest.UniqueID), "https://mods.smapi.io" }.Where(p => p != null).ToArray();
- errorReasonPhrase = $"it's no longer compatible. Please check for a new version at {string.Join(" or ", updateUrls)}.";
+ errorReasonPhrase = $"it's no longer compatible. Please check for a new version at {string.Join(" or ", updateUrls)}";
return false;
}
catch (SAssemblyLoadFailedException ex)
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index 25ffcabd..9818314a 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -69,9 +69,6 @@ namespace StardewModdingAPI.Framework
/// <remarks>Skipping a few frames ensures the game finishes initialising the world before mods try to change it.</remarks>
private readonly Countdown AfterLoadTimer = new Countdown(5);
- /// <summary>The current stage in the game's loading process.</summary>
- private LoadStage LoadStage = LoadStage.None;
-
/// <summary>Whether the game is saving and SMAPI has already raised <see cref="IGameLoopEvents.Saving"/>.</summary>
private bool IsBetweenSaveEvents;
@@ -215,12 +212,12 @@ namespace StardewModdingAPI.Framework
internal void OnLoadStageChanged(LoadStage newStage)
{
// nothing to do
- if (newStage == this.LoadStage)
+ if (newStage == Context.LoadStage)
return;
// update data
- LoadStage oldStage = this.LoadStage;
- this.LoadStage = newStage;
+ LoadStage oldStage = Context.LoadStage;
+ Context.LoadStage = newStage;
if (newStage == LoadStage.None)
{
this.Monitor.Log("Context: returned to title", LogLevel.Trace);
@@ -293,6 +290,7 @@ namespace StardewModdingAPI.Framework
// Run async tasks synchronously to avoid issues due to mod events triggering
// concurrently with game code.
+ bool saveParsed = false;
if (Game1.currentLoader != null)
{
this.Monitor.Log("Game loader synchronising...", LogLevel.Trace);
@@ -301,7 +299,8 @@ namespace StardewModdingAPI.Framework
// raise load stage changed
switch (Game1.currentLoader.Current)
{
- case 20:
+ case 20 when (!saveParsed && SaveGame.loaded != null):
+ saveParsed = true;
this.OnLoadStageChanged(LoadStage.SaveParsed);
break;
@@ -511,10 +510,10 @@ namespace StardewModdingAPI.Framework
*********/
if (wasWorldReady && !Context.IsWorldReady)
this.OnLoadStageChanged(LoadStage.None);
- else if (Context.IsWorldReady && this.LoadStage != LoadStage.Ready)
+ else if (Context.IsWorldReady && Context.LoadStage != LoadStage.Ready)
{
// print context
- string context = $"Context: loaded saved game '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.";
+ string context = $"Context: loaded save '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}, locale set to {this.ContentCore.Language}.";
if (Context.IsMultiplayer)
{
int onlineCount = Game1.getOnlineFarmers().Count();
@@ -884,7 +883,7 @@ namespace StardewModdingAPI.Framework
events.GameLaunched.Raise(new GameLaunchedEventArgs());
// preloaded
- if (Context.IsSaveLoaded && this.LoadStage != LoadStage.Loaded && this.LoadStage != LoadStage.Ready)
+ if (Context.IsSaveLoaded && Context.LoadStage != LoadStage.Loaded && Context.LoadStage != LoadStage.Ready)
this.OnLoadStageChanged(LoadStage.Loaded);
// update tick