Line 42: |
Line 42: |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
− | Note that <samp>ReadSaveData</samp> will return <samp>null</samp> if the data doesn't exist.</li> | + | Note that <samp>ReadSaveData</samp> will return <samp>null</samp> if the data doesn't exist. Additionally, <samp>ReadSaveData</samp> is scoped to your mod, so you do not need to prepend with your mod's uniqueID</li> |
| </ol> | | </ol> |
| | | |
Line 68: |
Line 68: |
| The ''data model'' is a C# class you create, with properties representing the data you want to store. It can contain almost anything from a few boolean fields to a complex object graph. Here's a simple data model: | | The ''data model'' is a C# class you create, with properties representing the data you want to store. It can contain almost anything from a few boolean fields to a complex object graph. Here's a simple data model: |
| <syntaxhighlight lang="c#"> | | <syntaxhighlight lang="c#"> |
− | class ModData | + | public sealed class ModData |
| { | | { |
| public bool ExampleBoolean { get; set; } | | public bool ExampleBoolean { get; set; } |
Line 82: |
Line 82: |
| } | | } |
| </syntaxhighlight> | | </syntaxhighlight> |
| + | |
| + | Only public properties and fields will be serialized, and your class needs a zero-parameter constructor. SMAPI uses NewtonSoft to serialize these models, so if you need finer-grained control over what gets serialized, use NewtonSoft annotations. |
| | | |
| ===Default values=== | | ===Default values=== |
Line 113: |
Line 115: |
| | | |
| ==Deletion== | | ==Deletion== |
− | To remove an entry or file, just pass <code>null</code> as the data model. This works with any of the <samp>Write*</samp> methods except <samp>WriteJsonFile</samp> ({{SMAPI upcoming|3.13.0|inline=1|fixed}}): | + | To remove an entry or file, just pass <code>null</code> as the data model. This works with any of the <samp>Write*</samp> methods: |
| <syntaxhighlight lang="c#"> | | <syntaxhighlight lang="c#"> |
| // delete entry (if present) | | // delete entry (if present) |
| this.Helper.Data.WriteSaveData<DataModel>("example-key", null); | | this.Helper.Data.WriteSaveData<DataModel>("example-key", null); |
| + | </syntaxhighlight> |
| + | |
| + | ==See also== |
| + | ===Mod data=== |
| + | You can also store custom data for individual game entities which have a <samp>modData</samp> dictionary field. That includes NPCs and players (<samp>Character</samp>), <samp>GameLocation</samp>, <samp>Item</samp>, and <samp>TerrainFeature</samp>. This is persisted to the save file and synchronized in multiplayer. |
| + | |
| + | Usage notes: |
| + | * To avoid mod conflicts, prefixing data fields with your mod ID is strongly recommended (see the example below). |
| + | * When you split an item stack, the new stack copies the previous one's <samp>modData</samp> field; when merged into another stack, the merged items adopt the target stack's mod data. Otherwise mod data has no effect on item split/merge logic (''e.g.,'' you can still merge items with different mod data).</li> |
| + | |
| + | For example, this writes and then reads a custom 'age' value for an item: |
| + | <syntaxhighlight lang="c#"> |
| + | // write a custom value |
| + | item.modData[$"{this.ModManifest.UniqueID}/item-age"] = "30"; |
| + | |
| + | // read it |
| + | if (item.modData.TryGetValue($"{this.ModManifest.UniqueID}/item-age", out string rawAge) && int.TryParse(rawAge, int age)) |
| + | ... |
| </syntaxhighlight> | | </syntaxhighlight> |