summaryrefslogtreecommitdiff
path: root/src/SMAPI
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-05-03 18:11:31 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2021-05-03 18:11:31 -0400
commit5d3d919d490fd414fe9647e566e92c71d7f64509 (patch)
treee1eab3352287ef04b5de4cdc28b550e255d58c3f /src/SMAPI
parentc48f6d78cc412c5f2e40a8b460b7b3c1c993c51a (diff)
parent3447e2f575c2c83af729777e4d37e93f4c2a6467 (diff)
downloadSMAPI-5d3d919d490fd414fe9647e566e92c71d7f64509.tar.gz
SMAPI-5d3d919d490fd414fe9647e566e92c71d7f64509.tar.bz2
SMAPI-5d3d919d490fd414fe9647e566e92c71d7f64509.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI')
-rw-r--r--src/SMAPI/Constants.cs76
-rw-r--r--src/SMAPI/Context.cs2
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForImage.cs4
-rw-r--r--src/SMAPI/Framework/Content/ContentCache.cs2
-rw-r--r--src/SMAPI/Framework/ContentCoordinator.cs2
-rw-r--r--src/SMAPI/Framework/ContentManagers/ModContentManager.cs30
-rw-r--r--src/SMAPI/Framework/ContentPack.cs2
-rw-r--r--src/SMAPI/Framework/IModMetadata.cs5
-rw-r--r--src/SMAPI/Framework/Logging/LogFileManager.cs2
-rw-r--r--src/SMAPI/Framework/Logging/LogManager.cs194
-rw-r--r--src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs2
-rw-r--r--src/SMAPI/Framework/ModLoading/ModMetadata.cs53
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs4
-rw-r--r--src/SMAPI/Framework/Models/SConfig.cs3
-rw-r--r--src/SMAPI/Framework/SCore.cs79
-rw-r--r--src/SMAPI/Framework/Serialization/ColorConverter.cs2
-rw-r--r--src/SMAPI/Framework/Serialization/PointConverter.cs2
-rw-r--r--src/SMAPI/Framework/Serialization/RectangleConverter.cs2
-rw-r--r--src/SMAPI/Framework/Serialization/Vector2Converter.cs2
-rw-r--r--src/SMAPI/GamePlatform.cs2
-rw-r--r--src/SMAPI/Metadata/CoreAssetPropagator.cs42
-rw-r--r--src/SMAPI/Program.cs4
-rw-r--r--src/SMAPI/SMAPI.config.json9
-rw-r--r--src/SMAPI/SMAPI.csproj6
24 files changed, 372 insertions, 159 deletions
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index 8b0c952d..3c21b205 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -38,6 +38,14 @@ namespace StardewModdingAPI
/// <summary>The target game platform.</summary>
internal static GamePlatform Platform { get; } = (GamePlatform)Enum.Parse(typeof(GamePlatform), LowLevelEnvironmentUtility.DetectPlatform());
+ /// <summary>Whether SMAPI is being compiled for Windows with a 64-bit Linux version of the game. This is highly specialized and shouldn't be used in most cases.</summary>
+ internal static bool IsWindows64BitHack { get; } =
+#if SMAPI_FOR_WINDOWS_64BIT_HACK
+ true;
+#else
+ false;
+#endif
+
/// <summary>The game framework running the game.</summary>
internal static GameFramework GameFramework { get; } =
#if SMAPI_FOR_XNA
@@ -47,10 +55,13 @@ namespace StardewModdingAPI
#endif
/// <summary>The game's assembly name.</summary>
- internal static string GameAssemblyName => EarlyConstants.Platform == GamePlatform.Windows ? "Stardew Valley" : "StardewValley";
+ internal static string GameAssemblyName => EarlyConstants.Platform == GamePlatform.Windows && !EarlyConstants.IsWindows64BitHack ? "Stardew Valley" : "StardewValley";
/// <summary>The <see cref="Context.ScreenId"/> value which should appear in the SMAPI log, if any.</summary>
internal static int? LogScreenId { get; set; }
+
+ /// <summary>SMAPI's current raw semantic version.</summary>
+ internal static string RawApiVersion = "3.10.0";
}
/// <summary>Contains SMAPI's constants and assumptions.</summary>
@@ -63,7 +74,7 @@ namespace StardewModdingAPI
** Public
****/
/// <summary>SMAPI's current semantic version.</summary>
- public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.9.5");
+ public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion(EarlyConstants.RawApiVersion);
/// <summary>The minimum supported version of Stardew Valley.</summary>
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.4");
@@ -231,33 +242,27 @@ namespace StardewModdingAPI
targetAssemblies.Add(typeof(StardewModdingAPI.IManifest).Assembly);
// get changes for platform
- switch (targetPlatform)
+ if (Constants.Platform != Platform.Windows || EarlyConstants.IsWindows64BitHack)
{
- case Platform.Linux:
- case Platform.Mac:
- removeAssemblyReferences.AddRange(new[]
- {
- "Netcode",
- "Stardew Valley"
- });
- targetAssemblies.Add(
- typeof(StardewValley.Game1).Assembly // note: includes Netcode types on Linux/Mac
- );
- break;
-
- case Platform.Windows:
- removeAssemblyReferences.Add(
- "StardewValley"
- );
- targetAssemblies.AddRange(new[]
- {
- typeof(Netcode.NetBool).Assembly,
- typeof(StardewValley.Game1).Assembly
- });
- break;
-
- default:
- throw new InvalidOperationException($"Unknown target platform '{targetPlatform}'.");
+ removeAssemblyReferences.AddRange(new[]
+ {
+ "Netcode",
+ "Stardew Valley"
+ });
+ targetAssemblies.Add(
+ typeof(StardewValley.Game1).Assembly // note: includes Netcode types on Linux/macOS
+ );
+ }
+ else
+ {
+ removeAssemblyReferences.Add(
+ "StardewValley"
+ );
+ targetAssemblies.AddRange(new[]
+ {
+ typeof(Netcode.NetBool).Assembly,
+ typeof(StardewValley.Game1).Assembly
+ });
}
// get changes for game framework
@@ -295,6 +300,21 @@ namespace StardewModdingAPI
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences.ToArray(), targetAssemblies.ToArray());
}
+ /// <summary>Get whether the game assembly was patched by Stardew64Installer.</summary>
+ /// <param name="version">The version of Stardew64Installer which was applied to the game assembly, if any.</param>
+ internal static bool IsPatchedByStardew64Installer(out ISemanticVersion version)
+ {
+ PropertyInfo property = typeof(Game1).GetProperty("Stardew64InstallerVersion");
+ if (property == null)
+ {
+ version = null;
+ return false;
+ }
+
+ version = new SemanticVersion((string)property.GetValue(null));
+ return true;
+ }
+
/*********
** Private methods
diff --git a/src/SMAPI/Context.cs b/src/SMAPI/Context.cs
index 5f70d0f7..a745592c 100644
--- a/src/SMAPI/Context.cs
+++ b/src/SMAPI/Context.cs
@@ -86,7 +86,7 @@ namespace StardewModdingAPI
public static bool HasRemotePlayers => Context.IsMultiplayer && !Game1.hasLocalClientsOnly;
/// <summary>Whether the current player is the main player. This is always true in single-player, and true when hosting in multiplayer.</summary>
- public static bool IsMainPlayer => Game1.IsMasterGame && !(TitleMenu.subMenu is FarmhandMenu);
+ public static bool IsMainPlayer => Game1.IsMasterGame && Context.ScreenId == 0 && !(TitleMenu.subMenu is FarmhandMenu);
/*********
diff --git a/src/SMAPI/Framework/Content/AssetDataForImage.cs b/src/SMAPI/Framework/Content/AssetDataForImage.cs
index 5f91610e..529fb93a 100644
--- a/src/SMAPI/Framework/Content/AssetDataForImage.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForImage.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.Framework.Content
** Fields
*********/
/// <summary>The minimum value to consider non-transparent.</summary>
- /// <remarks>On Linux/Mac, fully transparent pixels may have an alpha up to 4 for some reason.</remarks>
+ /// <remarks>On Linux/macOS, fully transparent pixels may have an alpha up to 4 for some reason.</remarks>
private const byte MinOpacity = 5;
@@ -82,7 +82,7 @@ namespace StardewModdingAPI.Framework.Content
// premultiplied by the content pipeline. The formula is derived from
// https://blogs.msdn.microsoft.com/shawnhar/2009/11/06/premultiplied-alpha/.
// Note: don't use named arguments here since they're different between
- // Linux/Mac and Windows.
+ // Linux/macOS and Windows.
float alphaBelow = 1 - (above.A / 255f);
newData[i] = new Color(
(int)(above.R + (below.R * alphaBelow)), // r
diff --git a/src/SMAPI/Framework/Content/ContentCache.cs b/src/SMAPI/Framework/Content/ContentCache.cs
index 7edc9ab9..5c7ad778 100644
--- a/src/SMAPI/Framework/Content/ContentCache.cs
+++ b/src/SMAPI/Framework/Content/ContentCache.cs
@@ -57,6 +57,8 @@ namespace StardewModdingAPI.Framework.Content
IReflectedMethod method = reflection.GetMethod(typeof(TitleContainer), "GetCleanPath");
this.NormalizeAssetNameForPlatform = path => method.Invoke<string>(path);
}
+ else if (EarlyConstants.IsWindows64BitHack)
+ this.NormalizeAssetNameForPlatform = PathUtilities.NormalizePath;
else
this.NormalizeAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load<T> logic
}
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs
index 2920e670..d0e759c2 100644
--- a/src/SMAPI/Framework/ContentCoordinator.cs
+++ b/src/SMAPI/Framework/ContentCoordinator.cs
@@ -132,7 +132,7 @@ namespace StardewModdingAPI.Framework
);
this.ContentManagers.Add(contentManagerForAssetPropagation);
this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory);
- this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, reflection, aggressiveMemoryOptimizations);
+ this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations);
}
/// <summary>Get a new content manager which handles reading files from the game content folder with support for interception.</summary>
diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index 9af14cb5..4f6aa775 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -34,6 +34,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// <summary>The language code for language-agnostic mod assets.</summary>
private readonly LanguageCode DefaultLanguage = Constants.DefaultLanguage;
+ /// <summary>If a map tilesheet's image source has no file extensions, the file extensions to check for in the local mod folder.</summary>
+ private readonly string[] LocalTilesheetExtensions = { ".png", ".xnb" };
+
/*********
** Public methods
@@ -215,11 +218,17 @@ namespace StardewModdingAPI.Framework.ContentManagers
FileInfo file = new FileInfo(Path.Combine(this.FullRootDirectory, path));
// try with default extension
- if (!file.Exists && file.Extension.ToLower() != ".xnb")
+ if (!file.Exists && file.Extension == string.Empty)
{
- FileInfo result = new FileInfo(file.FullName + ".xnb");
- if (result.Exists)
- file = result;
+ foreach (string extension in this.LocalTilesheetExtensions)
+ {
+ FileInfo result = new FileInfo(file.FullName + extension);
+ if (result.Exists)
+ {
+ file = result;
+ break;
+ }
+ }
}
return file;
@@ -259,6 +268,7 @@ namespace StardewModdingAPI.Framework.ContentManagers
string relativeMapFolder = Path.GetDirectoryName(relativeMapPath) ?? ""; // folder path containing the map, relative to the mod folder
// fix tilesheets
+ this.Monitor.VerboseLog($"Fixing tilesheet paths for map '{relativeMapPath}' from mod '{this.ModName}'...");
foreach (TileSheet tilesheet in map.TileSheets)
{
// get image source
@@ -280,6 +290,9 @@ namespace StardewModdingAPI.Framework.ContentManagers
if (!this.TryGetTilesheetAssetName(relativeMapFolder, imageSource, out string assetName, out string error))
throw new SContentLoadException($"{errorPrefix} {error}");
+ if (assetName != tilesheet.ImageSource)
+ this.Monitor.VerboseLog($" Mapped tilesheet '{tilesheet.ImageSource}' to '{assetName}'.");
+
tilesheet.ImageSource = assetName;
}
catch (Exception ex) when (!(ex is SContentLoadException))
@@ -308,6 +321,15 @@ namespace StardewModdingAPI.Framework.ContentManagers
return true;
}
+ // special case: local filenames starting with a dot should be ignored
+ // For example, this lets mod authors have a '.spring_town.png' file in their map folder so it can be
+ // opened in Tiled, while still mapping it to the vanilla 'Maps/spring_town' asset at runtime.
+ {
+ string filename = Path.GetFileName(relativePath);
+ if (filename.StartsWith("."))
+ relativePath = Path.Combine(Path.GetDirectoryName(relativePath) ?? "", filename.TrimStart('.'));
+ }
+
// get relative to map file
{
string localKey = Path.Combine(modRelativeMapFolder, relativePath);
diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs
index a6835dbe..0660a367 100644
--- a/src/SMAPI/Framework/ContentPack.cs
+++ b/src/SMAPI/Framework/ContentPack.cs
@@ -18,7 +18,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Encapsulates SMAPI's JSON file parsing.</summary>
private readonly JsonHelper JsonHelper;
- /// <summary>A cache of case-insensitive => exact relative paths within the content pack, for case-insensitive file lookups on Linux/Mac.</summary>
+ /// <summary>A cache of case-insensitive => exact relative paths within the content pack, for case-insensitive file lookups on Linux/macOS.</summary>
private readonly IDictionary<string, string> RelativePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 5d2f352d..f5babafb 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -117,6 +117,11 @@ namespace StardewModdingAPI.Framework
/// <param name="validOnly">Only return valid update keys.</param>
IEnumerable<UpdateKey> GetUpdateKeys(bool validOnly = true);
+ /// <summary>Get whether the given mod ID must be installed to load this mod.</summary>
+ /// <param name="modId">The mod ID to check.</param>
+ /// <param name="includeOptional">Whether to include optional dependencies.</param>
+ bool HasRequiredModId(string modId, bool includeOptional);
+
/// <summary>Get the mod IDs that must be installed to load this mod.</summary>
/// <param name="includeOptional">Whether to include optional dependencies.</param>
IEnumerable<string> GetRequiredModIds(bool includeOptional = false);
diff --git a/src/SMAPI/Framework/Logging/LogFileManager.cs b/src/SMAPI/Framework/Logging/LogFileManager.cs
index 6b5babcd..6ab2bdfb 100644
--- a/src/SMAPI/Framework/Logging/LogFileManager.cs
+++ b/src/SMAPI/Framework/Logging/LogFileManager.cs
@@ -44,7 +44,7 @@ namespace StardewModdingAPI.Framework.Logging
public void WriteLine(string message)
{
// always use Windows-style line endings for convenience
- // (Linux/Mac editors are fine with them, Windows editors often require them)
+ // (Linux/macOS editors are fine with them, Windows editors often require them)
this.Stream.Write(message + "\r\n");
}
diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs
index 243ca3ae..a4df3c18 100644
--- a/src/SMAPI/Framework/Logging/LogManager.cs
+++ b/src/SMAPI/Framework/Logging/LogManager.cs
@@ -283,13 +283,16 @@ namespace StardewModdingAPI.Framework.Logging
/// <param name="customSettings">The custom SMAPI settings.</param>
public void LogIntro(string modsPath, IDictionary<string, object> customSettings)
{
- // get platform label
- string platformLabel = EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform);
- if ((Constants.GameFramework == GameFramework.Xna) != (Constants.Platform == Platform.Windows))
- platformLabel += $" with {Constants.GameFramework}";
+ // log platform & patches
+ {
+ this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform)}", LogLevel.Info);
+
+ string[] patchLabels = this.GetPatchLabels().ToArray();
+ if (patchLabels.Any())
+ this.Monitor.Log($"Detected custom version: {string.Join(", ", patchLabels)}", LogLevel.Info);
+ }
- // init logging
- this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {platformLabel}", LogLevel.Info);
+ // log basic info
this.Monitor.Log($"Mods go here: {modsPath}", LogLevel.Info);
if (modsPath != Constants.DefaultModsPath)
this.Monitor.Log("(Using custom --mods-path argument.)");
@@ -406,6 +409,20 @@ namespace StardewModdingAPI.Framework.Logging
gameMonitor.Log(message, level);
}
+ /// <summary>Get human-readable labels to log for detected SMAPI and Stardew Valley customizations.</summary>
+ private IEnumerable<string> GetPatchLabels()
+ {
+ // custom game framework
+ if (EarlyConstants.IsWindows64BitHack)
+ yield return $"running 64-bit SMAPI with {Constants.GameFramework}";
+ else if ((Constants.GameFramework == GameFramework.Xna) != (Constants.Platform == Platform.Windows))
+ yield return $"running {Constants.GameFramework}";
+
+ // patched by Stardew64Installer
+ if (Constants.IsPatchedByStardew64Installer(out ISemanticVersion patchedByVersion))
+ yield return $"patched by Stardew64Installer {patchedByVersion}";
+ }
+
/// <summary>Write a summary of mod warnings to the console and log.</summary>
/// <param name="mods">The loaded mods.</param>
/// <param name="skippedMods">The mods which could not be loaded.</param>
@@ -426,67 +443,38 @@ namespace StardewModdingAPI.Framework.Logging
// log skipped mods
if (skippedMods.Any())
{
- // get logging logic
- HashSet<string> loggedDuplicateIds = new HashSet<string>();
- void LogSkippedMod(IModMetadata mod)
- {
- string message = $" - {mod.DisplayName}{(mod.Manifest?.Version != null ? " " + mod.Manifest.Version.ToString() : "")} because {mod.Error}";
+ var loggedDuplicateIds = new HashSet<string>();
- // handle duplicate mods
- // (log first duplicate only, don't show redundant version)
- if (mod.FailReason == ModFailReason.Duplicate && mod.HasManifest())
+ this.Monitor.Log(" Skipped mods", LogLevel.Error);
+ this.Monitor.Log(" " + "".PadRight(50, '-'), LogLevel.Error);
+ this.Monitor.Log(" These mods could not be added to your game.", LogLevel.Error);
+ this.Monitor.Newline();
+ foreach (var list in this.GroupFailedModsByPriority(skippedMods))
+ {
+ if (list.Any())
{
- if (!loggedDuplicateIds.Add(mod.Manifest.UniqueID))
- return; // already logged
+ foreach (IModMetadata mod in list.OrderBy(p => p.DisplayName))
+ {
+ string message = $" - {mod.DisplayName}{(" " + mod.Manifest?.Version?.ToString()).TrimEnd()} because {mod.Error}";
- message = $" - {mod.DisplayName} because {mod.Error}";
- }
+ // duplicate mod: log first one only, don't show redundant version
+ if (mod.FailReason == ModFailReason.Duplicate && mod.HasManifest())
+ {
+ if (loggedDuplicateIds.Add(mod.Manifest.UniqueID))
+ continue; // already logged
- // log message
- this.Monitor.Log(message, LogLevel.Error);
- if (mod.ErrorDetails != null)
- this.Monitor.Log($" ({mod.ErrorDetails})");
- }
+ message = $" - {mod.DisplayName} because {mod.Error}";
+ }
- // group mods
- List<IModMetadata> skippedDependencies = new List<IModMetadata>();
- List<IModMetadata> otherSkippedMods = new List<IModMetadata>();
- {
- // track broken dependencies
- HashSet<string> skippedDependencyIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
- HashSet<string> skippedModIds = new HashSet<string>(from mod in skippedMods where mod.HasID() select mod.Manifest.UniqueID, StringComparer.OrdinalIgnoreCase);
- foreach (IModMetadata mod in skippedMods)
- {
- foreach (string requiredId in skippedModIds.Intersect(mod.GetRequiredModIds()))
- skippedDependencyIds.Add(requiredId);
- }
+ // log message
+ this.Monitor.Log(message, LogLevel.Error);
+ if (mod.ErrorDetails != null)
+ this.Monitor.Log($" ({mod.ErrorDetails})");
+ }
- // collect mod groups
- foreach (IModMetadata mod in skippedMods)
- {
- if (mod.HasID() && skippedDependencyIds.Contains(mod.Manifest.UniqueID))
- skippedDependencies.Add(mod);
- else
- otherSkippedMods.Add(mod);
+ this.Monitor.Newline();
}
}
-
- // log skipped mods
- this.Monitor.Log(" Skipped mods", LogLevel.Error);
- this.Monitor.Log(" " + "".PadRight(50, '-'), LogLevel.Error);
- this.Monitor.Log(" These mods could not be added to your game.", LogLevel.Error);
- this.Monitor.Newline();
-
- if (skippedDependencies.Any())
- {
- foreach (IModMetadata mod in skippedDependencies.OrderBy(p => p.DisplayName))
- LogSkippedMod(mod);
- this.Monitor.Newline();
- }
-
- foreach (IModMetadata mod in otherSkippedMods.OrderBy(p => p.DisplayName))
- LogSkippedMod(mod);
- this.Monitor.Newline();
}
// log warnings
@@ -553,9 +541,95 @@ namespace StardewModdingAPI.Framework.Logging
// not crossplatform
this.LogModWarningGroup(modsWithWarnings, ModWarning.UsesDynamic, LogLevel.Debug, "Not crossplatform",
- "These mods use the 'dynamic' keyword, and won't work on Linux/Mac."
+ "These mods use the 'dynamic' keyword, and won't work on Linux/macOS."
+ );
+ }
+ }
+
+ /// <summary>Group failed mods by the priority players should update them, where mods in earlier groups are more likely to fix multiple mods.</summary>
+ /// <param name="failedMods">The failed mods to group.</param>
+ private IEnumerable<IList<IModMetadata>> GroupFailedModsByPriority(IList<IModMetadata> failedMods)
+ {
+ var failedOthers = failedMods.ToList();
+ var skippedModIds = new HashSet<string>(from mod in failedMods where mod.HasID() select mod.Manifest.UniqueID, StringComparer.OrdinalIgnoreCase);
+
+ // group B: dependencies which failed
+ var failedOtherDependencies = new List<IModMetadata>();
+ {
+ // get failed dependency IDs
+ var skippedDependencyIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+ foreach (IModMetadata mod in failedMods)
+ {
+ foreach (string requiredId in skippedModIds.Intersect(mod.GetRequiredModIds()))
+ skippedDependencyIds.Add(requiredId);
+ }
+
+ // group matching mods
+ this.FilterThrough(
+ fromList: failedOthers,
+ toList: failedOtherDependencies,
+ match: mod => mod.HasID() && skippedDependencyIds.Contains(mod.Manifest.UniqueID)
);
}
+
+ // group A: failed root dependencies which other dependencies need
+ var failedRootDependencies = new List<IModMetadata>();
+ {
+ var skippedDependencyIds = new HashSet<string>(failedOtherDependencies.Select(p => p.Manifest.UniqueID));
+ this.FilterThrough(
+ fromList: failedOtherDependencies,
+ toList: failedRootDependencies,
+ match: mod =>
+ {
+ // has no failed dependency
+ foreach (string requiredId in mod.GetRequiredModIds())
+ {
+ if (skippedDependencyIds.Contains(requiredId))
+ return false;
+ }
+
+ // another dependency depends on this mod
+ bool isDependedOn = false;
+ foreach (IModMetadata other in failedOtherDependencies)
+ {
+ if (other.HasRequiredModId(mod.Manifest.UniqueID, includeOptional: false))
+ {
+ isDependedOn = true;
+ break;
+ }
+ }
+
+ return isDependedOn;
+ }
+ );
+ }
+
+ // return groups
+ return new[]
+ {
+ failedRootDependencies,
+ failedOtherDependencies,
+ failedOthers
+ };
+ }
+
+ /// <summary>Filter matching items from one list and add them to the other.</summary>
+ /// <typeparam name="TItem">The list item type.</typeparam>
+ /// <param name="fromList">The list to filter.</param>
+ /// <param name="toList">The list to which to add filtered items.</param>
+ /// <param name="match">Matches items to filter through.</param>
+ private void FilterThrough<TItem>(IList<TItem> fromList, IList<TItem> toList, Func<TItem, bool> match)
+ {
+ for (int i = 0; i < fromList.Count; i++)
+ {
+ TItem item = fromList[i];
+ if (match(item))
+ {
+ toList.Add(item);
+ fromList.RemoveAt(i);
+ i--;
+ }
+ }
}
/// <summary>Write a mod warning group to the console and log.</summary>
diff --git a/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs b/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
index a948213b..baffc50e 100644
--- a/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
+++ b/src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs
@@ -20,7 +20,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>The instruction is compatible, but affects the save serializer in a way that may make saves unloadable without the mod.</summary>
DetectedSaveSerializer,
- /// <summary>The instruction is compatible, but uses the <c>dynamic</c> keyword which won't work on Linux/Mac.</summary>
+ /// <summary>The instruction is compatible, but uses the <c>dynamic</c> keyword which won't work on Linux/macOS.</summary>
DetectedDynamic,
/// <summary>The instruction is compatible, but references <see cref="ISpecializedEvents.UnvalidatedUpdateTicking"/> or <see cref="ISpecializedEvents.UnvalidatedUpdateTicked"/> which may impact stability.</summary>
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index b4de3d6c..17e6d59a 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -19,6 +19,9 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>The non-error issues with the mod, including warnings suppressed by the data record.</summary>
private ModWarning ActualWarnings = ModWarning.None;
+ /// <summary>The mod IDs which are listed as a requirement by this mod. The value for each pair indicates whether the dependency is required (i.e. not an optional dependency).</summary>
+ private readonly Lazy<IDictionary<string, bool>> Dependencies;
+
/*********
** Accessors
@@ -100,6 +103,8 @@ namespace StardewModdingAPI.Framework.ModLoading
this.Manifest = manifest;
this.DataRecord = dataRecord;
this.IsIgnored = isIgnored;
+
+ this.Dependencies = new Lazy<IDictionary<string, bool>>(this.ExtractDependencies);
}
/// <inheritdoc />
@@ -199,23 +204,21 @@ namespace StardewModdingAPI.Framework.ModLoading
}
/// <inheritdoc />
- public IEnumerable<string> GetRequiredModIds(bool includeOptional = false)
+ public bool HasRequiredModId(string modId, bool includeOptional)
{
- HashSet<string> required = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+ return
+ this.Dependencies.Value.TryGetValue(modId, out bool isRequired)
+ && (includeOptional || isRequired);
+ }
- // yield dependencies
- if (this.Manifest?.Dependencies != null)
+ /// <inheritdoc />
+ public IEnumerable<string> GetRequiredModIds(bool includeOptional = false)
+ {
+ foreach (var pair in this.Dependencies.Value)
{
- foreach (var entry in this.Manifest?.Dependencies)
- {
- if ((entry.IsRequired || includeOptional) && required.Add(entry.UniqueID))
- yield return entry.UniqueID;
- }
+ if (includeOptional || pair.Value)
+ yield return pair.Key;
}
-
- // yield content pack parent
- if (this.Manifest?.ContentPackFor?.UniqueID != null && required.Add(this.Manifest.ContentPackFor.UniqueID))
- yield return this.Manifest.ContentPackFor.UniqueID;
}
/// <inheritdoc />
@@ -237,5 +240,29 @@ namespace StardewModdingAPI.Framework.ModLoading
string rootFolderName = Path.GetFileName(this.RootPath) ?? "";
return Path.Combine(rootFolderName, this.RelativeDirectoryPath);
}
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Extract mod IDs from the manifest that must be installed to load this mod.</summary>
+ /// <returns>Returns a dictionary of mod ID => is required (i.e. not an optional dependency).</returns>
+ public IDictionary<string, bool> ExtractDependencies()
+ {
+ var ids = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
+
+ // yield dependencies
+ if (this.Manifest?.Dependencies != null)
+ {
+ foreach (var entry in this.Manifest?.Dependencies)
+ ids[entry.UniqueID] = entry.IsRequired;
+ }
+
+ // yield content pack parent
+ if (this.Manifest?.ContentPackFor?.UniqueID != null)
+ ids[this.Manifest.ContentPackFor.UniqueID] = true;
+
+ return ids;
+ }
}
}
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs
index cf71af77..aefd1c20 100644
--- a/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/SpriteBatchFacade.cs
@@ -4,10 +4,10 @@ using Microsoft.Xna.Framework.Graphics;
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades
{
- /// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility between Linux/Mac or Windows.</summary>
+ /// <summary>Provides <see cref="SpriteBatch"/> method signatures that can be injected into mod code for compatibility between Linux/macOS or Windows.</summary>
/// <remarks>This is public to support SMAPI rewriting and should not be referenced directly by mods.</remarks>
[SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Used via assembly rewriting")]
- [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/Mac.")]
+ [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/macOS.")]
[SuppressMessage("ReSharper", "CS1591", Justification = "Documentation not needed for facade classes.")]
public class SpriteBatchFacade : SpriteBatch
{
diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs
index 4a80e34c..a71bafd9 100644
--- a/src/SMAPI/Framework/Models/SConfig.cs
+++ b/src/SMAPI/Framework/Models/SConfig.cs
@@ -52,6 +52,9 @@ namespace StardewModdingAPI.Framework.Models
/// <summary>SMAPI's GitHub project name, used to perform update checks.</summary>
public string GitHubProjectName { get; set; }
+ /// <summary>Stardew64Installer's GitHub project name, used to perform update checks.</summary>
+ public string Stardew64InstallerGitHubProjectName { get; set; }
+
/// <summary>The base URL for SMAPI's web API, used to perform update checks.</summary>
public string WebApiBaseUrl { get; set; }
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index ebb21555..5862b112 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -187,7 +187,7 @@ namespace StardewModdingAPI.Framework
#if SMAPI_FOR_WINDOWS
if (Constants.Platform != Platform.Windows)
{
- this.Monitor.Log("Oops! You're running Windows, but this version of SMAPI is for Linux or Mac. Please reinstall SMAPI to fix this.", LogLevel.Error);
+ this.Monitor.Log("Oops! You're running Windows, but this version of SMAPI is for Linux or macOS. Please reinstall SMAPI to fix this.", LogLevel.Error);
this.LogManager.PressAnyKeyToExit();
}
#else
@@ -263,10 +263,7 @@ namespace StardewModdingAPI.Framework
});
// set window titles
- this.SetWindowTitles(
- game: $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}",
- smapi: $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}"
- );
+ this.UpdateWindowTitles();
}
catch (Exception ex)
{
@@ -280,10 +277,7 @@ namespace StardewModdingAPI.Framework
this.LogManager.LogSettingsHeader(this.Settings);
// set window titles
- this.SetWindowTitles(
- game: $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion}",
- smapi: $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion}"
- );
+ this.UpdateWindowTitles();
// start game
this.Monitor.Log("Starting game...", LogLevel.Debug);
@@ -387,11 +381,7 @@ namespace StardewModdingAPI.Framework
}
// update window titles
- int modsLoaded = this.ModRegistry.GetAll().Count();
- this.SetWindowTitles(
- game: $"Stardew Valley {Constants.GameVersion} - running SMAPI {Constants.ApiVersion} with {modsLoaded} mods",
- smapi: $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GameVersion} with {modsLoaded} mods"
- );
+ this.UpdateWindowTitles();
}
/// <summary>Raised after the game finishes initializing.</summary>
@@ -419,7 +409,7 @@ namespace StardewModdingAPI.Framework
Game1.mapDisplayDevice = new SDisplayDevice(Game1.content, Game1.game1.GraphicsDevice);
// log GPU info
-#if SMAPI_FOR_WINDOWS
+#if SMAPI_FOR_WINDOWS && !SMAPI_FOR_WINDOWS_64BIT_HACK
this.Monitor.Log($"Running on GPU: {Game1.game1.GraphicsDevice?.Adapter?.Description ?? "<unknown>"}");
#endif
}
@@ -1238,13 +1228,23 @@ namespace StardewModdingAPI.Framework
return !issuesFound;
}
- /// <summary>Set the window titles for the game and console windows.</summary>
- /// <param name="game">The game window text.</param>
- /// <param name="smapi">The SMAPI window text.</param>
- private void SetWindowTitles(string game, string smapi)
+ /// <summary>Set the titles for the game and console windows.</summary>
+ private void UpdateWindowTitles()
{
- this.Game.Window.Title = game;
- this.LogManager.SetConsoleTitle(smapi);
+ string smapiVersion = $"{Constants.ApiVersion}{(EarlyConstants.IsWindows64BitHack ? " [64-bit]" : "")}";
+
+ string consoleTitle = $"SMAPI {smapiVersion} - running Stardew Valley {Constants.GameVersion}";
+ string gameTitle = $"Stardew Valley {Constants.GameVersion} - running SMAPI {smapiVersion}";
+
+ if (this.ModRegistry.AreAllModsLoaded)
+ {
+ int modsLoaded = this.ModRegistry.GetAll().Count();
+ consoleTitle += $" with {modsLoaded} mods";
+ gameTitle += $" with {modsLoaded} mods";
+ }
+
+ this.Game.Window.Title = gameTitle;
+ this.LogManager.SetConsoleTitle(consoleTitle);
}
/// <summary>Asynchronously check for a new version of SMAPI and any installed mods, and print alerts to the console if an update is available.</summary>
@@ -1259,7 +1259,7 @@ namespace StardewModdingAPI.Framework
// create client
string url = this.Settings.WebApiBaseUrl;
#if !SMAPI_FOR_WINDOWS
- url = url.Replace("https://", "http://"); // workaround for OpenSSL issues with the game's bundled Mono on Linux/Mac
+ url = url.Replace("https://", "http://"); // workaround for OpenSSL issues with the game's bundled Mono on Linux/macOS
#endif
WebApiClient client = new WebApiClient(url, Constants.ApiVersion);
this.Monitor.Log("Checking for updates...");
@@ -1302,6 +1302,41 @@ namespace StardewModdingAPI.Framework
this.LogManager.WriteUpdateMarker(updateFound.ToString(), updateUrl);
}
+ // check Stardew64Installer version
+ if (Constants.IsPatchedByStardew64Installer(out ISemanticVersion patchedByVersion))
+ {
+ ISemanticVersion updateFound = null;
+ string updateUrl = null;
+ try
+ {
+ // fetch update check
+ ModEntryModel response = client.GetModInfo(new[] { new ModSearchEntryModel("Steviegt6.Stardew64Installer", patchedByVersion, new[] { $"GitHub:{this.Settings.Stardew64InstallerGitHubProjectName}" }) }, apiVersion: Constants.ApiVersion, gameVersion: Constants.GameVersion, platform: Constants.Platform).Single().Value;
+ updateFound = response.SuggestedUpdate?.Version;
+ updateUrl = response.SuggestedUpdate?.Url ?? Constants.HomePageUrl;
+
+ // log message
+ if (updateFound != null)
+ this.Monitor.Log($"You can update Stardew64Installer to {updateFound}: {updateUrl}", LogLevel.Alert);
+ else
+ this.Monitor.Log(" Stardew64Installer okay.");
+
+ // show errors
+ if (response.Errors.Any())
+ {
+ this.Monitor.Log("Couldn't check for a new version of Stardew64Installer. This won't affect your game, but you may not be notified of new versions if this keeps happening.", LogLevel.Warn);
+ this.Monitor.Log($"Error: {string.Join("\n", response.Errors)}");
+ }
+ }
+ catch (Exception ex)
+ {
+ this.Monitor.Log("Couldn't check for a new version of Stardew64Installer. This won't affect your game, but you won't be notified of new versions if this keeps happening.", LogLevel.Warn);
+ this.Monitor.Log(ex is WebException && ex.InnerException == null
+ ? $"Error: {ex.Message}"
+ : $"Error: {ex.GetLogSummary()}"
+ );
+ }
+ }
+
// check mod versions
if (mods.Any())
{
diff --git a/src/SMAPI/Framework/Serialization/ColorConverter.cs b/src/SMAPI/Framework/Serialization/ColorConverter.cs
index 7315f1a5..3b3720b5 100644
--- a/src/SMAPI/Framework/Serialization/ColorConverter.cs
+++ b/src/SMAPI/Framework/Serialization/ColorConverter.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Serialization
{
/// <summary>Handles deserialization of <see cref="Color"/> for crossplatform compatibility.</summary>
/// <remarks>
- /// - Linux/Mac format: { "B": 76, "G": 51, "R": 25, "A": 102 }
+ /// - Linux/macOS format: { "B": 76, "G": 51, "R": 25, "A": 102 }
/// - Windows format: "26, 51, 76, 102"
/// </remarks>
internal class ColorConverter : SimpleReadOnlyConverter<Color>
diff --git a/src/SMAPI/Framework/Serialization/PointConverter.cs b/src/SMAPI/Framework/Serialization/PointConverter.cs
index 6cf795dc..21d1f845 100644
--- a/src/SMAPI/Framework/Serialization/PointConverter.cs
+++ b/src/SMAPI/Framework/Serialization/PointConverter.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Serialization
{
/// <summary>Handles deserialization of <see cref="Point"/> for crossplatform compatibility.</summary>
/// <remarks>
- /// - Linux/Mac format: { "X": 1, "Y": 2 }
+ /// - Linux/macOS format: { "X": 1, "Y": 2 }
/// - Windows format: "1, 2"
/// </remarks>
internal class PointConverter : SimpleReadOnlyConverter<Point>
diff --git a/src/SMAPI/Framework/Serialization/RectangleConverter.cs b/src/SMAPI/Framework/Serialization/RectangleConverter.cs
index 8f7318b3..31f3ad77 100644
--- a/src/SMAPI/Framework/Serialization/RectangleConverter.cs
+++ b/src/SMAPI/Framework/Serialization/RectangleConverter.cs
@@ -9,7 +9,7 @@ namespace StardewModdingAPI.Framework.Serialization
{
/// <summary>Handles deserialization of <see cref="Rectangle"/> for crossplatform compatibility.</summary>
/// <remarks>
- /// - Linux/Mac format: { "X": 1, "Y": 2, "Width": 3, "Height": 4 }
+ /// - Linux/macOS format: { "X": 1, "Y": 2, "Width": 3, "Height": 4 }
/// - Windows format: "{X:1 Y:2 Width:3 Height:4}"
/// </remarks>
internal class RectangleConverter : SimpleReadOnlyConverter<Rectangle>
diff --git a/src/SMAPI/Framework/Serialization/Vector2Converter.cs b/src/SMAPI/Framework/Serialization/Vector2Converter.cs
index 3e2ab776..589febf8 100644
--- a/src/SMAPI/Framework/Serialization/Vector2Converter.cs
+++ b/src/SMAPI/Framework/Serialization/Vector2Converter.cs
@@ -8,7 +8,7 @@ namespace StardewModdingAPI.Framework.Serialization
{
/// <summary>Handles deserialization of <see cref="Vector2"/> for crossplatform compatibility.</summary>
/// <remarks>
- /// - Linux/Mac format: { "X": 1, "Y": 2 }
+ /// - Linux/macOS format: { "X": 1, "Y": 2 }
/// - Windows format: "1, 2"
/// </remarks>
internal class Vector2Converter : SimpleReadOnlyConverter<Vector2>
diff --git a/src/SMAPI/GamePlatform.cs b/src/SMAPI/GamePlatform.cs
index b64595e4..cce5ed8d 100644
--- a/src/SMAPI/GamePlatform.cs
+++ b/src/SMAPI/GamePlatform.cs
@@ -11,7 +11,7 @@ namespace StardewModdingAPI
/// <summary>The Linux version of the game.</summary>
Linux = Platform.Linux,
- /// <summary>The Mac version of the game.</summary>
+ /// <summary>The macOS version of the game.</summary>
Mac = Platform.Mac,
/// <summary>The Windows version of the game.</summary>
diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs
index 52da3946..623c65d5 100644
--- a/src/SMAPI/Metadata/CoreAssetPropagator.cs
+++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using Microsoft.Xna.Framework.Graphics;
using Netcode;
+using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.ContentManagers;
using StardewModdingAPI.Framework.Reflection;
using StardewModdingAPI.Toolkit.Utilities;
@@ -36,15 +37,18 @@ namespace StardewModdingAPI.Metadata
/// <summary>An internal content manager used only for asset propagation. See remarks on <see cref="GameContentManagerForAssetPropagation"/>.</summary>
private readonly GameContentManagerForAssetPropagation DisposableContentManager;
+ /// <summary>Writes messages to the console.</summary>
+ private readonly IMonitor Monitor;
+
+ /// <summary>Simplifies access to private game code.</summary>
+ private readonly Reflector Reflection;
+
/// <summary>Whether to enable more aggressive memory optimizations.</summary>
private readonly bool AggressiveMemoryOptimizations;
/// <summary>Normalizes an asset key to match the cache key and assert that it's valid.</summary>
private readonly Func<string, string> AssertAndNormalizeAssetName;
- /// <summary>Simplifies access to private game code.</summary>
- private readonly Reflector Reflection;
-
/// <summary>Optimized bucket categories for batch reloading assets.</summary>
private enum AssetBucket
{
@@ -65,12 +69,14 @@ namespace StardewModdingAPI.Metadata
/// <summary>Initialize the core asset data.</summary>
/// <param name="mainContent">The main content manager through which to reload assets.</param>
/// <param name="disposableContent">An internal content manager used only for asset propagation.</param>
+ /// <param name="monitor">Writes messages to the console.</param>
/// <param name="reflection">Simplifies access to private code.</param>
/// <param name="aggressiveMemoryOptimizations">Whether to enable more aggressive memory optimizations.</param>
- public CoreAssetPropagator(LocalizedContentManager mainContent, GameContentManagerForAssetPropagation disposableContent, Reflector reflection, bool aggressiveMemoryOptimizations)
+ public CoreAssetPropagator(LocalizedContentManager mainContent, GameContentManagerForAssetPropagation disposableContent, IMonitor monitor, Reflector reflection, bool aggressiveMemoryOptimizations)
{
this.MainContentManager = mainContent;
this.DisposableContentManager = disposableContent;
+ this.Monitor = monitor;
this.Reflection = reflection;
this.AggressiveMemoryOptimizations = aggressiveMemoryOptimizations;
@@ -116,7 +122,17 @@ namespace StardewModdingAPI.Metadata
default:
foreach (var entry in bucket)
{
- bool changed = this.PropagateOther(entry.Key, entry.Value, ignoreWorld, out bool curChangedMapWarps);
+ bool changed = false;
+ bool curChangedMapWarps = false;
+ try
+ {
+ changed = this.PropagateOther(entry.Key, entry.Value, ignoreWorld, out curChangedMapWarps);
+ }
+ catch (Exception ex)
+ {
+ this.Monitor.Log($"An error occurred while propagating asset changes. Error details:\n{ex.GetLogSummary()}", LogLevel.Error);
+ }
+
propagatedAssets[entry.Key] = changed;
updatedNpcWarps = updatedNpcWarps || curChangedMapWarps;
}
@@ -258,6 +274,7 @@ namespace StardewModdingAPI.Metadata
return true;
case "data\\bundles": // NetWorldState constructor
+ if (Context.IsMainPlayer && Game1.netWorldState != null)
{
var bundles = this.Reflection.GetField<NetBundles>(Game1.netWorldState.Value, "bundles").GetValue();
var rewards = this.Reflection.GetField<NetIntDictionary<bool, NetBool>>(Game1.netWorldState.Value, "bundleRewards").GetValue();
@@ -286,6 +303,10 @@ namespace StardewModdingAPI.Metadata
Game1.clothingInformation = content.Load<Dictionary<int, string>>(key);
return true;
+ case "data\\concessions": // MovieTheater.GetConcessions
+ MovieTheater.ClearCachedLocalizedData();
+ return true;
+
case "data\\concessiontastes": // MovieTheater.GetConcessionTasteForCharacter
this.Reflection
.GetField<List<ConcessionTaste>>(typeof(MovieTheater), "_concessionTastes")
@@ -306,16 +327,9 @@ namespace StardewModdingAPI.Metadata
case "data\\hairdata": // Farmer.GetHairStyleMetadataFile
return this.ReloadHairData();
- case "data\\moviesreactions": // MovieTheater.GetMovieReactions
- this.Reflection
- .GetField<List<MovieCharacterReaction>>(typeof(MovieTheater), "_genericReactions")
- .SetValue(content.Load<List<MovieCharacterReaction>>(key));
- return true;
-
case "data\\movies": // MovieTheater.GetMovieData
- this.Reflection
- .GetField<Dictionary<string, MovieData>>(typeof(MovieTheater), "_movieData")
- .SetValue(content.Load<Dictionary<string, MovieData>>(key));
+ case "data\\moviesreactions": // MovieTheater.GetMovieReactions
+ MovieTheater.ClearCachedLocalizedData();
return true;
case "data\\npcdispositions": // NPC constructor
diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs
index 986d2780..e830f799 100644
--- a/src/SMAPI/Program.cs
+++ b/src/SMAPI/Program.cs
@@ -24,6 +24,8 @@ namespace StardewModdingAPI
/// <param name="args">The command-line arguments.</param>
public static void Main(string[] args)
{
+ Console.Title = $"SMAPI {EarlyConstants.RawApiVersion}{(EarlyConstants.IsWindows64BitHack ? " 64-bit" : "")} - {Console.Title}";
+
try
{
AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
@@ -159,7 +161,7 @@ namespace StardewModdingAPI
Console.ResetColor();
Console.WriteLine();
}
-
+
Program.PressAnyKeyToExit(showMessage: true);
}
diff --git a/src/SMAPI/SMAPI.config.json b/src/SMAPI/SMAPI.config.json
index 034eceed..7b5625d6 100644
--- a/src/SMAPI/SMAPI.config.json
+++ b/src/SMAPI/SMAPI.config.json
@@ -70,8 +70,13 @@ copy all the settings, or you may cause bugs due to overridden changes in future
"GitHubProjectName": "Pathoschild/SMAPI",
/**
+ * Stardew64Installer's GitHub project name, used to perform update checks.
+ */
+ "Stardew64InstallerGitHubProjectName": "Steviegt6/Stardew64Installer",
+
+ /**
* The base URL for SMAPI's web API, used to perform update checks.
- * Note: the protocol will be changed to http:// on Linux/Mac due to OpenSSL issues with the
+ * Note: the protocol will be changed to http:// on Linux/macOS due to OpenSSL issues with the
* game's bundled Mono.
*/
"WebApiBaseUrl": "https://smapi.io/api/",
@@ -85,7 +90,7 @@ copy all the settings, or you may cause bugs due to overridden changes in future
* The colors to use for text written to the SMAPI console.
*
* The possible values for 'UseScheme' are:
- * - AutoDetect: SMAPI will assume a light background on Mac, and detect the background color
+ * - AutoDetect: SMAPI will assume a light background on macOS, and detect the background color
* automatically on Linux or Windows.
* - LightBackground: use darker text colors that look better on a white or light background.
* - DarkBackground: use lighter text colors that look better on a black or dark background.
diff --git a/src/SMAPI/SMAPI.csproj b/src/SMAPI/SMAPI.csproj
index ceef33df..413d9f33 100644
--- a/src/SMAPI/SMAPI.csproj
+++ b/src/SMAPI/SMAPI.csproj
@@ -14,6 +14,10 @@
<Import Project="..\..\build\common.targets" />
+ <PropertyGroup Condition="$(DefineConstants.Contains(SMAPI_FOR_WINDOWS_64BIT_HACK))">
+ <PlatformTarget>x64</PlatformTarget>
+ </PropertyGroup>
+
<ItemGroup>
<PackageReference Include="LargeAddressAware" Version="1.0.5" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
@@ -34,7 +38,7 @@
<!-- Windows only -->
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
- <Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" />
+ <Reference Include="Netcode" HintPath="$(GamePath)\Netcode.dll" Private="False" Condition="!$(DefineConstants.Contains(SMAPI_FOR_WINDOWS_64BIT_HACK))" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>