summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.editorconfig2
-rw-r--r--src/StardewModdingAPI/Context.cs17
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs4
-rw-r--r--src/StardewModdingAPI/Framework/Models/ModCompatibility.cs7
-rw-r--r--src/StardewModdingAPI/Framework/SGame.cs192
-rw-r--r--src/StardewModdingAPI/Mod.cs2
-rw-r--r--src/StardewModdingAPI/Program.cs17
-rw-r--r--src/StardewModdingAPI/StardewModdingAPI.config.json307
8 files changed, 349 insertions, 199 deletions
diff --git a/src/.editorconfig b/src/.editorconfig
index 3037884e..132fe6cb 100644
--- a/src/.editorconfig
+++ b/src/.editorconfig
@@ -11,6 +11,8 @@ indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
+[*.json]
+indent_size = 2
##########
## C# formatting
diff --git a/src/StardewModdingAPI/Context.cs b/src/StardewModdingAPI/Context.cs
index 45f6a05f..6bc5ae56 100644
--- a/src/StardewModdingAPI/Context.cs
+++ b/src/StardewModdingAPI/Context.cs
@@ -4,18 +4,27 @@ using StardewValley.Menus;
namespace StardewModdingAPI
{
/// <summary>Provides information about the current game state.</summary>
- internal static class Context
+ public static class Context
{
/*********
** Accessors
*********/
+ /****
+ ** Public
+ ****/
+ /// <summary>Whether the player has loaded a save and the world has finished initialising.</summary>
+ public static bool IsWorldReady { get; internal set; }
+
+ /****
+ ** Internal
+ ****/
/// <summary>Whether a player save has been loaded.</summary>
- public static bool IsSaveLoaded => Game1.hasLoadedGame && !string.IsNullOrEmpty(Game1.player.name);
+ internal static bool IsSaveLoaded => Game1.hasLoadedGame && !string.IsNullOrEmpty(Game1.player.name);
/// <summary>Whether the game is currently writing to the save file.</summary>
- public static bool IsSaving => Game1.activeClickableMenu is SaveGameMenu || Game1.activeClickableMenu is ShippingMenu; // saving is performed by SaveGameMenu, but it's wrapped by ShippingMenu on days when the player shipping something
+ internal static bool IsSaving => Game1.activeClickableMenu is SaveGameMenu || Game1.activeClickableMenu is ShippingMenu; // saving is performed by SaveGameMenu, but it's wrapped by ShippingMenu on days when the player shipping something
/// <summary>Whether the game is currently running the draw loop.</summary>
- public static bool IsInDrawLoop { get; set; }
+ internal static bool IsInDrawLoop { get; set; }
}
}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
index 2b081edc..00d4448b 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
@@ -55,7 +55,7 @@ namespace StardewModdingAPI.Framework.ModLoading
compatibility = (
from mod in compatibilityRecords
where
- mod.ID == key
+ mod.ID.Contains(key, StringComparer.InvariantCultureIgnoreCase)
&& (mod.LowerSemanticVersion == null || !manifest.Version.IsOlderThan(mod.LowerSemanticVersion))
&& !manifest.Version.IsNewerThan(mod.UpperSemanticVersion)
select mod
@@ -93,7 +93,7 @@ namespace StardewModdingAPI.Framework.ModLoading
bool hasUnofficialUrl = !string.IsNullOrWhiteSpace(mod.Compatibility.UnofficialUpdateUrl);
string reasonPhrase = compatibility.ReasonPhrase ?? "it's not compatible with the latest version of the game";
- string error = $"{reasonPhrase}. Please check for a version newer than {compatibility.UpperVersion} here:";
+ string error = $"{reasonPhrase}. Please check for a version newer than {compatibility.UpperVersionLabel ?? compatibility.UpperVersion} here:";
if (hasOfficialUrl)
error += !hasUnofficialUrl ? $" {compatibility.UpdateUrl}" : $"{Environment.NewLine}- official mod: {compatibility.UpdateUrl}";
if (hasUnofficialUrl)
diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs
index 1e71dae0..90cbd237 100644
--- a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs
+++ b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs
@@ -12,8 +12,8 @@ namespace StardewModdingAPI.Framework.Models
/****
** From config
****/
- /// <summary>The unique mod ID.</summary>
- public string ID { get; set; }
+ /// <summary>The unique mod IDs.</summary>
+ public string[] ID { get; set; }
/// <summary>The mod name.</summary>
public string Name { get; set; }
@@ -24,6 +24,9 @@ namespace StardewModdingAPI.Framework.Models
/// <summary>The most recent incompatible mod version.</summary>
public string UpperVersion { get; set; }
+ /// <summary>A label to show to the user instead of <see cref="UpperVersion"/>, when the manifest version differs from the user-facing version.</summary>
+ public string UpperVersionLabel { get; set; }
+
/// <summary>The URL the user can check for an official updated version.</summary>
public string UpdateUrl { get; set; }
diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs
index 7ee72af2..a340b995 100644
--- a/src/StardewModdingAPI/Framework/SGame.cs
+++ b/src/StardewModdingAPI/Framework/SGame.cs
@@ -39,9 +39,6 @@ namespace StardewModdingAPI.Framework
/// <summary>The number of consecutive failed draws.</summary>
private int FailedDraws;
- /// <summary>Whether the player has loaded a save and the world has finished initialising.</summary>
- private bool IsWorldReady => this.AfterLoadTimer < 0;
-
/// <summary>Whether the game is returning to the menu.</summary>
private bool IsExiting;
@@ -90,6 +87,9 @@ namespace StardewModdingAPI.Framework
/// <summary>The keys that just entered the up state.</summary>
private Keys[] FrameReleasedKeys => this.PreviouslyPressedKeys.Except(this.CurrentlyPressedKeys).ToArray();
+ /// <summary>The previous save ID at last check.</summary>
+ private ulong PreviousSaveID;
+
/// <summary>A hash of <see cref="Game1.locations"/> at last check.</summary>
private int PreviousGameLocations;
@@ -168,7 +168,7 @@ namespace StardewModdingAPI.Framework
private static Stopwatch _fpsStopwatch => SGame.Reflection.GetPrivateField<Stopwatch>(typeof(Game1), nameof(SGame._fpsStopwatch)).GetValue();
private static float _fps
{
- set { SGame.Reflection.GetPrivateField<float>(typeof(Game1), nameof(_fps)).SetValue(value); }
+ set => SGame.Reflection.GetPrivateField<float>(typeof(Game1), nameof(_fps)).SetValue(value);
}
private static Task _newDayTask => SGame.Reflection.GetPrivateField<Task>(typeof(Game1), nameof(_newDayTask)).GetValue();
private Color bgColor => SGame.Reflection.GetPrivateField<Color>(this, nameof(bgColor)).GetValue();
@@ -202,6 +202,15 @@ namespace StardewModdingAPI.Framework
SGame.Reflection = reflection;
Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef; // required by Stardew Valley
+
+ // The game uses the default content manager instead of Game1.CreateContentManager in
+ // several cases (See http://community.playstarbound.com/threads/130058/page-27#post-3159274).
+ // The workaround is...
+ // 1. Override the default content manager.
+ // 2. Since Game1.content isn't initialised yet, and we need one main instance to
+ // support custom map tilesheets, detect when Game1.content is being initialised
+ // and use the same instance.
+ this.Content = new SContentManager(this.Content.ServiceProvider, this.Content.RootDirectory, this.Monitor);
}
/****
@@ -212,6 +221,12 @@ namespace StardewModdingAPI.Framework
/// <param name="rootDirectory">The root directory to search for content.</param>
protected override LocalizedContentManager CreateContentManager(IServiceProvider serviceProvider, string rootDirectory)
{
+ // When Game1.content is being initialised, use SMAPI's main content manager instance.
+ // See comment in SGame constructor.
+ if (Game1.content == null && this.Content is SContentManager mainContentManager)
+ return mainContentManager;
+
+ // build new instance
return new SContentManager(this.Content.ServiceProvider, this.Content.RootDirectory, this.Monitor);
}
@@ -306,6 +321,7 @@ namespace StardewModdingAPI.Framework
if (this.AfterLoadTimer == 0)
{
this.Monitor.Log($"Context: loaded saved game '{Constants.SaveFolderName}', starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
+ Context.IsWorldReady = true;
SaveEvents.InvokeAfterLoad(this.Monitor);
PlayerEvents.InvokeLoadedGame(this.Monitor, new EventArgsLoadedGameChanged(Game1.hasLoadedGame));
@@ -322,9 +338,11 @@ namespace StardewModdingAPI.Framework
this.IsExiting = true;
// after exit to title
- if (this.IsWorldReady && this.IsExiting && Game1.activeClickableMenu is TitleMenu)
+ if (Context.IsWorldReady && this.IsExiting && Game1.activeClickableMenu is TitleMenu)
{
this.Monitor.Log("Context: returned to title", LogLevel.Trace);
+ Context.IsWorldReady = false;
+
SaveEvents.InvokeAfterReturnToTitle(this.Monitor);
this.AfterLoadTimer = 5;
this.IsExiting = false;
@@ -418,109 +436,83 @@ namespace StardewModdingAPI.Framework
/*********
** World & player events
*********/
- if (this.IsWorldReady)
+ if (Context.IsWorldReady)
{
- // raise location list changed
- if (this.GetHash(Game1.locations) != this.PreviousGameLocations)
- {
- LocationEvents.InvokeLocationsChanged(this.Monitor, Game1.locations);
- this.PreviousGameLocations = this.GetHash(Game1.locations);
- }
-
- // raise current location changed
- if (Game1.currentLocation != this.PreviousGameLocation)
- {
- if (this.VerboseLogging)
- this.Monitor.Log($"Context: set location to {Game1.currentLocation?.Name ?? "(none)"}.", LogLevel.Trace);
- LocationEvents.InvokeCurrentLocationChanged(this.Monitor, this.PreviousGameLocation, Game1.currentLocation);
- this.PreviousGameLocation = Game1.currentLocation;
- }
-
- // raise player changed
- if (Game1.player != this.PreviousFarmer)
- {
- PlayerEvents.InvokeFarmerChanged(this.Monitor, this.PreviousFarmer, Game1.player);
- this.PreviousFarmer = Game1.player;
- }
-
- // raise player leveled up a skill
- if (Game1.player.combatLevel != this.PreviousCombatLevel)
- {
- PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Combat, Game1.player.combatLevel);
- this.PreviousCombatLevel = Game1.player.combatLevel;
- }
- if (Game1.player.farmingLevel != this.PreviousFarmingLevel)
- {
- PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Farming, Game1.player.farmingLevel);
- this.PreviousFarmingLevel = Game1.player.farmingLevel;
- }
- if (Game1.player.fishingLevel != this.PreviousFishingLevel)
- {
- PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Fishing, Game1.player.fishingLevel);
- this.PreviousFishingLevel = Game1.player.fishingLevel;
- }
- if (Game1.player.foragingLevel != this.PreviousForagingLevel)
- {
- PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Foraging, Game1.player.foragingLevel);
- this.PreviousForagingLevel = Game1.player.foragingLevel;
- }
- if (Game1.player.miningLevel != this.PreviousMiningLevel)
- {
- PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Mining, Game1.player.miningLevel);
- this.PreviousMiningLevel = Game1.player.miningLevel;
- }
- if (Game1.player.luckLevel != this.PreviousLuckLevel)
+ // raise events (only when something changes, not on the initial load)
+ if (Game1.uniqueIDForThisGame == this.PreviousSaveID)
{
- PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Luck, Game1.player.luckLevel);
- this.PreviousLuckLevel = Game1.player.luckLevel;
- }
+ // raise location list changed
+ if (this.GetHash(Game1.locations) != this.PreviousGameLocations)
+ LocationEvents.InvokeLocationsChanged(this.Monitor, Game1.locations);
- // raise player inventory changed
- ItemStackChange[] changedItems = this.GetInventoryChanges(Game1.player.items, this.PreviousItems).ToArray();
- if (changedItems.Any())
- {
- PlayerEvents.InvokeInventoryChanged(this.Monitor, Game1.player.items, changedItems);
- this.PreviousItems = Game1.player.items.Where(n => n != null).ToDictionary(n => n, n => n.Stack);
- }
-
- // raise current location's object list changed
- {
- int? objectHash = Game1.currentLocation?.objects != null ? this.GetHash(Game1.currentLocation.objects) : (int?)null;
- if (objectHash != null && this.PreviousLocationObjects != objectHash)
+ // raise current location changed
+ if (Game1.currentLocation != this.PreviousGameLocation)
{
- LocationEvents.InvokeOnNewLocationObject(this.Monitor, Game1.currentLocation.objects);
- this.PreviousLocationObjects = objectHash.Value;
+ if (this.VerboseLogging)
+ this.Monitor.Log($"Context: set location to {Game1.currentLocation?.Name ?? "(none)"}.", LogLevel.Trace);
+ LocationEvents.InvokeCurrentLocationChanged(this.Monitor, this.PreviousGameLocation, Game1.currentLocation);
}
- }
- // raise time changed
- if (Game1.timeOfDay != this.PreviousTime)
- {
- TimeEvents.InvokeTimeOfDayChanged(this.Monitor, this.PreviousTime, Game1.timeOfDay);
- this.PreviousTime = Game1.timeOfDay;
- }
- if (Game1.dayOfMonth != this.PreviousDay)
- {
- TimeEvents.InvokeDayOfMonthChanged(this.Monitor, this.PreviousDay, Game1.dayOfMonth);
- this.PreviousDay = Game1.dayOfMonth;
- }
- if (Game1.currentSeason != this.PreviousSeason)
- {
- TimeEvents.InvokeSeasonOfYearChanged(this.Monitor, this.PreviousSeason, Game1.currentSeason);
- this.PreviousSeason = Game1.currentSeason;
- }
- if (Game1.year != this.PreviousYear)
- {
- TimeEvents.InvokeYearOfGameChanged(this.Monitor, this.PreviousYear, Game1.year);
- this.PreviousYear = Game1.year;
- }
+ // raise player changed
+ if (Game1.player != this.PreviousFarmer)
+ PlayerEvents.InvokeFarmerChanged(this.Monitor, this.PreviousFarmer, Game1.player);
+
+ // raise player leveled up a skill
+ if (Game1.player.combatLevel != this.PreviousCombatLevel)
+ PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Combat, Game1.player.combatLevel);
+ if (Game1.player.farmingLevel != this.PreviousFarmingLevel)
+ PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Farming, Game1.player.farmingLevel);
+ if (Game1.player.fishingLevel != this.PreviousFishingLevel)
+ PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Fishing, Game1.player.fishingLevel);
+ if (Game1.player.foragingLevel != this.PreviousForagingLevel)
+ PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Foraging, Game1.player.foragingLevel);
+ if (Game1.player.miningLevel != this.PreviousMiningLevel)
+ PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Mining, Game1.player.miningLevel);
+ if (Game1.player.luckLevel != this.PreviousLuckLevel)
+ PlayerEvents.InvokeLeveledUp(this.Monitor, EventArgsLevelUp.LevelType.Luck, Game1.player.luckLevel);
+
+ // raise player inventory changed
+ ItemStackChange[] changedItems = this.GetInventoryChanges(Game1.player.items, this.PreviousItems).ToArray();
+ if (changedItems.Any())
+ PlayerEvents.InvokeInventoryChanged(this.Monitor, Game1.player.items, changedItems);
+
+ // raise current location's object list changed
+ if (this.GetHash(Game1.currentLocation.objects) != this.PreviousLocationObjects)
+ LocationEvents.InvokeOnNewLocationObject(this.Monitor, Game1.currentLocation.objects);
- // raise mine level changed
- if (Game1.mine != null && Game1.mine.mineLevel != this.PreviousMineLevel)
- {
- MineEvents.InvokeMineLevelChanged(this.Monitor, this.PreviousMineLevel, Game1.mine.mineLevel);
- this.PreviousMineLevel = Game1.mine.mineLevel;
+ // raise time changed
+ if (Game1.timeOfDay != this.PreviousTime)
+ TimeEvents.InvokeTimeOfDayChanged(this.Monitor, this.PreviousTime, Game1.timeOfDay);
+ if (Game1.dayOfMonth != this.PreviousDay)
+ TimeEvents.InvokeDayOfMonthChanged(this.Monitor, this.PreviousDay, Game1.dayOfMonth);
+ if (Game1.currentSeason != this.PreviousSeason)
+ TimeEvents.InvokeSeasonOfYearChanged(this.Monitor, this.PreviousSeason, Game1.currentSeason);
+ if (Game1.year != this.PreviousYear)
+ TimeEvents.InvokeYearOfGameChanged(this.Monitor, this.PreviousYear, Game1.year);
+
+ // raise mine level changed
+ if (Game1.mine != null && Game1.mine.mineLevel != this.PreviousMineLevel)
+ MineEvents.InvokeMineLevelChanged(this.Monitor, this.PreviousMineLevel, Game1.mine.mineLevel);
}
+
+ // update state
+ this.PreviousGameLocations = this.GetHash(Game1.locations);
+ this.PreviousGameLocation = Game1.currentLocation;
+ this.PreviousFarmer = Game1.player;
+ this.PreviousCombatLevel = Game1.player.combatLevel;
+ this.PreviousFarmingLevel = Game1.player.farmingLevel;
+ this.PreviousFishingLevel = Game1.player.fishingLevel;
+ this.PreviousForagingLevel = Game1.player.foragingLevel;
+ this.PreviousMiningLevel = Game1.player.miningLevel;
+ this.PreviousLuckLevel = Game1.player.luckLevel;
+ this.PreviousItems = Game1.player.items.Where(n => n != null).ToDictionary(n => n, n => n.Stack);
+ this.PreviousLocationObjects = this.GetHash(Game1.currentLocation.objects);
+ this.PreviousTime = Game1.timeOfDay;
+ this.PreviousDay = Game1.dayOfMonth;
+ this.PreviousSeason = Game1.currentSeason;
+ this.PreviousYear = Game1.year;
+ this.PreviousMineLevel = Game1.mine.mineLevel;
+ this.PreviousSaveID = Game1.uniqueIDForThisGame;
}
/*********
diff --git a/src/StardewModdingAPI/Mod.cs b/src/StardewModdingAPI/Mod.cs
index a65b135c..171088cf 100644
--- a/src/StardewModdingAPI/Mod.cs
+++ b/src/StardewModdingAPI/Mod.cs
@@ -116,7 +116,7 @@ namespace StardewModdingAPI
return Path.Combine(this.PathOnDisk, "psconfigs");
}
- /// <summary>Release or reset unmanaged resources.</summary>
+ /// <summary>Release or reset unmanaged resources when the game exits. There's no guarantee this will be called on every exit.</summary>
/// <param name="disposing">Whether the instance is being disposed explicitly rather than finalised. If this is false, the instance shouldn't dispose other objects since they may already be finalised.</param>
protected virtual void Dispose(bool disposing) { }
diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs
index 743de050..228071ce 100644
--- a/src/StardewModdingAPI/Program.cs
+++ b/src/StardewModdingAPI/Program.cs
@@ -324,9 +324,20 @@ namespace StardewModdingAPI
IList<Action> deprecationWarnings = new List<Action>();
foreach (IModMetadata mod in mods)
{
- // missing unique ID
- if (string.IsNullOrWhiteSpace(mod.Manifest.UniqueID))
- deprecationWarnings.Add(() => this.Monitor.Log($"{mod.DisplayName} doesn't have specify a {nameof(IManifest.UniqueID)} field in its manifest. This will be required in an upcoming SMAPI release.", LogLevel.Warn));
+ // missing fields that will be required in SMAPI 2.0
+ {
+ List<string> missingFields = new List<string>(3);
+
+ if (string.IsNullOrWhiteSpace(mod.Manifest.Name))
+ missingFields.Add(nameof(IManifest.Name));
+ if (mod.Manifest.Version.ToString() == "0.0")
+ missingFields.Add(nameof(IManifest.Version));
+ if (string.IsNullOrWhiteSpace(mod.Manifest.UniqueID))
+ missingFields.Add(nameof(IManifest.UniqueID));
+
+ if (missingFields.Any())
+ deprecationWarnings.Add(() => this.Monitor.Log($"{mod.Manifest.Name} is missing some manifest fields ({string.Join(", ", missingFields)}) which will be required in an upcoming SMAPI version.", LogLevel.Warn));
+ }
// per-save directories
if ((mod.Manifest as Manifest)?.PerSaveConfigs == true)
diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json
index f42a4dfc..08bd3cff 100644
--- a/src/StardewModdingAPI/StardewModdingAPI.config.json
+++ b/src/StardewModdingAPI/StardewModdingAPI.config.json
@@ -34,251 +34,384 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha
"ModCompatibility": [
{
"Name": "AccessChestAnywhere",
- "ID": "AccessChestAnywhere",
+ "ID": [ "AccessChestAnywhere" ],
"UpperVersion": "1.1",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/257",
"UnofficialUpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/518",
- "Notes": "Crashes with 'Method not found: Void StardewValley.Item.set_Name(System.String)'."
+ "Notes": "Needs update for SDV 1.1."
},
{
"Name": "Almighty Tool",
- "ID": "AlmightyTool.dll",
+ "ID": [ "AlmightyTool.dll" ],
"UpperVersion": "1.1.1",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/439",
- "Notes": "Uses obsolete StardewModdingAPI.Extensions."
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Better Sprinklers",
- "ID": "SPDSprinklersMod",
+ "ID": [ "SPDSprinklersMod", /*since 2.3*/ "Speeder.BetterSprinklers" ],
"UpperVersion": "2.3",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/41",
- "Notes": "Uses obsolete StardewModdingAPI.Extensions."
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.2."
},
{
- "Name": "Better Sprinklers",
- "ID": "Speeder.BetterSprinklers",
- "UpperVersion": "2.3",
+ "Name": "Birthday Mail",
+ "ID": [ "005e02dc-d900-425c-9c68-1ff55c5a295d" ],
+ "UpperVersion": "1.2.2",
"Compatibility": "AssumeBroken",
- "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/41",
- "Notes": "ID changed in 2.3. Uses obsolete StardewModdingAPI.Extensions."
+ "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/276",
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Chest Label System",
- "ID": "SPDChestLabel",
- "UpperVersion": "1.5",
+ "ID": [ "SPDChestLabel" ],
+ "UpperVersion": "1.6",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/242",
- "Notes": "Not compatible with Stardew Valley 1.1+"
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.1."
},
{
- "Name": "Chests Anywhere",
- "ID": "ChestsAnywhere",
- "UpperVersion": "1.8.2",
+ "Name": "Chest Pooling",
+ "ID": [ "ChestPooling.dll" ],
+ "UpperVersion": "1.2",
"Compatibility": "AssumeBroken",
- "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/518",
- "Notes": "Crashes with 'Method not found: Void StardewValley.Menus.TextBox.set_Highlighted(Boolean)'."
+ "UpdateUrl": "http://community.playstarbound.com/threads/111988",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Chests Anywhere",
- "ID": "Pathoschild.ChestsAnywhere",
+ "ID": [ "ChestsAnywhere", /*since 1.9*/ "Pathoschild.ChestsAnywhere" ],
"UpperVersion": "1.9-beta",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/518",
- "Notes": "ID changed in 1.9. Crashes with InvalidOperationException: 'The menu doesn't seem to have a player inventory'."
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "CJB Automation",
- "ID": "CJBAutomation",
+ "ID": [ "CJBAutomation" ],
"UpperVersion": "1.4",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/211",
- "Notes": "Crashes with 'Method not found: Void StardewValley.Item.set_Name(System.String)'."
+ "UnofficialUpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/1063",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "CJB Cheats Menu",
- "ID": "CJBCheatsMenu",
+ "ID": [ "CJBCheatsMenu" ],
"UpperVersion": "1.12",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/4",
- "Notes": "Not compatible with Stardew Valley 1.1+"
+ "Notes": "Needs update for SDV 1.1."
},
{
"Name": "CJB Item Spawner",
- "ID": "CJBItemSpawner",
+ "ID": [ "CJBItemSpawner" ],
"UpperVersion": "1.5",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/93",
- "Notes": "Not compatible with Stardew Valley 1.1+"
+ "Notes": "Needs update for SDV 1.1."
},
{
"Name": "CJB Show Item Sell Price",
- "ID": "CJBShowItemSellPrice",
+ "ID": [ "CJBShowItemSellPrice" ],
"UpperVersion": "1.6",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/93",
- "Notes": "Uses SMAPI's internal SGame class."
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Cooking Skill",
- "ID": "CookingSkill",
+ "ID": [ "CookingSkill" ],
"UpperVersion": "1.0.3",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/522",
- "Notes": "Crashes with 'Method not found: Void StardewValley.Buff..ctor(Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, System.String)'."
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "Cooking Skill Prestige Adapter",
+ "ID": [ "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63" ],
+ "UpperVersion": "1.0.4",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/569",
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Enemy Health Bars",
- "ID": "SPDHealthBar",
+ "ID": [ "SPDHealthBar" ],
"UpperVersion": "1.7",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/193",
- "Notes": "Uses obsolete GraphicsEvents.DrawTick."
+ "UnofficialUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Entoarox Framework",
- "ID": "eacdb74b-4080-4452-b16b-93773cda5cf9",
- "UpperVersion": "1.6.5",
+ "ID": [ "eacdb74b-4080-4452-b16b-93773cda5cf9", /*since ???*/ "Entoarox.EntoaroxFramework" ],
+ "UpperVersion": "1.7.5",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://community.playstarbound.com/resources/4228",
- "Notes": "Uses obsolete StardewModdingAPI.Inheritance.SObject until 1.6.1; then crashes until 1.6.4 ('Entoarox Framework requested an immediate game shutdown: Fatal error attempting to update player tick properties System.NullReferenceException: Object reference not set to an instance of an object. at Entoarox.Framework.PlayerHelper.Update(Object s, EventArgs e)')."
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Extended Fridge",
- "ID": "Mystra007ExtendedFridge",
+ "ID": [ "Mystra007ExtendedFridge" ],
"UpperVersion": "1.0",
+ "UpperVersionLabel": "0.94",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/485",
- "Notes": "Actual upper version is 0.94, but mod incorrectly sets it to 1.0 in the manifest. Crashes with 'Field not found: StardewValley.Game1.mouseCursorTransparency'."
+ "Notes": "Needs update for SDV 1.2. Actual upper version is 0.94, but mod incorrectly sets it to 1.0 in the manifest."
+ },
+ {
+ "Name": "FarmAutomation.ItemCollector",
+ "ID": [ "FarmAutomation.ItemCollector.dll", /*since 0.4*/ "Maddy99.FarmAutomation.ItemCollector" ],
+ "UpperVersion": "0.4",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/threads/125172",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "Instant Geode",
+ "ID": [ "InstantGeode" ],
+ "UpperVersion": "1.12",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/threads/109038",
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "Gate Opener",
+ "ID": [ "GateOpener.dll", /*since 1.1*/ "mralbobo.GateOpener" ],
+ "UpperVersion": "1.0.1",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/threads/111988",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Get Dressed",
- "ID": "GetDressed.dll",
- "UpperVersion": "3.2",
+ "ID": [ "GetDressed.dll", /*since 3.3*/ "Advize.GetDressed" ],
+ "UpperVersion": "3.3",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/331",
- "Notes": "Crashes with NullReferenceException in GameEvents.UpdateTick."
+ "Notes": "Needs update for SDV 1.2."
},
{
- "Name": "Lookup Anything",
- "ID": "LookupAnything",
- "UpperVersion": "1.10",
+ "Name": "Gift Taste Helper",
+ "ID": [ "8008db57-fa67-4730-978e-34b37ef191d6" ],
+ "UpperVersion": "2.3.1",
"Compatibility": "AssumeBroken",
- "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/541",
- "Notes": "Crashes with FormatException when looking up NPCs."
+ "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/229",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Lookup Anything",
- "ID": "Pathoschild.LookupAnything",
+ "ID": [ "LookupAnything", /*since 1.10.1*/ "Pathoschild.LookupAnything" ],
"UpperVersion": "1.10.1",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/541",
- "Notes": "ID changed in 1.10.1. Crashes with FormatException when looking up NPCs."
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Makeshift Multiplayer",
- "ID": "StardewValleyMP",
+ "ID": [ "StardewValleyMP", /*since 0.3*/ "spacechase0.StardewValleyMP" ],
"Compatibility": "AssumeBroken",
- "UpperVersion": "0.2.10",
+ "UpperVersion": "0.3.3",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/501",
- "Notes": "Uses obsolete GraphicsEvents.OnPreRenderHudEventNoCheck."
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "NoSoilDecay",
- "ID": "289dee03-5f38-4d8e-8ffc-e440198e8610",
+ "ID": [ "289dee03-5f38-4d8e-8ffc-e440198e8610" ],
"UpperVersion": "0.5",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/237",
- "Notes": "Uses obsolete StardewModdingAPI.Extensions and Assembly.GetExecutingAssembly().Location."
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.2, and uses Assembly.GetExecutingAssembly().Location."
},
{
"Name": "NPC Map Locations",
- "ID": "NPCMapLocationsMod",
+ "ID": [ "NPCMapLocationsMod" ],
"LowerVersion": "1.42",
"UpperVersion": "1.43",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/239",
- "ReasonPhrase": "this version has an update check error which crashes the game"
+ "ReasonPhrase": "These versions have an update check error which crash the game."
+ },
+ {
+ "Name": "Part of the Community",
+ "ID": [ "SB_PotC" ],
+ "UpperVersion": "1.0.8",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/923",
+ "ReasonPhrase": "Needs update for SDV 1.2."
},
{
"Name": "Point-and-Plant",
- "ID": "PointAndPlant.dll",
+ "ID": [ "PointAndPlant.dll" ],
"UpperVersion": "1.0.2",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/572",
- "Notes": "Uses obsolete StardewModdingAPI.Extensions."
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "PrairieKingMadeEasy",
+ "ID": [ "PrairieKingMadeEasy.dll" ],
+ "UpperVersion": "1.0.0",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/resources/3594",
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "Rush Orders",
+ "ID": [ "RushOrders", /*since 1.1*/ "spacechase0.RushOrders" ],
+ "UpperVersion": "1.1",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/605",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Save Anywhere",
- "ID": "SaveAnywhere",
- "UpperVersion": "2.0",
+ "ID": [ "SaveAnywhere" ],
+ "UpperVersion": "2.3",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/444",
- "Notes": "Depends on StarDustCore."
+ "Notes": "Needs update for SDV 1.2."
},
{
- "Name": "StackSplitX",
- "ID": "StackSplitX.dll",
+ "Name": "Simple Sprinklers",
+ "ID": [ "SimpleSprinkler.dll" ],
+ "UpperVersion": "1.4",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/76",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "Sprint and Dash",
+ "ID": [ "SPDSprintAndDash" ],
"UpperVersion": "1.0",
"Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/resources/3531",
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/resources/4201",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "Sprint and Dash Redux",
+ "ID": [ "SPDSprintAndDash" ],
+ "UpperVersion": "1.2",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/resources/4201",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "StackSplitX",
+ "ID": [ "StackSplitX.dll" ],
+ "UpperVersion": "1.2",
+ "Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/798",
- "Notes": "Uses SMAPI's internal SGame class."
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "StarDustCore",
- "ID": "StarDustCore",
+ "ID": [ "StarDustCore" ],
"UpperVersion": "1.0",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/683",
- "Notes": "Crashes with 'Method not found: Void StardewModdingAPI.Command.CallCommand(System.String)'."
+ "Notes": "Obsolete (originally needed by Save Anywhere); broken in SDV 1.2."
},
{
"Name": "Teleporter",
- "ID": "Teleporter",
+ "ID": [ "Teleporter" ],
"UpperVersion": "1.0.2",
"Compatibility": "AssumeBroken",
"UpdateUrl": "http://community.playstarbound.com/resources/4374",
- "Notes": "Crashes with 'InvalidOperationException: The StardewValley.Menus.MapPage object doesn't have a private 'points' instance field'."
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "UiModSuite",
+ "ID": [ "Demiacle.UiModSuite" ],
+ "UpperVersion": "0.5",
+ "UpperVersionLabel": "1.0",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/1023",
+ "Notes": "Needs update for SDV 1.2. Actual upper version is 1.0, but mod incorrectly sets it to 0.5 in the manifest."
+ },
+ {
+ "Name": "Weather Controller",
+ "ID": [ "WeatherController.dll" ],
+ "UpperVersion": "1.0.2",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/threads/111526",
+ "UnofficialUpdateUrl": "http://community.playstarbound.com/threads/132096",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "zDailyIncrease",
+ "ID": [ "zdailyincrease" ],
+ "UpperVersion": "1.2",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/resources/4247",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "Zoom Out Extreme",
+ "ID": [ "ZoomMod" ],
+ "UpperVersion": "0.1",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "http://community.playstarbound.com/threads/115028",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Zoryn's Better RNG",
- "ID": "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6",
- "UpperVersion": "1.5",
+ "ID": [ "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6", /*since 1.6*/ "Zoryn.BetterRNG" ],
+ "UpperVersion": "1.6",
"Compatibility": "AssumeBroken",
- "UpdateUrl": "http://community.playstarbound.com/threads/108756",
- "Notes": "Uses SMAPI's internal SGame class."
+ "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Zoryn's Calendar Anywhere",
- "ID": "a41c01cd-0437-43eb-944f-78cb5a53002a",
- "UpperVersion": "1.5",
+ "ID": [ "a41c01cd-0437-43eb-944f-78cb5a53002a", /*since 1.6*/ "Zoryn.CalendarAnywhere" ],
+ "UpperVersion": "1.6",
"Compatibility": "AssumeBroken",
- "UpdateUrl": "http://community.playstarbound.com/threads/108756",
- "Notes": "Uses SMAPI's internal SGame class."
+ "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Zoryn's Health Bars",
- "ID": "HealthBars.dll",
- "UpperVersion": "1.5",
+ "ID": [ "HealthBars.dll", /*since 1.6*/ "Zoryn.HealthBars" ],
+ "UpperVersion": "1.6",
+ "Compatibility": "AssumeBroken",
+ "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases",
+ "Notes": "Needs update for SDV 1.2."
+ },
+ {
+ "Name": "Zoryn's Junimo Deposit Anywhere",
+ "ID": [ "f93a4fe8-cade-4146-9335-b5f82fbbf7bc", /*since 1.6*/ "Zoryn.JunimoDepositAnywhere" ],
+ "UpperVersion": "1.7",
"Compatibility": "AssumeBroken",
- "UpdateUrl": "http://community.playstarbound.com/threads/108756",
- "Notes": "Uses SMAPI's internal SGame class."
+ "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Zoryn's Movement Mod",
- "ID": "8a632929-8335-484f-87dd-c29d2ba3215d",
- "UpperVersion": "1.5",
+ "ID": [ "8a632929-8335-484f-87dd-c29d2ba3215d", /*since 1.6*/ "Zoryn.MovementModifier" ],
+ "UpperVersion": "1.6",
"Compatibility": "AssumeBroken",
- "UpdateUrl": "http://community.playstarbound.com/threads/108756",
- "Notes": "Uses SMAPI's internal SGame class."
+ "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases",
+ "Notes": "Needs update for SDV 1.2."
},
{
"Name": "Zoryn's Regen Mod",
- "ID": "dfac4383-1b6b-4f33-ae4e-37fc23e5252e",
- "UpperVersion": "1.5",
+ "ID": [ "dfac4383-1b6b-4f33-ae4e-37fc23e5252e", /*since 1.6*/ "Zoryn.RegenMod" ],
+ "UpperVersion": "1.6",
"Compatibility": "AssumeBroken",
- "UpdateUrl": "http://community.playstarbound.com/threads/108756",
- "Notes": "Uses SMAPI's internal SGame class."
+ "UpdateUrl": "https://github.com/Zoryn4163/SMAPI-Mods/releases",
+ "Notes": "Needs update for SDV 1.2."
}
]
}