From 0a2b15d3c36a0a5db4d8fb6e48b592507dc74c14 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 2 Feb 2020 14:20:41 -0500 Subject: [PATCH 1/7] add support for self-broadcasts, optimize network messages --- docs/release-notes.md | 5 ++ .../Framework/Networking/ModMessageModel.cs | 2 +- src/SMAPI/Framework/SMultiplayer.cs | 88 +++++++++++++------ 3 files changed, 65 insertions(+), 30 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 926f4058..fe21cdb7 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,6 +1,11 @@ ← [README](README.md) # Release notes +## Upcoming release +* For modders: + * Added support for self-broadcasts through the multiplayer API. (Mods can now send messages to the current machine. That enables simple integrations between mods without needing an API, and lets mods notify a host mod without needing different code depending on whether the current player is the host or a farmhand.) + * Eliminated unneeded network messages when broadcasting to a peer who can't handle the message (e.g. because they don't have SMAPI or don't have the target mod). + ## 3.2 Released 01 February 2020 for Stardew Valley 1.4.1 or later. diff --git a/src/SMAPI/Framework/Networking/ModMessageModel.cs b/src/SMAPI/Framework/Networking/ModMessageModel.cs index 7ee39863..4f694f9c 100644 --- a/src/SMAPI/Framework/Networking/ModMessageModel.cs +++ b/src/SMAPI/Framework/Networking/ModMessageModel.cs @@ -21,7 +21,7 @@ namespace StardewModdingAPI.Framework.Networking /**** ** Destination ****/ - /// The players who should receive the message, or null for all players. + /// The players who should receive the message. public long[] ToPlayerIDs { get; set; } /// The mods which should receive the message, or null for all mods. diff --git a/src/SMAPI/Framework/SMultiplayer.cs b/src/SMAPI/Framework/SMultiplayer.cs index e04205c8..821c343f 100644 --- a/src/SMAPI/Framework/SMultiplayer.cs +++ b/src/SMAPI/Framework/SMultiplayer.cs @@ -338,30 +338,49 @@ namespace StardewModdingAPI.Framework /// The values for the players who should receive the message, or null for all players. If you don't need to broadcast to all players, specifying player IDs is recommended to reduce latency. public void BroadcastModMessage(TMessage message, string messageType, string fromModID, string[] toModIDs, long[] toPlayerIDs) { - // validate + // validate input if (message == null) throw new ArgumentNullException(nameof(message)); if (string.IsNullOrWhiteSpace(messageType)) throw new ArgumentNullException(nameof(messageType)); if (string.IsNullOrWhiteSpace(fromModID)) throw new ArgumentNullException(nameof(fromModID)); - if (!this.Peers.Any()) + + // get target players + long curPlayerId = Game1.player.UniqueMultiplayerID; + bool sendToSelf = false; + List sendToPeers = new List(); + if (toPlayerIDs == null) { - this.Monitor.VerboseLog($"Ignored '{messageType}' broadcast from mod {fromModID}: not connected to any players."); - return; + sendToSelf = true; + sendToPeers.AddRange(this.Peers.Values); + } + else + { + foreach (long id in toPlayerIDs.Distinct()) + { + if (id == curPlayerId) + sendToSelf = true; + else if (this.Peers.TryGetValue(id, out MultiplayerPeer peer) && peer.HasSmapi) + sendToPeers.Add(peer); + } } - // filter player IDs - HashSet playerIDs = null; - if (toPlayerIDs != null && toPlayerIDs.Any()) + // filter by mod ID + if (toModIDs != null) { - playerIDs = new HashSet(toPlayerIDs); - playerIDs.RemoveWhere(id => !this.Peers.ContainsKey(id)); - if (!playerIDs.Any()) - { - this.Monitor.VerboseLog($"Ignored '{messageType}' broadcast from mod {fromModID}: none of the specified player IDs are connected."); - return; - } + HashSet sendToMods = new HashSet(toModIDs, StringComparer.InvariantCultureIgnoreCase); + if (sendToSelf && toModIDs.All(id => this.ModRegistry.Get(id) == null)) + sendToSelf = false; + + sendToPeers.RemoveAll(peer => peer.Mods.All(mod => !sendToMods.Contains(mod.ID))); + } + + // validate recipients + if (!sendToSelf && !sendToPeers.Any()) + { + this.Monitor.VerboseLog($"Ignored '{messageType}' broadcast from mod {fromModID}: none of the specified player IDs can receive this message."); + return; } // get data to send @@ -369,33 +388,44 @@ namespace StardewModdingAPI.Framework fromPlayerID: Game1.player.UniqueMultiplayerID, fromModID: fromModID, toModIDs: toModIDs, - toPlayerIDs: playerIDs?.ToArray(), + toPlayerIDs: sendToPeers.Select(p => p.PlayerID).ToArray(), type: messageType, data: JToken.FromObject(message) ); string data = JsonConvert.SerializeObject(model, Formatting.None); - // log message - if (this.LogNetworkTraffic) - this.Monitor.Log($"Broadcasting '{messageType}' message: {data}.", LogLevel.Trace); - - // send message - if (Context.IsMainPlayer) + // send self-message + if (sendToSelf) { - foreach (MultiplayerPeer peer in this.Peers.Values) + if (this.LogNetworkTraffic) + this.Monitor.Log($"Broadcasting '{messageType}' message to self: {data}.", LogLevel.Trace); + + this.OnModMessageReceived(model); + } + + // send message to peers + if (sendToPeers.Any()) + { + if (Context.IsMainPlayer) { - if (playerIDs == null || playerIDs.Contains(peer.PlayerID)) + foreach (MultiplayerPeer peer in sendToPeers) { - model.ToPlayerIDs = new[] { peer.PlayerID }; + if (this.LogNetworkTraffic) + this.Monitor.Log($"Broadcasting '{messageType}' message to farmhand {peer.PlayerID}: {data}.", LogLevel.Trace); + peer.SendMessage(new OutgoingMessage((byte)MessageType.ModMessage, peer.PlayerID, data)); } } - } - else if (this.HostPeer != null && this.HostPeer.HasSmapi) - this.HostPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModMessage, this.HostPeer.PlayerID, data)); - else - this.Monitor.VerboseLog(" Can't send message because no valid connections were found."); + else if (this.HostPeer?.HasSmapi == true) + { + if (this.LogNetworkTraffic) + this.Monitor.Log($"Broadcasting '{messageType}' message to host {this.HostPeer.PlayerID}: {data}.", LogLevel.Trace); + this.HostPeer.SendMessage(new OutgoingMessage((byte)MessageType.ModMessage, this.HostPeer.PlayerID, data)); + } + else + this.Monitor.VerboseLog(" Can't send message because no valid connections were found."); + } } From 4991b4d6afea97dc6c9aa5174d438cac72e7d116 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 2 Feb 2020 15:01:03 -0500 Subject: [PATCH 2/7] prefix OS name in log on Android --- docs/release-notes.md | 3 +++ src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index fe21cdb7..56d4e7c3 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,9 @@ * Added support for self-broadcasts through the multiplayer API. (Mods can now send messages to the current machine. That enables simple integrations between mods without needing an API, and lets mods notify a host mod without needing different code depending on whether the current player is the host or a farmhand.) * Eliminated unneeded network messages when broadcasting to a peer who can't handle the message (e.g. because they don't have SMAPI or don't have the target mod). +* For SMAPI/tool developers: + * The SMAPI log now prefixes the OS name with `Android` on Android. + ## 3.2 Released 01 February 2020 for Stardew Valley 1.4.1 or later. diff --git a/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs b/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs index 2a01fe4b..c45448f3 100644 --- a/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs +++ b/src/SMAPI.Toolkit/Utilities/EnvironmentUtility.cs @@ -53,7 +53,19 @@ namespace StardewModdingAPI.Toolkit.Utilities } catch { } #endif - return (platform == Platform.Mac ? "MacOS " : "") + Environment.OSVersion; + + string name = Environment.OSVersion.ToString(); + switch (platform) + { + case Platform.Android: + name = $"Android {name}"; + break; + + case Platform.Mac: + name = $"MacOS {name}"; + break; + } + return name; } /// Get the name of the Stardew Valley executable. From 65180f86d81736327a5c8179ebf76a3cb4aff32c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 3 Feb 2020 19:12:39 -0500 Subject: [PATCH 3/7] update CP schema for .tmx support --- docs/release-notes.md | 3 +++ src/SMAPI.Web/wwwroot/schemas/content-patcher.json | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 56d4e7c3..a365b153 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -6,6 +6,9 @@ * Added support for self-broadcasts through the multiplayer API. (Mods can now send messages to the current machine. That enables simple integrations between mods without needing an API, and lets mods notify a host mod without needing different code depending on whether the current player is the host or a farmhand.) * Eliminated unneeded network messages when broadcasting to a peer who can't handle the message (e.g. because they don't have SMAPI or don't have the target mod). +* For the web UI: + * Updated the JSON validator and Content Patcher schema for `.tmx` support. + * For SMAPI/tool developers: * The SMAPI log now prefixes the OS name with `Android` on Android. diff --git a/src/SMAPI.Web/wwwroot/schemas/content-patcher.json b/src/SMAPI.Web/wwwroot/schemas/content-patcher.json index 7e00c28e..e6cd4e65 100644 --- a/src/SMAPI.Web/wwwroot/schemas/content-patcher.json +++ b/src/SMAPI.Web/wwwroot/schemas/content-patcher.json @@ -142,7 +142,7 @@ }, "FromFile": { "title": "Source file", - "description": "The relative file path in your content pack folder to load instead (like 'assets/dinosaur.png'). This can be a .json (data), .png (image), .tbin (map), or .xnb file. This field supports tokens and capitalization doesn't matter.", + "description": "The relative file path in your content pack folder to load instead (like 'assets/dinosaur.png'). This can be a .json (data), .png (image), .tbin or .tmx (map), or .xnb file. This field supports tokens and capitalization doesn't matter.", "type": "string", "allOf": [ { @@ -151,12 +151,12 @@ } }, { - "pattern": "\\.(json|png|tbin|xnb) *$" + "pattern": "\\.(json|png|tbin|tmx|xnb) *$" } ], "@errorMessages": { "allOf:indexes: 0": "Invalid value; must not contain directory climbing (like '../').", - "allOf:indexes: 1": "Invalid value; must be a file path ending with .json, .png, .tbin, or .xnb." + "allOf:indexes: 1": "Invalid value; must be a file path ending with .json, .png, .tbin, .tmx, or .xnb." } }, "FromArea": { @@ -325,7 +325,7 @@ "then": { "properties": { "FromFile": { - "description": "The relative path to the map in your content pack folder from which to copy (like assets/town.tbin). This can be a .tbin or .xnb file. This field supports tokens and capitalization doesn't matter.\nContent Patcher will handle tilesheets referenced by the FromFile map for you:\n - If a tilesheet isn't referenced by the target map, Content Patcher will add it for you (with a z_ ID prefix to avoid conflicts with hardcoded game logic). If the source map has a custom version of a tilesheet that's already referenced, it'll be added as a separate tilesheet only used by your tiles.\n - If you include the tilesheet file in your mod folder, Content Patcher will use that one automatically; otherwise it will be loaded from the game's Content/Maps folder." + "description": "The relative path to the map in your content pack folder from which to copy (like assets/town.tbin). This can be a .tbin, .tmx, or .xnb file. This field supports tokens and capitalization doesn't matter.\nContent Patcher will handle tilesheets referenced by the FromFile map for you:\n - If a tilesheet isn't referenced by the target map, Content Patcher will add it for you (with a z_ ID prefix to avoid conflicts with hardcoded game logic). If the source map has a custom version of a tilesheet that's already referenced, it'll be added as a separate tilesheet only used by your tiles.\n - If you include the tilesheet file in your mod folder, Content Patcher will use that one automatically; otherwise it will be loaded from the game's Content/Maps folder." }, "FromArea": { "description": "The part of the source map to copy. Defaults to the whole source map." From f3acc0b07c1d126b03fbf1ea48f45be1308dce97 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 8 Feb 2020 23:34:43 -0500 Subject: [PATCH 4/7] add Italian translations --- docs/release-notes.md | 3 +++ src/SMAPI/i18n/it.json | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 src/SMAPI/i18n/it.json diff --git a/docs/release-notes.md b/docs/release-notes.md index a365b153..78b0d132 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,9 @@ # Release notes ## Upcoming release +* For players: + * Updated translations. Thanks to xCarloC (added Italian)! + * For modders: * Added support for self-broadcasts through the multiplayer API. (Mods can now send messages to the current machine. That enables simple integrations between mods without needing an API, and lets mods notify a host mod without needing different code depending on whether the current player is the host or a farmhand.) * Eliminated unneeded network messages when broadcasting to a peer who can't handle the message (e.g. because they don't have SMAPI or don't have the target mod). diff --git a/src/SMAPI/i18n/it.json b/src/SMAPI/i18n/it.json new file mode 100644 index 00000000..43493018 --- /dev/null +++ b/src/SMAPI/i18n/it.json @@ -0,0 +1,3 @@ +{ + "warn.invalid-content-removed": "Contenuto non valido rimosso per prevenire un crash (Guarda la console di SMAPI per maggiori informazioni)." +} From 136773678e1ce623bd23f170dca265d31030d200 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 9 Feb 2020 01:04:55 -0500 Subject: [PATCH 5/7] add helper.Input.GetStatus method --- docs/release-notes.md | 1 + src/SMAPI/Framework/Input/SInputState.cs | 7 +++++++ src/SMAPI/Framework/ModHelpers/InputHelper.cs | 7 +++++++ src/SMAPI/IInputHelper.cs | 4 ++++ src/SMAPI/{Framework/Input => }/InputStatus.cs | 4 ++-- 5 files changed, 21 insertions(+), 2 deletions(-) rename src/SMAPI/{Framework/Input => }/InputStatus.cs (92%) diff --git a/docs/release-notes.md b/docs/release-notes.md index 78b0d132..4b7c359a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -7,6 +7,7 @@ * For modders: * Added support for self-broadcasts through the multiplayer API. (Mods can now send messages to the current machine. That enables simple integrations between mods without needing an API, and lets mods notify a host mod without needing different code depending on whether the current player is the host or a farmhand.) + * Added `helper.Input.GetStatus` method to get the low-level status of a button. * Eliminated unneeded network messages when broadcasting to a peer who can't handle the message (e.g. because they don't have SMAPI or don't have the target mod). * For the web UI: diff --git a/src/SMAPI/Framework/Input/SInputState.cs b/src/SMAPI/Framework/Input/SInputState.cs index 84cea36c..f54d3124 100644 --- a/src/SMAPI/Framework/Input/SInputState.cs +++ b/src/SMAPI/Framework/Input/SInputState.cs @@ -169,6 +169,13 @@ namespace StardewModdingAPI.Framework.Input return buttons.Any(button => this.IsDown(button.ToSButton())); } + /// Get the status of a button. + /// The button to check. + public InputStatus GetStatus(SButton button) + { + return this.GetStatus(this.ActiveButtons, button); + } + /********* ** Private methods diff --git a/src/SMAPI/Framework/ModHelpers/InputHelper.cs b/src/SMAPI/Framework/ModHelpers/InputHelper.cs index f4cd12b6..5858cddd 100644 --- a/src/SMAPI/Framework/ModHelpers/InputHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/InputHelper.cs @@ -50,5 +50,12 @@ namespace StardewModdingAPI.Framework.ModHelpers { this.InputState.SuppressButtons.Add(button); } + + /// Get the status of a button. + /// The button to check. + public InputStatus GetStatus(SButton button) + { + return this.InputState.GetStatus(button); + } } } diff --git a/src/SMAPI/IInputHelper.cs b/src/SMAPI/IInputHelper.cs index 328f504b..ba26773d 100644 --- a/src/SMAPI/IInputHelper.cs +++ b/src/SMAPI/IInputHelper.cs @@ -17,5 +17,9 @@ namespace StardewModdingAPI /// Prevent the game from handling a button press. This doesn't prevent other mods from receiving the event. /// The button to suppress. void Suppress(SButton button); + + /// Get the status of a button. + /// The button to check. + InputStatus GetStatus(SButton button); } } diff --git a/src/SMAPI/Framework/Input/InputStatus.cs b/src/SMAPI/InputStatus.cs similarity index 92% rename from src/SMAPI/Framework/Input/InputStatus.cs rename to src/SMAPI/InputStatus.cs index 99b0006c..d9cdd6b2 100644 --- a/src/SMAPI/Framework/Input/InputStatus.cs +++ b/src/SMAPI/InputStatus.cs @@ -1,7 +1,7 @@ -namespace StardewModdingAPI.Framework.Input +namespace StardewModdingAPI { /// The input status for a button during an update frame. - internal enum InputStatus + public enum InputStatus { /// The button was neither pressed, held, nor released. None, From 801eaa70871a9ca86dab8022abfd87d558fa1db3 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 10 Feb 2020 19:14:06 -0500 Subject: [PATCH 6/7] improve save backup logic --- docs/release-notes.md | 4 +++ src/SMAPI.Mods.SaveBackup/ModEntry.cs | 46 +++++++++++++++++---------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 4b7c359a..fb66ea1c 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,10 @@ * For players: * Updated translations. Thanks to xCarloC (added Italian)! +* For the Save Backup mod: + * Fixed warning on MacOS when you have no saves yet. + * Reduced log messages. + * For modders: * Added support for self-broadcasts through the multiplayer API. (Mods can now send messages to the current machine. That enables simple integrations between mods without needing an API, and lets mods notify a host mod without needing different code depending on whether the current player is the host or a farmhand.) * Added `helper.Input.GetStatus` method to get the low-level status of a button. diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index 8b139d8f..b8d3be1c 100644 --- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs +++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs @@ -66,29 +66,37 @@ namespace StardewModdingAPI.Mods.SaveBackup FileInfo targetFile = new FileInfo(Path.Combine(backupFolder.FullName, this.FileName)); DirectoryInfo fallbackDir = new DirectoryInfo(Path.Combine(backupFolder.FullName, this.BackupLabel)); if (targetFile.Exists || fallbackDir.Exists) + { + this.Monitor.Log("Already backed up today."); return; + } // copy saves to fallback directory (ignore non-save files/folders) - this.Monitor.Log($"Backing up saves to {fallbackDir.FullName}...", LogLevel.Trace); DirectoryInfo savesDir = new DirectoryInfo(Constants.SavesPath); - this.RecursiveCopy(savesDir, fallbackDir, entry => this.MatchSaveFolders(savesDir, entry), copyRoot: false); + if (!this.RecursiveCopy(savesDir, fallbackDir, entry => this.MatchSaveFolders(savesDir, entry), copyRoot: false)) + { + this.Monitor.Log("No saves found."); + return; + } // compress backup if possible - this.Monitor.Log("Compressing backup if possible...", LogLevel.Trace); if (!this.TryCompress(fallbackDir.FullName, targetFile, out Exception compressError)) { - if (Constants.TargetPlatform != GamePlatform.Android) // expected to fail on Android - this.Monitor.Log($"Couldn't compress backup, leaving it uncompressed.\n{compressError}", LogLevel.Trace); + this.Monitor.Log(Constants.TargetPlatform != GamePlatform.Android + ? $"Backed up to {fallbackDir.FullName}." // expected to fail on Android + : $"Backed up to {fallbackDir.FullName}. Couldn't compress backup:\n{compressError}" + ); } else + { + this.Monitor.Log($"Backed up to {targetFile.FullName}."); fallbackDir.Delete(recursive: true); - - this.Monitor.Log("Backup done!", LogLevel.Trace); + } } catch (Exception ex) { - this.Monitor.Log("Couldn't back up save files (see log file for details).", LogLevel.Warn); - this.Monitor.Log(ex.ToString(), LogLevel.Trace); + this.Monitor.Log("Couldn't back up saves (see log file for details).", LogLevel.Warn); + this.Monitor.Log(ex.ToString()); } } @@ -108,7 +116,7 @@ namespace StardewModdingAPI.Mods.SaveBackup { try { - this.Monitor.Log($"Deleting {entry.Name}...", LogLevel.Trace); + this.Monitor.Log($"Deleting {entry.Name}..."); if (entry is DirectoryInfo folder) folder.Delete(recursive: true); else @@ -123,7 +131,7 @@ namespace StardewModdingAPI.Mods.SaveBackup catch (Exception ex) { this.Monitor.Log("Couldn't remove old backups (see log file for details).", LogLevel.Warn); - this.Monitor.Log(ex.ToString(), LogLevel.Trace); + this.Monitor.Log(ex.ToString()); } } @@ -199,29 +207,33 @@ namespace StardewModdingAPI.Mods.SaveBackup /// Whether to copy the root folder itself, or false to only copy its contents. /// A filter which matches the files or directories to copy, or null to copy everything. /// Derived from the SMAPI installer code. - private void RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, Func filter, bool copyRoot = true) + /// Returns whether any files were copied. + private bool RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, Func filter, bool copyRoot = true) { - if (!targetFolder.Exists) - targetFolder.Create(); + if (!source.Exists || filter?.Invoke(source) == false) + return false; - if (filter?.Invoke(source) == false) - return; + bool anyCopied = false; switch (source) { case FileInfo sourceFile: + targetFolder.Create(); sourceFile.CopyTo(Path.Combine(targetFolder.FullName, sourceFile.Name)); + anyCopied = true; break; case DirectoryInfo sourceDir: DirectoryInfo targetSubfolder = copyRoot ? new DirectoryInfo(Path.Combine(targetFolder.FullName, sourceDir.Name)) : targetFolder; foreach (var entry in sourceDir.EnumerateFileSystemInfos()) - this.RecursiveCopy(entry, targetSubfolder, filter); + anyCopied = this.RecursiveCopy(entry, targetSubfolder, filter) || anyCopied; break; default: throw new NotSupportedException($"Unknown filesystem info type '{source.GetType().FullName}'."); } + + return anyCopied; } /// A copy filter which matches save folders. From c649572db8f2f57f5f39ed6d842529b188601206 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 10 Feb 2020 19:37:59 -0500 Subject: [PATCH 7/7] fix dialogue propagation clearing marriage dialogue --- docs/release-notes.md | 1 + src/SMAPI/Metadata/CoreAssetPropagator.cs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index fb66ea1c..a14f6175 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ * Added support for self-broadcasts through the multiplayer API. (Mods can now send messages to the current machine. That enables simple integrations between mods without needing an API, and lets mods notify a host mod without needing different code depending on whether the current player is the host or a farmhand.) * Added `helper.Input.GetStatus` method to get the low-level status of a button. * Eliminated unneeded network messages when broadcasting to a peer who can't handle the message (e.g. because they don't have SMAPI or don't have the target mod). + * Fixed marriage dialogue cleared when propagating dialogue changes. * For the web UI: * Updated the JSON validator and Content Patcher schema for `.tmx` support. diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs index 7a58d52c..8d5ad3ab 100644 --- a/src/SMAPI/Metadata/CoreAssetPropagator.cs +++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs @@ -886,10 +886,17 @@ namespace StardewModdingAPI.Metadata return false; // update dialogue + // Note that marriage dialogue isn't reloaded after reset, but it doesn't need to be + // propagated anyway since marriage dialogue keys can't be added/removed and the field + // doesn't store the text itself. foreach (NPC villager in villagers) { + MarriageDialogueReference[] marriageDialogue = villager.currentMarriageDialogue.ToArray(); + villager.resetSeasonalDialogue(); // doesn't only affect seasonal dialogue villager.resetCurrentDialogue(); + + villager.currentMarriageDialogue.Set(marriageDialogue); } return true;