Table of Contents

Write Your First Plugin

This tutorial builds a plugin that shows a notification and writes a line to its log each time a save finishes loading.

Requirements

To build:

  • The .NET 10 SDK.

To run:

SkyrimForge is not yet published. Package.ps1 in the SkyrimForge repository builds both parts a plugin author needs:

  • artifacts\SkyrimForge-<version>.zip, which installs SkyrimForge: extract it into the game's Data folder, or install it with a mod manager.
  • artifacts\packages\SkyrimForge.<version>.nupkg, the NuGet package a plugin references.

Create the Project

A plugin is a .NET 10 class library that references the SkyrimForge NuGet package. Create a folder named GreetingPlugin containing GreetingPlugin.csproj:

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

  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="SkyrimForge" Version="0.1.0" />
  </ItemGroup>

</Project>

NuGet finds the package through a package source. Add a nuget.config beside the project file that names Package.ps1's output folder, artifacts\packages:

<configuration>
  <packageSources>
    <add key="SkyrimForge" value="C:\path\to\SkyrimForge\artifacts\packages" />
  </packageSources>
</configuration>

Write the Plugin Class

A plugin class implements IPlugin. Its one public constructor declares the services it uses, and the framework supplies them when it constructs the plugin. Services lists every one. Start() runs once every plugin has been constructed.

This plugin uses three services:

  • IGameEvents, to run code when a save finishes loading.
  • IDebug, to show the notification.
  • ILog, to write to the plugin's own log file.

Add GreetingPlugin.cs:

namespace SkyrimForge.ExamplePlugins;

/// <summary>Shows a notification and writes a line to its log each time a save finishes loading.</summary>
/// <remarks>The plugin the documentation's first tutorial builds.</remarks>
public sealed class GreetingPlugin(IGameEvents events, IDebug debug, ILog log) : IPlugin
{
    private readonly IGameEvents events = events;
    private readonly IDebug debug = debug;
    private readonly ILog log = log;

    /// <inheritdoc/>
    public void Start()
    {
        events.OnSaveLoaded.Subscribe(_ =>
        {
            debug.Notification("Hello from GreetingPlugin.");
            log.Write("A save finished loading.");
        });
        log.Write("GreetingPlugin started.");
    }
}

That file is the example in the SkyrimForge repository. Change its namespace to GreetingPlugin, and add using SkyrimForge; at the top, which the example omits because its namespace lies inside SkyrimForge.

Build and Install

dotnet build -c Release

Copy the contents of bin\Release\net10.0 into SKSE\Plugins\SkyrimForge\Plugins, under the game's Data folder or a mod folder. SkyrimForge loads every plugin class in the DLLs there when the game starts.

Run It

Start the game through SKSE and load a save. The notification "Hello from GreetingPlugin." appears at the top left.

The plugin's log is SKSE\Plugins\SkyrimForge\Plugins\GreetingPlugin.GreetingPlugin.log, named for the full name of the plugin class. Mod Organizer 2 writes it to its overwrite folder. The log contains "GreetingPlugin started." and, after each load, "A save finished loading."

A plugin that cannot be loaded is reported in a message box at the main menu, naming the plugin and the reason.

Next Steps