Line 4: |
Line 4: |
| | | |
| ==Metadata== | | ==Metadata== |
| + | ===Mod path=== |
| + | Before handling mod folder paths, be aware that: |
| + | * The '''mod's folder path is not consistent'''. The game is installed to different folders, Nexus mods are often unzipped into a folder like <samp>Mods/Your Mod Name 1.27.5-541-1-27-5-1598664794/YourModFolder</samp> by default, and players can organize their mod folders like <samp>Mods/For single-player/YourModFolder</samp>. |
| + | * Paths are formatted differently on Linux/Mac/Android vs Windows. |
| + | |
| + | You don't need to worry about that when using SMAPI APIs, which take relative paths and automatically fix the format if needed: |
| + | <syntaxhighlight lang="c#"> |
| + | var data = this.Helper.Data.ReadJsonFile<SomeDataModel>("assets/data.json"); |
| + | </syntaxhighlight> |
| + | |
| + | If you really need a full path, you should use <samp>this.Helper.DirectoryPath</samp> and <samp>Path.Combine</samp> to get it: |
| + | <syntaxhighlight lang="c#"> |
| + | string path = Path.Combine(this.Helper.DirectoryPath, "assets", "data.json"); // "assets/data.json" in the current mod's folder |
| + | var file = new FileInfo(path); |
| + | </syntaxhighlight> |
| + | |
| + | See ''[[#Constants|Constants]]'' for other paths like the game folder. |
| + | |
| ===Constants=== | | ===Constants=== |
− | The <tt>Constants</tt> class provides metadata about SMAPI and the game. | + | The <samp>Constants</samp> class provides metadata about SMAPI and the game. |
| | | |
| {| class="wikitable" | | {| class="wikitable" |
Line 12: |
Line 30: |
| ! meaning | | ! meaning |
| |- | | |- |
− | | <tt>Constants.ApiVersion</tt> | + | | <samp>Constants.ApiVersion</samp> |
| | The version of the running SMAPI instance. | | | The version of the running SMAPI instance. |
| |- | | |- |
− | | <tt>Constants.MinimumGameVersion</tt><br /><tt>Constants.MaximumGameVersion</tt> | + | | <samp>Constants.MinimumGameVersion</samp><br /><samp>Constants.MaximumGameVersion</samp> |
| | The game versions supported by the running SMAPI instance. | | | The game versions supported by the running SMAPI instance. |
| |- | | |- |
− | | <tt>Constants.TargetPlatform</tt> | + | | <samp>Constants.TargetPlatform</samp> |
− | | The current operating system (one of <tt>Android</tt>, <tt>Linux</tt>, <tt>Mac</tt>, or <tt>Windows</tt>). | + | | The current operating system (one of <samp>Android</samp>, <samp>Linux</samp>, <samp>Mac</samp>, or <samp>Windows</samp>). |
| + | |- |
| + | | <samp>Constants.GameFramework</samp> |
| + | | The game framework running the game (one of <samp>Xna</samp> or <samp>MonoGame</samp>). |
| + | |- |
| + | | <samp>Constants.GamePath</samp> |
| + | | The absolute path to the <samp>Stardew Valley</samp> folder. |
| |- | | |- |
− | | <tt>Constants.ExecutionPath</tt> | + | | <samp>Constants.ContentPath</samp> |
− | | The absolute path to the <tt>Stardew Valley</tt> folder. | + | | The absolute path to the game's <samp>Content</samp> folder. |
| |- | | |- |
− | | <tt>Constants.DataPath</tt> | + | | <samp>Constants.DataPath</samp> |
| | The absolute path to the game's data folder (which contains the [[Saves|save folder]]). | | | The absolute path to the game's data folder (which contains the [[Saves|save folder]]). |
| |- | | |- |
− | | <tt>Constants.LogDir</tt> | + | | <samp>Constants.LogDir</samp> |
| | The absolute path to the folder containing the game and SMAPI logs. | | | The absolute path to the folder containing the game and SMAPI logs. |
| |- | | |- |
− | | <tt>Constants.SavesPath</tt> | + | | <samp>Constants.SavesPath</samp> |
| | The absolute path to the [[Saves|save folder]]. | | | The absolute path to the [[Saves|save folder]]. |
| |- | | |- |
− | | <tt>Constants.CurrentSavePath</tt> | + | | <samp>Constants.CurrentSavePath</samp> |
| | The absolute path to the current save folder, if a save is loaded. | | | The absolute path to the current save folder, if a save is loaded. |
| |- | | |- |
− | | <tt>Constants.SaveFolderName</tt> | + | | <samp>Constants.SaveFolderName</samp> |
− | | The name of the current save folder (like <tt>Name_012345789</tt>), if a save is loaded. | + | | The name of the current save folder (like <samp>Name_012345789</samp>), if a save is loaded. |
| |} | | |} |
| | | |
| ===Context=== | | ===Context=== |
− | The <tt>Context</tt> class provides information about the game state and player control: | + | The <samp>Context</samp> class provides information about the game state and player control. |
| | | |
− | {| class="wikitable" | + | ; Game/player state: |
| + | : {| class="wikitable" |
| |- | | |- |
| ! value | | ! value |
| ! meaning | | ! meaning |
| |- | | |- |
− | | <tt>Context.IsGameLaunched</tt> | + | | <samp>Context.IsGameLaunched</samp> |
| | Whether the game has been launched and initialised. This becomes true immediately before the first update tick. | | | Whether the game has been launched and initialised. This becomes true immediately before the first update tick. |
| |- | | |- |
− | | <tt>Context.IsWorldReady</tt> | + | | <samp>Context.IsWorldReady</samp> |
| | Whether the player has loaded a save and the world has finished initialising. Useful for ignoring events before the save is loaded. | | | Whether the player has loaded a save and the world has finished initialising. Useful for ignoring events before the save is loaded. |
| |- | | |- |
− | | <tt>Context.IsPlayerFree</tt> | + | | <samp>Context.IsPlayerFree</samp> |
− | | Whether <tt>Context.IsWorldReady</tt> and the player is free to act on the world (no menu is displayed, no cutscene is in progress, etc). | + | | Whether <samp>Context.IsWorldReady</samp> and the player is free to act on the world (no menu is displayed, no cutscene is in progress, etc). |
| |- | | |- |
− | | <tt>Context.CanPlayerMove</tt> | + | | <samp>Context.CanPlayerMove</samp> |
− | | Whether <tt>Context.IsPlayerFree</tt> and the player is free to move (e.g. not using a tool). | + | | Whether <samp>Context.IsPlayerFree</samp> and the player is free to move (''e.g.,'' not using a tool). |
| |- | | |- |
− | | <tt>Context.IsMultiplayer</tt> | + | | <samp>Context.IsInDrawLoop</samp> |
− | | Whether <tt>Context.IsWorldReady</tt>, and the player loaded the save in multiplayer mode (regardless of whether any other players are connected). | + | | Whether the game is currently running the draw loop. This isn't relevant to most mods, since you should use [[Modding:Modder Guide/APIs/Events|display events]] to draw to the screen. |
| + | |} |
| + | |
| + | ; [[Multiplayer]]: |
| + | : {| class="wikitable" |
| |- | | |- |
− | | <tt>Context.IsMainPlayer</tt> | + | ! value |
− | | Whether <tt>Context.IsWorldReady</tt>, and the player is the main player. This is always true in single-player, and true when hosting in multiplayer. | + | ! meaning |
| + | |- |
| + | | <samp>Context.IsMultiplayer</samp> |
| + | | Whether <samp>Context.IsWorldReady</samp>, and the world was loaded in multiplayer mode (regardless of whether any other players are connected) or is currently in split-screen mode. |
| + | |- |
| + | | <samp>Context.IsSplitScreen</samp> |
| + | | Whether <samp>Context.IsMultiplayer</samp> and the ''current player'' is in split-screen mode. This doesn't apply for remote players. |
| + | |- |
| + | | <samp>Context.HasRemotePlayers</samp> |
| + | | Whether <samp>Context.IsMultiplayer</samp> and any players are connected over the network. |
| + | |- |
| + | | <samp>Context.IsMainPlayer</samp> |
| + | | Whether <samp>Context.IsWorldReady</samp>, and the player is the main player. This is always true in single-player, and true when hosting in multiplayer. |
| + | |- |
| + | | <samp>Context.IsOnHostComputer</samp> |
| + | | Whether the current player is on the host computer. This is true when <samp>Context.IsMainPlayer</samp>, or for farmhands in split-screen mode. |
| + | |- |
| + | | <samp>Context.ScreenId</samp> |
| + | | The unique ID of the current screen in split-screen mode. The main player always has ID 0. A screen is always assigned a new ID when it's opened (so a player who quits and rejoins will get a new screen ID). |
| |} | | |} |
| | | |
| ==Helpers== | | ==Helpers== |
| ===Dates=== | | ===Dates=== |
− | Use <tt>SDate</tt> for calculating in-game dates. You start by creating a date: | + | Use <samp>SDate</samp> for calculating in-game dates. You start by creating a date: |
− | <source lang="c#"> | + | <syntaxhighlight lang="c#"> |
| var date = SDate.Now(); // current date | | var date = SDate.Now(); // current date |
| var date = new SDate(28, "spring"); // date in the current year | | var date = new SDate(28, "spring"); // date in the current year |
| var date = new SDate(28, "spring", 2); // date in the given year | | var date = new SDate(28, "spring", 2); // date in the given year |
| var date = SDate.From(Game1.Date); // from a game date | | var date = SDate.From(Game1.Date); // from a game date |
− | </source> | + | </syntaxhighlight> |
| | | |
| Then you can calculate offsets from any date: | | Then you can calculate offsets from any date: |
− | <source lang="c#"> | + | <syntaxhighlight lang="c#"> |
| // add days | | // add days |
| new SDate(28, "spring", 1).AddDays(370); // 06 fall in year 4 | | new SDate(28, "spring", 1).AddDays(370); // 06 fall in year 4 |
Line 84: |
Line 131: |
| // subtract days | | // subtract days |
| new SDate(01, "spring", 2).AddDays(-1); // 28 winter in year 1 | | new SDate(01, "spring", 2).AddDays(-1); // 28 winter in year 1 |
− | </source> | + | </syntaxhighlight> |
| | | |
| ...and compare dates: | | ...and compare dates: |
− | <source lang="c#"> | + | <syntaxhighlight lang="c#"> |
| var a = new SDate(01, "spring"); | | var a = new SDate(01, "spring"); |
| var b = new SDate(02, "spring"); | | var b = new SDate(02, "spring"); |
| if (a < b) // true | | if (a < b) // true |
| ... | | ... |
− | </source> | + | </syntaxhighlight> |
| | | |
| ...and get a translated date string: | | ...and get a translated date string: |
− | <source lang="c#"> | + | <syntaxhighlight lang="c#"> |
| var date = new SDate(15, "summer"); | | var date = new SDate(15, "summer"); |
| string message = $"See you on {date.ToLocaleString(withYear: false)}!"; // See you on Summer 15! | | string message = $"See you on {date.ToLocaleString(withYear: false)}!"; // See you on Summer 15! |
− | </source> | + | </syntaxhighlight> |
| | | |
− | Note that <tt>SDate</tt> won't let you create invalid dates: | + | Note that <samp>SDate</samp> won't let you create invalid dates: |
− | <source lang="C#"> | + | <syntaxhighlight lang="C#"> |
| // ArgumentException: Invalid day '30', must be a value from 1 to 28. | | // ArgumentException: Invalid day '30', must be a value from 1 to 28. |
| new SDate(30, "spring"); | | new SDate(30, "spring"); |
Line 107: |
Line 154: |
| // ArithmeticException: Adding -1 days to 01 spring Y1 would result in invalid date 28 winter Y0. | | // ArithmeticException: Adding -1 days to 01 spring Y1 would result in invalid date 28 winter Y0. |
| new SDate(01, "spring", 1).AddDays(-1); | | new SDate(01, "spring", 1).AddDays(-1); |
− | </source> | + | </syntaxhighlight> |
| | | |
| Once created, dates have a few properties you can use: | | Once created, dates have a few properties you can use: |
Line 115: |
Line 162: |
| ! meaning | | ! meaning |
| |- | | |- |
− | | <tt>Day</tt> | + | | <samp>Day</samp> |
| | The day of month. | | | The day of month. |
| |- | | |- |
− | | <tt>Season</tt> | + | | <samp>Season</samp> |
| | The normalised season name. | | | The normalised season name. |
| |- | | |- |
− | | <tt>SeasonIndex</tt> | + | | <samp>SeasonIndex</samp> |
− | | The zero-based season index recognised by game methods like <tt>Utility.getSeasonNameFromNumber</tt>. | + | | The zero-based season index recognised by game methods like <samp>Utility.getSeasonNameFromNumber</samp>. |
| |- | | |- |
− | | <tt>Year</tt> | + | | <samp>Year</samp> |
| | The year number. | | | The year number. |
| |- | | |- |
− | | <tt>DayOfWeek</tt> | + | | <samp>DayOfWeek</samp> |
− | | The day of week (like <tt>Monday</tt>). | + | | The day of week (like <samp>Monday</samp>). |
| |- | | |- |
− | | <tt>DaysSinceStart</tt> | + | | <samp>DaysSinceStart</samp> |
− | | The number of days since the first day, inclusively (i.e. 01 spring Y1 = 1). | + | | The number of days since the first day, inclusively (''i.e.,'' 01 spring Y1 = 1). |
| |} | | |} |
| | | |
| ===File paths=== | | ===File paths=== |
− | {{SMAPI upcoming|3.7}}
| + | <samp>PathUtilities</samp> provides utility methods for working with file paths and [[Modding:Modder Guide/APIs/Content|asset names]], complementing the <samp>Path</samp> class provided by .NET: |
− | | |
− | <tt>PathUtilities</tt> provides utility methods for working with file paths and [[Modding:Modder Guide/APIs/Content|asset names]], complementing the <tt>Path</tt> class provided by .NET: | |
| | | |
| {| class="wikitable" | | {| class="wikitable" |
Line 147: |
Line 192: |
| | Split a path into its delimited segments, like <code>/usr/bin/example</code> → <code>usr</code>, <code>bin</code>, and <code>example</code>. For example: | | | Split a path into its delimited segments, like <code>/usr/bin/example</code> → <code>usr</code>, <code>bin</code>, and <code>example</code>. For example: |
| | | |
− | <source lang="c#"> | + | <syntaxhighlight lang="c#"> |
| string[] segments = PathUtilities.GetSegments(Constants.ExecutionPath); | | string[] segments = PathUtilities.GetSegments(Constants.ExecutionPath); |
− | </source> | + | </syntaxhighlight> |
| |- | | |- |
| | <code>IsSafeRelativePath</code> | | | <code>IsSafeRelativePath</code> |
Line 155: |
Line 200: |
| |- | | |- |
| | <code>IsSlug</code> | | | <code>IsSlug</code> |
− | | Get whether a string can be used as a 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, SMAPI IDs, etc). | + | | Get whether a string can be used as a 'slug', containing only basic characters that are safe in all contexts (''e.g.,'' filenames, URLs, SMAPI IDs, etc). |
| |- | | |- |
| | <code>NormalizePath</code> | | | <code>NormalizePath</code> |
| | Normalize file paths or asset names to match the format used by the current OS. For example: | | | Normalize file paths or asset names to match the format used by the current OS. For example: |
| | | |
− | <source lang="c#">string path = PathUtilities.NormalizePathSeparators(@"Characters\Dialogue//Abigail"); | + | <syntaxhighlight lang="c#">string path = PathUtilities.NormalizePathSeparators(@"Characters\Dialogue//Abigail"); |
| // Linux/Mac: "Characters/Dialogue/Abigail" | | // Linux/Mac: "Characters/Dialogue/Abigail" |
| // Windows: "Characters\Dialogue\Abigail" | | // Windows: "Characters\Dialogue\Abigail" |
− | </source> | + | </syntaxhighlight> |
| |} | | |} |
| + | |
| + | ===Per-screen data=== |
| + | SMAPI's <code>PerScreen<T></code> utility manages a separate value for each local screen in split-screen mode. See [[Modding:Modder Guide/APIs/Multiplayer#Per-screen data|<samp>PerScreen<T></samp> in the multiplayer API]] for details. |
| | | |
| ===Semantic versions=== | | ===Semantic versions=== |
− | Use <tt>SemanticVersion</tt> to manipulate and compare versions per the [http://semver.org/ Semantic Versioning 2.0 standard]. Example usage: | + | Use <samp>SemanticVersion</samp> to manipulate and compare versions per the [http://semver.org/ Semantic Versioning 2.0 standard]. Example usage: |
− | <source lang="c#"> | + | <syntaxhighlight lang="c#"> |
| // build version from parts | | // build version from parts |
| ISemanticVersion version = new SemanticVersion(5, 1, 0, "beta"); | | ISemanticVersion version = new SemanticVersion(5, 1, 0, "beta"); |
Line 179: |
Line 227: |
| new SemanticVersion("5.10").IsNewerThan("5.10-beta"); // true | | new SemanticVersion("5.10").IsNewerThan("5.10-beta"); // true |
| new SemanticVersion("5.1").IsBetween("5.0", "5.2"); // true | | new SemanticVersion("5.1").IsBetween("5.0", "5.2"); // true |
− | </source> | + | </syntaxhighlight> |
| | | |
− | Note that game versions before 1.2.0 and some mod versions are non-standard (e.g. Stardew Valley 1.11 comes ''before'' 1.2). All SMAPI versions are standard. | + | Note that game versions before 1.2.0 and some mod versions are non-standard (''e.g.,'' Stardew Valley 1.11 comes ''before'' 1.2). All SMAPI versions are standard. |
| | | |
| ==Input== | | ==Input== |
− | SMAPI's <tt>SButton</tt> constants uniquely represent controller, keyboard, and mouse button presses or clicks. See the [[../Input|Input]] page for more info. | + | SMAPI's <samp>SButton</samp> constants uniquely represent controller, keyboard, and mouse button presses or clicks. See the [[../Input|Input]] page for more info. |