diff --git a/build/common.targets b/build/common.targets
index 8a83ca45..01b1d373 100644
--- a/build/common.targets
+++ b/build/common.targets
@@ -4,7 +4,7 @@
- 3.3.2
+ 3.4.0
SMAPI
$(AssemblySearchPaths);{GAC}
diff --git a/docs/README.md b/docs/README.md
index f45e950c..546ee6b3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -67,7 +67,7 @@ Chinese | ✓ [fully translated](../src/SMAPI/i18n/zh.json)
French | ✓ [fully translated](../src/SMAPI/i18n/fr.json)
German | ✓ [fully translated](../src/SMAPI/i18n/de.json)
Hungarian | ✓ [fully translated](../src/SMAPI/i18n/hu.json)
-Italian | ❑ not translated
+Italian | ✓ [fully translated](../src/SMAPI/i18n/it.json)
Japanese | ✓ [fully translated](../src/SMAPI/i18n/ja.json)
Korean | ❑ not translated
Portuguese | ✓ [fully translated](../src/SMAPI/i18n/pt.json)
diff --git a/docs/release-notes.md b/docs/release-notes.md
index 4a136df5..5a5e24d4 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -1,14 +1,20 @@
← [README](README.md)
# Release notes
-## Upcoming release
+## 3.4
+Released 22 March 2020 for Stardew Valley 1.4.1 or later.
+
* For players:
+ * Fixed semi-transparency issues on Linux/Mac in recent versions of Mono (e.g. pink shadows).
+ * Fixed `player_add` command error if you have broken XNB mods.
+ * Removed invalid-location check now handled by the game.
* Updated translations. Thanks to Annosz (added Hungarian)!
* For modders:
* Added support for flipped and rotated map tiles (in collaboration with Platonymous).
* Added support for `.tmx` maps using zlib compression (thanks to Platonymous!).
- * Mods are no longer prevented from suppressing key presses in the chatbox. Use this power wisely.
+ * Added `this.Monitor.LogOnce` method.
+ * Mods are no longer prevented from suppressing key presses in the chatbox.
* For the web UI:
* Added option to upload files using a file picker.
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
index 08dd8eed..6a17213c 100644
--- a/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
+++ b/src/SMAPI.Mods.ConsoleCommands/Framework/ItemRepository.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
using StardewModdingAPI.Mods.ConsoleCommands.Framework.ItemData;
using StardewValley;
using StardewValley.Menus;
@@ -59,13 +60,13 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
yield return this.TryCreate(ItemType.Flooring, id, () => new Wallpaper(id, isFloor: true) { Category = SObject.furnitureCategory });
// equipment
- foreach (int id in Game1.content.Load>("Data\\Boots").Keys)
+ foreach (int id in this.TryLoad("Data\\Boots").Keys)
yield return this.TryCreate(ItemType.Boots, id, () => new Boots(id));
- foreach (int id in Game1.content.Load>("Data\\hats").Keys)
+ foreach (int id in this.TryLoad("Data\\hats").Keys)
yield return this.TryCreate(ItemType.Hat, id, () => new Hat(id));
// weapons
- foreach (int id in Game1.content.Load>("Data\\weapons").Keys)
+ foreach (int id in this.TryLoad("Data\\weapons").Keys)
{
yield return this.TryCreate(ItemType.Weapon, id, () => (id >= 32 && id <= 34)
? (Item)new Slingshot(id)
@@ -74,7 +75,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
}
// furniture
- foreach (int id in Game1.content.Load>("Data\\Furniture").Keys)
+ foreach (int id in this.TryLoad("Data\\Furniture").Keys)
{
if (id == 1466 || id == 1468)
yield return this.TryCreate(ItemType.Furniture, id, () => new TV(id, Vector2.Zero));
@@ -94,7 +95,7 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
// secret notes
if (id == 79)
{
- foreach (int secretNoteId in Game1.content.Load>("Data\\SecretNotes").Keys)
+ foreach (int secretNoteId in this.TryLoad("Data\\SecretNotes").Keys)
{
yield return this.TryCreate(ItemType.Object, this.CustomIDOffset + secretNoteId, () =>
{
@@ -233,6 +234,23 @@ namespace StardewModdingAPI.Mods.ConsoleCommands.Framework
/*********
** Private methods
*********/
+ /// Try to load a data file, and return empty data if it's invalid.
+ /// The asset key type.
+ /// The asset value type.
+ /// The data asset name.
+ private Dictionary TryLoad(string assetName)
+ {
+ try
+ {
+ return Game1.content.Load>(assetName);
+ }
+ catch (ContentLoadException)
+ {
+ // generally due to a player incorrectly replacing a data file with an XNB mod
+ return new Dictionary();
+ }
+ }
+
/// Create a searchable item if valid.
/// The item type.
/// The unique ID (if different from the item's parent sheet index).
diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json
index 0e6805dc..dbed84eb 100644
--- a/src/SMAPI.Mods.ConsoleCommands/manifest.json
+++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Console Commands",
"Author": "SMAPI",
- "Version": "3.3.2",
+ "Version": "3.4.0",
"Description": "Adds SMAPI console commands that let you manipulate the game.",
"UniqueID": "SMAPI.ConsoleCommands",
"EntryDll": "ConsoleCommands.dll",
- "MinimumApiVersion": "3.3.2"
+ "MinimumApiVersion": "3.4.0"
}
diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json
index 5165d2b2..dc8bc8d4 100644
--- a/src/SMAPI.Mods.SaveBackup/manifest.json
+++ b/src/SMAPI.Mods.SaveBackup/manifest.json
@@ -1,9 +1,9 @@
{
"Name": "Save Backup",
"Author": "SMAPI",
- "Version": "3.3.2",
+ "Version": "3.4.0",
"Description": "Automatically backs up all your saves once per day into its folder.",
"UniqueID": "SMAPI.SaveBackup",
"EntryDll": "SaveBackup.dll",
- "MinimumApiVersion": "3.3.2"
+ "MinimumApiVersion": "3.4.0"
}
diff --git a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
index 4554e801..e4f0c1ff 100644
--- a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
+++ b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj
index 05200c94..0a978b30 100644
--- a/src/SMAPI.Web/SMAPI.Web.csproj
+++ b/src/SMAPI.Web/SMAPI.Web.csproj
@@ -12,14 +12,14 @@
-
+
-
-
+
+
-
+
diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index 4c53cfb7..98040057 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -20,7 +20,7 @@ namespace StardewModdingAPI
** Public
****/
/// SMAPI's current semantic version.
- public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.3.2.4", allowNonStandard: true);
+ public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.4.0");
/// The minimum supported version of Stardew Valley.
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.4.5");
@@ -53,7 +53,7 @@ namespace StardewModdingAPI
** Internal
****/
/// The URL of the SMAPI home page.
- internal const string HomePageUrl = "https://github.com/MartyrPher/SMAPI-Android-Installer/releases/latest";
+ internal const string HomePageUrl = "https://smapi.io";
/// The default performance counter name for unknown event handlers.
internal const string GamePerformanceCounterName = "";
diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index c6f28f23..c84f481c 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -247,13 +247,6 @@ namespace StardewModdingAPI.Framework.ContentManagers
/// Based on code by David Gouveia.
private Texture2D PremultiplyTransparency(Texture2D texture)
{
- // Textures loaded by Texture2D.FromStream are already premultiplied on Linux/Mac, even
- // though the XNA documentation explicitly says otherwise. That's a glitch in MonoGame
- // fixed in newer versions, but the game uses a bundled version that will always be
- // affected. See https://github.com/MonoGame/MonoGame/issues/4820 for more info.
- if (Constants.TargetPlatform != GamePlatform.Windows && Constants.TargetPlatform != GamePlatform.Android)
- return texture;
-
// premultiply pixels
Color[] data = new Color[texture.Width * texture.Height];
texture.GetData(data);
diff --git a/src/SMAPI/Framework/Monitor.cs b/src/SMAPI/Framework/Monitor.cs
index a9708960..e19f02c2 100644
--- a/src/SMAPI/Framework/Monitor.cs
+++ b/src/SMAPI/Framework/Monitor.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using StardewModdingAPI.Framework.Logging;
using StardewModdingAPI.Internal.ConsoleWriting;
@@ -26,6 +27,9 @@ namespace StardewModdingAPI.Framework
/// The maximum length of the values.
private static readonly int MaxLevelLength = (from level in Enum.GetValues(typeof(LogLevel)).Cast() select level.ToString().Length).Max();
+ /// A cache of messages that should only be logged once.
+ private readonly HashSet LogOnceCache = new HashSet();
+
/*********
** Accessors
@@ -74,6 +78,15 @@ namespace StardewModdingAPI.Framework
this.LogImpl(this.Source, message, (ConsoleLogLevel)level);
}
+ /// Log a message for the player or developer, but only if it hasn't already been logged since the last game launch.
+ /// The message to log.
+ /// The log severity level.
+ public void LogOnce(string message, LogLevel level = LogLevel.Trace)
+ {
+ if (this.LogOnceCache.Add($"{message}|{level}"))
+ this.LogImpl(this.Source, message, (ConsoleLogLevel)level);
+ }
+
/// Log a message that only appears when is enabled.
/// The message to log.
public void VerboseLog(string message)
diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs
index c424bdf6..221d251f 100644
--- a/src/SMAPI/Framework/SGame.cs
+++ b/src/SMAPI/Framework/SGame.cs
@@ -209,7 +209,7 @@ namespace StardewModdingAPI.Framework
protected override void LoadContent()
{
base.LoadContent();
- //Game1.mapDisplayDevice = new SDisplayDevice(Game1.content, this.GraphicsDevice);
+ Game1.mapDisplayDevice = new SDisplayDevice(Game1.content, this.GraphicsDevice);
}
/// Initialize just before the game's first update tick.
@@ -307,8 +307,8 @@ namespace StardewModdingAPI.Framework
{
this.Multiplayer.CleanupOnMultiplayerExit();
- //if (!(Game1.mapDisplayDevice is SDisplayDevice))
- // Game1.mapDisplayDevice = new SDisplayDevice(Game1.content, this.GraphicsDevice);
+ if (!(Game1.mapDisplayDevice is SDisplayDevice))
+ Game1.mapDisplayDevice = new SDisplayDevice(Game1.content, this.GraphicsDevice);
}
/// Constructor a content manager to read XNB files.
diff --git a/src/SMAPI/IMonitor.cs b/src/SMAPI/IMonitor.cs
index f2d110b8..c400a211 100644
--- a/src/SMAPI/IMonitor.cs
+++ b/src/SMAPI/IMonitor.cs
@@ -18,6 +18,11 @@ namespace StardewModdingAPI
/// The log severity level.
void Log(string message, LogLevel level = LogLevel.Trace);
+ /// Log a message for the player or developer, but only if it hasn't already been logged since the last game launch.
+ /// The message to log.
+ /// The log severity level.
+ void LogOnce(string message, LogLevel level = LogLevel.Trace);
+
/// Log a message that only appears when is enabled.
/// The message to log.
void VerboseLog(string message);
diff --git a/src/SMAPI/Patches/LoadErrorPatch.cs b/src/SMAPI/Patches/LoadErrorPatch.cs
index c16ca7cc..77415ff2 100644
--- a/src/SMAPI/Patches/LoadErrorPatch.cs
+++ b/src/SMAPI/Patches/LoadErrorPatch.cs
@@ -67,8 +67,7 @@ namespace StardewModdingAPI.Patches
private static bool Before_SaveGame_LoadDataToLocations(List gamelocations)
{
bool removedAny =
- LoadErrorPatch.RemoveInvalidLocations(gamelocations)
- | LoadErrorPatch.RemoveBrokenBuildings(gamelocations)
+ LoadErrorPatch.RemoveBrokenBuildings(gamelocations)
| LoadErrorPatch.RemoveInvalidNpcs(gamelocations);
if (removedAny)
@@ -77,28 +76,6 @@ namespace StardewModdingAPI.Patches
return true;
}
- /// Remove locations which don't exist in-game.
- /// The current game locations.
- private static bool RemoveInvalidLocations(List locations)
- {
- bool removedAny = false;
-
- foreach (GameLocation location in locations.ToArray())
- {
- if (location is Cellar)
- continue; // missing cellars will be added by the game code
-
- if (Game1.getLocationFromName(location.name) == null)
- {
- LoadErrorPatch.Monitor.Log($"Removed invalid location '{location.Name}' to avoid a crash when loading save '{Constants.SaveFolderName}'. (Did you remove a custom location mod?)", LogLevel.Warn);
- locations.Remove(location);
- removedAny = true;
- }
- }
-
- return removedAny;
- }
-
/// Remove buildings which don't exist in the game data.
/// The current game locations.
private static bool RemoveBrokenBuildings(IEnumerable locations)
diff --git a/src/SMAPI/SMAPI.csproj b/src/SMAPI/SMAPI.csproj
index 15c21a51..aada4903 100644
--- a/src/SMAPI/SMAPI.csproj
+++ b/src/SMAPI/SMAPI.csproj
@@ -24,7 +24,7 @@
portable
false
bin\Debug\
- TRACE;DEBUG;ANDROID_TARGET_SAMSUNG
+ TRACE;DEBUG
prompt
4
latest