Modding:Modder Guide/APIs/Utilities

From Stardew Valley Wiki
Jump to navigation Jump to search

Creating SMAPI mods SMAPI mascot.png


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


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.


The Constants class provides metadata about SMAPI and the game.

value meaning
Constants.ApiVersion The version of the running SMAPI instance.
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.


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

Game/player state:
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.IsInDrawLoop Whether the game is currently running the draw loop. This isn't relevant to most mods, since you should use display events to draw to the screen.
value meaning
Context.IsMultiplayer Whether Context.IsWorldReady, and the world was loaded in multiplayer mode (regardless of whether any other players are connected) or is currently in split-screen mode.
Context.IsSplitScreen Whether Context.IsMultiplayer and the current player is in split-screen mode. This doesn't apply for remote players.
Context.HasRemotePlayers Whether Context.IsMultiplayer and any players are connected over the network.
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.
Context.IsOnHostComputer Whether the current player is on the host computer. This is true when Context.IsMainPlayer, or for farmhands in split-screen mode.
Context.ScreenId 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).



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.


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