Skip to content

Plugin Development Tutorial

3cxc edited this page Sep 12, 2026 · 2 revisions

Plugin Development Tutorial

This tutorial walks you through creating a minimal plugin for Unknown: Site-0 using NativePluginKit.


1. Create the Project

dotnet new classlib -n HelloWorldPlugin -f net8.0
cd HelloWorldPlugin

Edit HelloWorldPlugin.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <PublishAot>true</PublishAot>
    <NativeLib>Shared</NativeLib>
    <IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    <InvariantGlobalization>true</InvariantGlobalization>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\NativePluginKit\NativePluginKit.csproj" />
  </ItemGroup>
</Project>

2. Write the Plugin

Create HelloWorldPlugin.cs:

using NativePluginKit.Features.Logger;
using NativePluginKit.Loader.Plugins;

namespace HelloWorldPlugin;

public partial class HelloWorldPlugin : Plugin
{
    public override string Name        => "HelloWorldPlugin";
    public override string Description => "A simple hello world plugin.";
    public override string Author      => "Your Name";

    public override Version RequiredApiVersion => new(1, 0, 0);

    protected override void OnStart()
    {
        Log.PrintLog("Hello, World!");
    }

    public override void OnStop()
    {
        // Clean up resources here.
    }
}

Note — The class must be declared partial. A source generator emits the required OnInit, OnStop export, and [ModuleInitializer] bootstrap for you.


3. Use Host APIs

Logging

Log.PrintLog("Something happened.");

Scene Switching

Transition.TransitionToScene("res://scenes/menu.tscn", fadeOut: true);

Accessing the Raw API Table

If you need direct access to HostApiTable:

HostApiTable api = Api;

4. Build & Deploy

dotnet publish -c Release -r win-x64

Copy bin/Release/net8.0/win-x64/publish/HelloWorldPlugin.dll into the host's plugins/ directory:

<GameRoot>/
├── ExampleClient.exe
└── plugins/
    └── HelloWorldPlugin.dll

Launch the client. You should see:

Loading plugin: HelloWorldPlugin v1.0.0.0 by Your Name
Hello, World!

5. Lifecycle

Stage Method Description
Load (source-generated OnInit) Called once when the plugin DLL is loaded. Registers metadata and creates the plugin instance.
Start OnStart() Called immediately after OnInit, after Api is populated.
Stop OnStop() Called by the host during unload. Release unmanaged resources here.

6. Best Practices

  • Do not throw from OnStart / OnStop — unhandled exceptions may cross the ABI boundary and crash the host.
  • Keep the plugin DLL self-contained. Only depend on NativePluginKit and the BCL.
  • Match the host API version. If the host reports 1.0, declare RequiredApiVersion => new Version(1, 0).
  • Use Log.PrintLog for diagnostics instead of Console.WriteLine, so logs appear in the host's output.
  • Free unmanaged memory allocated in OnStart before returning from OnStop.

Clone this wiki locally