Difference between revisions of "Modding:Modder Guide/APIs/Content Packs"

From Stardew Valley Wiki
Jump to navigation Jump to search
(+ footer nav links)
m (update code example how to asset from content pack)
 
(15 intermediate revisions by 4 users not shown)
Line 5: Line 5:
 
__TOC__
 
__TOC__
 
==Content pack format==
 
==Content pack format==
A content pack is a folder containing a <tt>manifest.json</tt> file which specifies your mod in its <tt>ContentPackFor</tt> field (see [[../Manifest|manifest]]). The format beyond that is up to you to define. For example, {{nexus mod|1915|Content Patcher}} requires a <tt>content.json</tt> file, but that's not validated by SMAPI itself. Instead, SMAPI provides an API you can use to read any other file you need from the content pack.
+
A content pack is a folder containing a <samp>manifest.json</samp> file which specifies your mod in its <samp>ContentPackFor</samp> field (see [[../Manifest|manifest]]). The format beyond that is up to you to define. For example, {{nexus mod|1915|Content Patcher}} requires a <samp>content.json</samp> file, but that's not validated by SMAPI itself. Instead, SMAPI provides an API you can use to read any other file you need from the content pack.
  
 
See [[Modding:Content packs]] for more info about content packs.
 
See [[Modding:Content packs]] for more info about content packs.
  
 
==Content pack API==
 
==Content pack API==
You can get a list of content packs installed for your mod, and access the files within it:
+
===Get owned content packs===
<source lang="c#">
+
To get the content packs installed for your mod (including a <samp>Manifest</samp> field with the content pack's [[Modding:Modder Guide/APIs/Manifest|full manifest info]]):
foreach(IContentPack contentPack in this.Helper.GetContentPacks())
+
<syntaxhighlight lang="c#">
 +
foreach(IContentPack contentPack in this.Helper.ContentPacks.GetOwned())
 
{
 
{
 
   this.Monitor.Log($"Reading content pack: {contentPack.Manifest.Name} {contentPack.Manifest.Version} from {contentPack.DirectoryPath}");
 
   this.Monitor.Log($"Reading content pack: {contentPack.Manifest.Name} {contentPack.Manifest.Version} from {contentPack.DirectoryPath}");
 +
}
 +
</syntaxhighlight>
 +
 +
Content packs are listed in load order, so they're already sorted for dependencies. For example, if content pack B requires A, they'll be listed in the order A → B.
 +
 +
As a note, this can be done as early as ModEntry, although mods often choose to perform this step in the GameLoad or SaveLoaded event.
  
    // read a JSON file
+
===Check if a file exists===
    YourDataFile data = contentPack.ReadJsonFile<YourDataFile>("content.json");
+
You can check if a file exists in the content pack folder using <samp>HasFile</samp>. You can specify a relative path like <samp>data/content.json</samp>.
  
    // load an asset or image
+
<syntaxhighlight lang="c#">
    Texture2D image = contentPack.LoadAsset<Texture2D>("image.png");
+
if (!contentPack.HasFile("content.json"))
 +
{
 +
  // show 'required file missing' error
 
}
 
}
</source>
+
</syntaxhighlight>
  
If you need to pass a content pack asset name to game code, you can use its <tt>GetActualAssetKey</tt> method:
+
It is often a good idea to alert the player (and modders who may be using your mod) if the file is missing on deployment.
<source lang="c#">
+
 
 +
===Read a JSON file===
 +
You can read a JSON file from a content pack folder into [[Modding:Modder Guide/APIs/Data#Data model|a data model]] (<samp>YourDataModel</samp> in the example below) using <samp>ReadJsonFile</samp>. You can specify a relative path like <samp>data/content.json</samp>. This will return <samp>null</samp> if the file doesn't exist.
 +
 
 +
<syntaxhighlight lang="c#">
 +
YourDataModel data = contentPack.ReadJsonFile<YourDataFile>("content.json");
 +
</syntaxhighlight>
 +
 
 +
===Write a JSON file===
 +
You can write a JSON file in a content pack folder using <samp>WriteJsonFile</samp>, using a [[Modding:Modder Guide/APIs/Data#Data model|data model]] (<samp>YourDataModel</samp> in the example below). You can specify a relative path like <samp>data/content.json</samp> (subdirectories will be created automatically if needed). This will create the file if it doesn't exist, or overwrite it if it does.
 +
 
 +
'''Note:''' this is best used for generated files. Overwriting the original files isn't recommended, since a bug in your mod which changes an original file might permanently break the content pack and require the player to reinstall it.
 +
 
 +
<syntaxhighlight lang="c#">
 +
YourDataModel data = new YourDataModel();
 +
contentPack.WriteJsonFile("content.json", data);
 +
</syntaxhighlight>
 +
 
 +
===Read content asset===
 +
You can read [[Modding:Modder Guide/APIs/Content|a content asset]] from the content pack folder using <samp>LoadAsset</samp>. You can optionally specify a relative path to read from a subfolder.
 +
<syntaxhighlight lang="c#">
 +
Texture2D image = contentPack.ModContent.Load<Texture2D>("image.png");
 +
</syntaxhighlight>
 +
 
 +
If you need to pass an asset name to game code, you can use the <samp>GetActualAssetKey</samp> method:
 +
<syntaxhighlight lang="c#">
 
tilesheet.ImageSource = contentPack.GetActualAssetKey("image.png");
 
tilesheet.ImageSource = contentPack.GetActualAssetKey("image.png");
</source>
+
</syntaxhighlight>
 +
 
 +
===Get translations===
 +
You can read translations from the content pack's <samp>i18n</samp> folder. This is identical to the [[Modding:Modder Guide/APIs/Translation|translation API]], but accessed through <samp>contentPack.Translations</samp>:
 +
<syntaxhighlight lang="c#">
 +
string text = contentPack.Translation.Get("item-type.label");
 +
</syntaxhighlight>
 +
 
 +
===Create a fake content pack===
 +
In specialised cases, you can create a temporary content pack for a given directory path:
 +
<syntaxhighlight lang="c#">
 +
// create with random manifest data
 +
IContentPack contentPack = this.Helper.ContentPacks.CreateFake(
 +
  directoryPath: Path.Combine(this.Helper.DirectoryPath, "content-pack"),
 +
);
  
{{modding guide footer
+
// create with given manifest fields
|prev = [[Modding:Modder Guide/APIs|SMAPI reference]]
+
IContentPack contentPack = this.Helper.ContentPacks.CreateTemporary(
|next =
+
  directoryPath: Path.Combine(this.Helper.DirectoryPath, "content-pack"),
}}
+
  id: Guid.NewGuid().ToString("N"),
 +
  name: "temporary content pack",
 +
  description: "...",
 +
  author: "...",
 +
  version: new SemanticVersion(1, 0, 0)
 +
);
 +
</syntaxhighlight>

Latest revision as of 16:37, 12 September 2022

Creating SMAPI mods SMAPI mascot.png


Modding:Index

A content pack is a special type of mod that isn't run by SMAPI directly, but contains files your own mod can read.

Content pack format

A content pack is a folder containing a manifest.json file which specifies your mod in its ContentPackFor field (see manifest). The format beyond that is up to you to define. For example, Content Patcher requires a content.json file, but that's not validated by SMAPI itself. Instead, SMAPI provides an API you can use to read any other file you need from the content pack.

See Modding:Content packs for more info about content packs.

Content pack API

Get owned content packs

To get the content packs installed for your mod (including a Manifest field with the content pack's full manifest info):

foreach(IContentPack contentPack in this.Helper.ContentPacks.GetOwned())
{
   this.Monitor.Log($"Reading content pack: {contentPack.Manifest.Name} {contentPack.Manifest.Version} from {contentPack.DirectoryPath}");
}

Content packs are listed in load order, so they're already sorted for dependencies. For example, if content pack B requires A, they'll be listed in the order A → B.

As a note, this can be done as early as ModEntry, although mods often choose to perform this step in the GameLoad or SaveLoaded event.

Check if a file exists

You can check if a file exists in the content pack folder using HasFile. You can specify a relative path like data/content.json.

if (!contentPack.HasFile("content.json"))
{
   // show 'required file missing' error
}

It is often a good idea to alert the player (and modders who may be using your mod) if the file is missing on deployment.

Read a JSON file

You can read a JSON file from a content pack folder into a data model (YourDataModel in the example below) using ReadJsonFile. You can specify a relative path like data/content.json. This will return null if the file doesn't exist.

YourDataModel data = contentPack.ReadJsonFile<YourDataFile>("content.json");

Write a JSON file

You can write a JSON file in a content pack folder using WriteJsonFile, using a data model (YourDataModel in the example below). You can specify a relative path like data/content.json (subdirectories will be created automatically if needed). This will create the file if it doesn't exist, or overwrite it if it does.

Note: this is best used for generated files. Overwriting the original files isn't recommended, since a bug in your mod which changes an original file might permanently break the content pack and require the player to reinstall it.

YourDataModel data = new YourDataModel();
contentPack.WriteJsonFile("content.json", data);

Read content asset

You can read a content asset from the content pack folder using LoadAsset. You can optionally specify a relative path to read from a subfolder.

Texture2D image = contentPack.ModContent.Load<Texture2D>("image.png");

If you need to pass an asset name to game code, you can use the GetActualAssetKey method:

tilesheet.ImageSource = contentPack.GetActualAssetKey("image.png");

Get translations

You can read translations from the content pack's i18n folder. This is identical to the translation API, but accessed through contentPack.Translations:

string text = contentPack.Translation.Get("item-type.label");

Create a fake content pack

In specialised cases, you can create a temporary content pack for a given directory path:

// create with random manifest data
IContentPack contentPack = this.Helper.ContentPacks.CreateFake(
   directoryPath: Path.Combine(this.Helper.DirectoryPath, "content-pack"),
);

// create with given manifest fields
IContentPack contentPack = this.Helper.ContentPacks.CreateTemporary(
   directoryPath: Path.Combine(this.Helper.DirectoryPath, "content-pack"),
   id: Guid.NewGuid().ToString("N"),
   name: "temporary content pack",
   description: "...",
   author: "...",
   version: new SemanticVersion(1, 0, 0)
);