> For the complete documentation index, see [llms.txt](https://stoneforgeteam.gitbook.io/stoneforgeteam-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://stoneforgeteam.gitbook.io/stoneforgeteam-docs/getting-started/writing-a-mod.md).

# Writing a mod

A mod is a folder in `Stoneshard\mods`: its `mod.json`, its C# source (`*.cs`, in any subfolders), its pictures and sounds in `Assets\`, its translations in `Localization\` (see [Localization](/stoneforgeteam-docs/core-concepts/localization.md)), its NPC dialogue edits in `Dialogue\` (see [NPC dialogues](/stoneforgeteam-docs/ui/npc-dialogues.md#where-edits-go)), and optionally its GML in `GML\` (see [GML bindings](/stoneforgeteam-docs/gml-and-c-code/gml-bindings.md); GML runs only on the VM modbranch). StoneForge compiles and checks the source when the game starts. A folder holds one mod.

## mod.json

```json
{
  "id": "examplemod",
  "name": "Example Mod",
  "version": "1.0.0",
  "author": "you",
  "description": "What it does, in a sentence or two.",
  "stoneforge": "0.9.4",
  "requires": ["othermod"],
  "after": ["thirdmod"]
}
```

| Key                     |          |                                                                                                                                                                                                                                                   |
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                    | required | The mod's permanent ID: lowercase letters and digits, single underscores between them (`examplemod`, `yourname_examplemod`). Its settings, whether it's switched on, and its content are known by it - don't change it once players have the mod. |
| `name`                  | required | Shown in the Mods window, its log lines and "Mod: ..." on its items.                                                                                                                                                                              |
| `version`               | required | Your mod's version.                                                                                                                                                                                                                               |
| `author`, `description` | optional | Shown in the Mods window.                                                                                                                                                                                                                         |
| `stoneforge`            | optional | The StoneForge version it needs, at least (`"0.9.4"`). An older StoneForge doesn't load it and says so. `"latest"` marks a mod in development: see below.                                                                                         |
| `requires`              | optional | Mods it needs, by ID. It loads after them, only if they're all there and running, and it can use their public types. See [Using other mods](/stoneforgeteam-docs/core-concepts/other-mods.md).                                                    |
| `after`                 | optional | Mods to load after, if they're there.                                                                                                                                                                                                             |
| `github`                | optional | The mod's GitHub repository (`"YourAccount/YourMod"`, or its URL), for [bug reports](/stoneforgeteam-docs/getting-started/bug-reports.md) from the Esc menu.                                                                                      |
| `contributors`          | optional | Steam accounts (as strings: account IDs or SteamID64s) that can use the mod's dev tools, such as the [dialogue editor](/stoneforgeteam-docs/ui/npc-dialogues.md#the-in-game-dialogue-editor). `context.IsContributor` checks the current one.     |
| `trusted`               | optional | `true` asks for full access: its own DLLs, the whole of .NET and no sandbox. It runs only once the player allows it in the Mods window. See [Trusted mods](/stoneforgeteam-docs/core-concepts/mod-context.md#trusted-mods).                       |

### Mods in development: "latest"

While you develop a mod against StoneForge's `main` - between releases, for its newest API - set `"stoneforge": "latest"`. Any StoneForge loads it, the version check is skipped, and the log calls it a development build. In code, `context.Manifest.InDevelopment` says so. When you release the mod, set the version of StoneForge it was built against.

Without a valid `mod.json` the folder isn't loaded; the Mods window says what's wrong. Two folders with the same `id`: the second isn't loaded. Code reads the manifest as `context.Manifest`.

## The mod class

```csharp
using StoneForge;

namespace MyMod;

public class MyMod : IStoneMod
{
    public void Load(ModContext context) => context.Log("Hello from " + context.Name);
    public void Unload() { }
}
```

One public `IStoneMod` class per folder. Its name, version and so on come from `mod.json`.

## The editor project

The game compiles a mod's `.cs` files itself when it starts, so a mod needs no build step. A project file is still worth having: it gives your editor (Visual Studio, Rider, VS Code) autocomplete, the generated game API and errors as you type. Nothing it builds is loaded.

A mod folder, with its project:

```
Stoneshard\mods\MyMod  mod.json
  MyMod.csproj
  MyMod.cs
  Items\...
  Assets\         pictures and sounds (icon.png for the Mods window)
  GML\            optional: GML functions of your own
```

`MyMod.csproj`, as the [Example Mod](https://github.com/StoneForgeTeam/ExampleMod)'s:

```xml
<Project Sdk="Microsoft.NET.Sdk">

  <!-- For editing in an IDE only: the game compiles this folder's .cs files itself when it starts (and checks
       them - only what's safe for a mod is allowed). Nothing built here is loaded. -->
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <!-- As the game compiles mods: no implicit usings, C# 12. -->
    <ImplicitUsings>disable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <LangVersion>12</LangVersion>
    <PlatformTarget>x64</PlatformTarget>
    <!-- StoneForge as installed in the game: STONESHARD_DIR, else the game folder this mod is in. -->
    <StoneForgeSdkDir Condition="'$(StoneForgeSdkDir)' == '' and '$(STONESHARD_DIR)' != ''">$(STONESHARD_DIR)\dotnet</StoneForgeSdkDir>
    <StoneForgeSdkDir Condition="'$(StoneForgeSdkDir)' == ''">$(MSBuildThisFileDirectory)..\..\dotnet</StoneForgeSdkDir>
  </PropertyGroup>

  <ItemGroup>
    <!-- StoneForge.API as installed in the game: what the game compiles mods against. -->
    <Reference Include="StoneForge.API">
      <HintPath>$(StoneForgeSdkDir)\StoneForge.API.dll</HintPath>
      <Private>false</Private>
    </Reference>
    <!-- GML functions of your own (the GML folder) as C#: the generator makes MyMod.Gml from them, as the game does. -->
    <Analyzer Include="$(StoneForgeSdkDir)\StoneForge.GmlGenerator.dll" />
    <AdditionalFiles Include="GML\**\*.gml" />
  </ItemGroup>

  <Target Name="ValidateStoneForgeSdk" BeforeTargets="ResolveReferences">
    <Error Condition="!Exists('$(StoneForgeSdkDir)\StoneForge.API.dll') or !Exists('$(StoneForgeSdkDir)\StoneForge.GmlGenerator.dll')"
           Text="Install StoneForge and set STONESHARD_DIR to the game folder, or pass -p:StoneForgeSdkDir=path-to-its-dotnet-folder." />
  </Target>

</Project>
```

It finds StoneForge in one of three ways:

* **Inside the game:** a mod in `<Stoneshard>\mods\MyMod` finds `<Stoneshard>\dotnet` on its own.
* **Elsewhere** (a Git checkout, say): set `STONESHARD_DIR` to the game folder before opening your editor:

  ```powershell
  $env:STONESHARD_DIR = 'C:\Program Files (x86)\Steam\steamapps\common\Stoneshard'
  dotnet build MyMod.csproj
  ```
* **Or** pass `-p:StoneForgeSdkDir="C:\path\to\Stoneshard\dotnet"`.

The project needs the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0). `ImplicitUsings` and `LangVersion` match how the game compiles mods, so code that builds here builds in the game - though the game's sandbox can still refuse what isn't allowed for mods (files, threads, reflection...), which the editor doesn't check. The folder's name decides the generated GML class: `MyMod.Gml` (see [Your own GML](/stoneforgeteam-docs/gml-and-c-code/gml-bindings.md)).

When copying a mod into the game from elsewhere, leave out `bin`, `obj`, `.git` and `.vs`.

## Content IDs

Items, consumables, buffs and skills are created with a short key, and StoneForge puts the mod's ID in front of it, so two mods can use the same key without clashing:

```csharp
public class Tonic : Wine
{
    public Tonic() : base("tonic") { DisplayName = "Example Tonic"; }
}
// context.Items.Add(tonic): tonic.Id is "examplemod:tonic"
```

|                    |                                                                                                                                                |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `Key`              | `"tonic"`: what you wrote. Keys can't start with `_` or contain `:`.                                                                           |
| `Id`               | `"examplemod:tonic"`: how code refers to it, including other mods (`Items.Give("examplemod:Example Blade")`, `RequireSkill("othermod:bolt")`). |
| in the game's data | `examplemod__tonic`: its objects (`o_inv_examplemod__tonic`), table rows and saves.                                                            |

A mod's ID has no `__` and keys don't start with `_`, so no two mods' content can ever get the same name in the game. Keys (and the mod's ID) are what saves refer to: changing them loses that content from existing saves.

## Next steps

* [The mod context](/stoneforgeteam-docs/core-concepts/mod-context.md): loading, ticking, logging, pictures, files.
* [How GML maps to C# code](/stoneforgeteam-docs/gml-and-c-code/overview.md): how StoneForge's API reaches into the game.
* [Items](/stoneforgeteam-docs/content/items.md), [buffs](/stoneforgeteam-docs/content/buffs.md) and [skills](/stoneforgeteam-docs/content/skills.md): content of your own.
* [Screens and elements](/stoneforgeteam-docs/ui/screens-and-elements.md): UI of your own.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://stoneforgeteam.gitbook.io/stoneforgeteam-docs/getting-started/writing-a-mod.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
