Difference between revisions of "Modding:Modder Guide/APIs/Utilities"

From Stardew Valley Wiki
Jump to navigation Jump to search
(→‎Per-screen data: move content to multiplayer API page)
m (Replace deprecated <source> tags with <syntaxhighlight> tags)
Line 10: Line 10:
  
 
You don't need to worry about that when using SMAPI APIs, which take relative paths and automatically fix the format if needed:
 
You don't need to worry about that when using SMAPI APIs, which take relative paths and automatically fix the format if needed:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
var data = this.Helper.Data.ReadJsonFile<SomeDataModel>("assets/data.json");
 
var data = this.Helper.Data.ReadJsonFile<SomeDataModel>("assets/data.json");
</source>
+
</syntaxhighlight>
  
 
If you really need a full path, you should use <tt>this.Helper.DirectoryPath</tt> and <tt>Path.Combine</tt> to get it:
 
If you really need a full path, you should use <tt>this.Helper.DirectoryPath</tt> and <tt>Path.Combine</tt> to get it:
<source lang="c#">
+
<syntaxhighlight lang="c#">
 
string path = Path.Combine(this.Helper.DirectoryPath, "assets", "data.json"); // "assets/data.json" in the current mod's folder
 
string path = Path.Combine(this.Helper.DirectoryPath, "assets", "data.json"); // "assets/data.json" in the current mod's folder
 
var file = new FileInfo(path);
 
var file = new FileInfo(path);
</source>
+
</syntaxhighlight>
  
 
See ''[[#Constants|Constants]]'' for other paths like the game folder.
 
See ''[[#Constants|Constants]]'' for other paths like the game folder.
Line 88: Line 88:
 
===Dates===
 
===Dates===
 
Use <tt>SDate</tt> for calculating in-game dates. You start by creating a date:
 
Use <tt>SDate</tt> 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 102: Line 102:
 
// 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 <tt>SDate</tt> 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 125: Line 125:
 
// 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 163: Line 163:
 
| Split a path into its delimited segments, like <code>/usr/bin/example</code> &rarr; <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> &rarr; <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 176: Line 176:
 
| 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>
 
|}
 
|}
  
Line 187: Line 187:
 
===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 <tt>SemanticVersion</tt> 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 198: Line 198:
 
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.

Revision as of 18:28, 19 February 2021

Creating SMAPI mods SMAPI mascot.png


Modding:Index

SMAPI provides some C# objects you can use to simplify your code.

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 Mods/Your Mod Name 1.27.5-541-1-27-5-1598664794/YourModFolder by default, and players can organize their mod folders like Mods/For single-player/YourModFolder.
  • 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:

var data = this.Helper.Data.ReadJsonFile<SomeDataModel>("assets/data.json");

If you really need a full path, you should use this.Helper.DirectoryPath and Path.Combine to get it:

string path = Path.Combine(this.Helper.DirectoryPath, "assets", "data.json"); // "assets/data.json" in the current mod's folder
var file = new FileInfo(path);

See Constants for other paths like the game folder.

Constants

The Constants class provides metadata about SMAPI and the game.

value meaning
Constants.ApiVersion The version of the running SMAPI instance.
Constants.MinimumGameVersion
Constants.MaximumGameVersion
The game versions supported by the running SMAPI instance.
Constants.TargetPlatform The current operating system (one of Android, Linux, Mac, or Windows).
Constants.ExecutionPath The absolute path to the Stardew Valley folder.
Constants.DataPath The absolute path to the game's data folder (which contains the save folder).
Constants.LogDir The absolute path to the folder containing the game and SMAPI logs.
Constants.SavesPath The absolute path to the save folder.
Constants.CurrentSavePath The absolute path to the current save folder, if a save is loaded.
Constants.SaveFolderName The name of the current save folder (like Name_012345789), if a save is loaded.

Context

The Context class provides information about the game state and player control:

value meaning
Context.IsGameLaunched Whether the game has been launched and initialised. This becomes true immediately before the first update tick.
Context.IsWorldReady Whether the player has loaded a save and the world has finished initialising. Useful for ignoring events before the save is loaded.
Context.IsPlayerFree Whether Context.IsWorldReady and the player is free to act on the world (no menu is displayed, no cutscene is in progress, etc).
Context.CanPlayerMove Whether Context.IsPlayerFree and the player is free to move (e.g. not using a tool).
Context.IsMultiplayer Whether Context.IsWorldReady, and the player loaded the save in multiplayer mode (regardless of whether any other players are connected).
Context.IsMainPlayer Whether Context.IsWorldReady, and the player is the main player. This is always true in single-player, and true when hosting in multiplayer.

Helpers

Dates

Use SDate for calculating in-game dates. You start by creating a date:

var date = SDate.Now(); // current date
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 = SDate.From(Game1.Date); // from a game date

Then you can calculate offsets from any date:

// add days
new SDate(28, "spring", 1).AddDays(370); // 06 fall in year 4

// subtract days
new SDate(01, "spring", 2).AddDays(-1); // 28 winter in year 1

...and compare dates:

var a = new SDate(01, "spring");
var b = new SDate(02, "spring");
if (a < b) // true
  ...

...and get a translated date string:

var date = new SDate(15, "summer");
string message = $"See you on {date.ToLocaleString(withYear: false)}!"; // See you on Summer 15!

Note that SDate won't let you create invalid dates:

// ArgumentException: Invalid day '30', must be a value from 1 to 28.
new SDate(30, "spring");

// ArithmeticException: Adding -1 days to 01 spring Y1 would result in invalid date 28 winter Y0.
new SDate(01, "spring", 1).AddDays(-1);

Once created, dates have a few properties you can use:

property meaning
Day The day of month.
Season The normalised season name.
SeasonIndex The zero-based season index recognised by game methods like Utility.getSeasonNameFromNumber.
Year The year number.
DayOfWeek The day of week (like Monday).
DaysSinceStart The number of days since the first day, inclusively (i.e. 01 spring Y1 = 1).

File paths

PathUtilities provides utility methods for working with file paths and asset names, complementing the Path class provided by .NET:

method usage
GetSegments Split a path into its delimited segments, like /usr/bin/exampleusr, bin, and example. For example:
string[] segments = PathUtilities.GetSegments(Constants.ExecutionPath);
IsSafeRelativePath Get whether a path is relative and doesn't contain directory climbing (../), so it's guaranteed to be within the parent folder.
IsSlug 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).
NormalizePath Normalize file paths or asset names to match the format used by the current OS. For example:
string path = PathUtilities.NormalizePathSeparators(@"Characters\Dialogue//Abigail");
// Linux/Mac: "Characters/Dialogue/Abigail"
// Windows: "Characters\Dialogue\Abigail"

Per-screen data

SMAPI's PerScreen<T> utility manages a separate value for each local screen in split-screen mode. See PerScreen<T> in the multiplayer API for details.

Semantic versions

Use SemanticVersion to manipulate and compare versions per the Semantic Versioning 2.0 standard. Example usage:

// build version from parts
ISemanticVersion version = new SemanticVersion(5, 1, 0, "beta");

// build version from string
ISemanticVersion version = new SemanticVersion("5.1.0-beta");

// compare versions (also works with SemanticVersion instances instead of strings)
new SemanticVersion("5.2").IsOlderThan("5.10"); // true
new SemanticVersion("5.10").IsNewerThan("5.10-beta"); // true
new SemanticVersion("5.1").IsBetween("5.0", "5.2"); // true

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

SMAPI's SButton constants uniquely represent controller, keyboard, and mouse button presses or clicks. See the Input page for more info.