Create Your First Plugin
This guide walks through the shortest path to a working C# plugin for Moongate.
You will:
- install the plugin template
- scaffold a new plugin project
- add one simple command
- package the plugin into a runtime-ready folder and zip
- copy it into the server plugin directory
If you want the architecture and lifecycle details first, read Plugin System.
Prerequisites
Before you start, make sure you have:
- .NET SDK 10 or later
- access to the Moongate NuGet packages
- a local Moongate runtime directory
1. Install the Template
Install the template package:
dotnet new install Moongate.Templates::<version>
You only need to do this once per SDK environment. When a newer version is published, install that version again to update the template.
2. Generate a New Plugin
Create a new plugin project:
dotnet new moongate-plugin \
--name HelloPlugin \
--pluginId hello-plugin \
--authors "Squid" \
--description "Example plugin"
This creates a folder like:
HelloPlugin/
HelloPlugin.csproj
Plugin.cs
manifest.json
README.md
data/
scripts/
assets/
If your plugin needs custom persisted entities, start with the persistence option enabled:
dotnet new moongate-plugin \
--name HelloPlugin \
--pluginId hello-plugin \
--authors "Squid" \
--description "Example plugin" \
--withPersistence true
3. Inspect the Generated Files
The generated Plugin.cs already implements IMoongatePlugin.
The generated manifest.json already matches:
- the plugin id
- the plugin name
- the entry assembly path
- the entry type
That means you can start adding behavior immediately without building the plugin structure by hand.
4. Add a Minimal Command
Open Plugin.cs and register one command during Configure(...).
Example:
using Moongate.Plugin.Abstractions.Interfaces;
using Moongate.Server.Abstractions.Attributes;
using Moongate.Server.Abstractions.Data.Internal.Commands;
using Moongate.Server.Abstractions.Interfaces.Services.Console;
namespace HelloPlugin;
public sealed class Plugin : IMoongatePlugin
{
public string Id => "hello-plugin";
public string Name => "HelloPlugin";
public string Version => "1.0.0";
public IReadOnlyList<string> Authors => ["Squid"];
public string? Description => "Example plugin";
public void Configure(IMoongatePluginContext context)
{
context.RegisterConsoleCommand<HelloPluginCommand>();
}
public Task InitializeAsync(
IMoongatePluginRuntimeContext context,
CancellationToken cancellationToken
)
{
return Task.CompletedTask;
}
}
[RegisterConsoleCommand("hello_plugin", "Prints a message from the plugin.")]
public sealed class HelloPluginCommand : ICommandExecutor
{
public Task ExecuteCommandAsync(CommandSystemContext context)
{
context.Print("Hello from the plugin.");
return Task.CompletedTask;
}
}
Build the project:
dotnet build
5. Package the Plugin
The template generates packaging scripts for you.
On macOS or Linux:
bash scripts/pack-plugin.sh
On PowerShell:
pwsh ./scripts/pack-plugin.ps1
This produces:
artifacts/hello-plugin/artifacts/hello-plugin.zip
The runtime-ready folder will look like:
artifacts/hello-plugin/
manifest.json
bin/
HelloPlugin.dll
HelloPlugin.deps.json
...
data/
scripts/
assets/
6. Install the Plugin into Moongate
Copy the packaged folder into the runtime plugin directory:
moongate_data/plugins/hello-plugin/
The final runtime layout should look like:
moongate_data/plugins/hello-plugin/
manifest.json
bin/
HelloPlugin.dll
HelloPlugin.deps.json
...
If your runtime root is not the repository moongate_data/ folder, use the equivalent
plugins/<plugin-id>/ directory under your configured runtime root.
7. Start the Server
Start Moongate normally. The plugin loader will:
- discover
manifest.json - resolve plugin dependencies by id
- load the entry assembly
- run
Configure(...) - finish bootstrap
- run
InitializeAsync(...)
If the plugin loads correctly, your command becomes available at startup.
Common Problems
Package version mismatch
Use the same version across the whole Moongate package chain:
Moongate.Plugin.AbstractionsMoongate.Server.AbstractionsMoongate.PersistenceMoongate.UO.Data
Manifest and plugin class disagree
Keep these values aligned:
manifest.json.idandPlugin.Idmanifest.json.nameandPlugin.Namemanifest.json.versionandPlugin.Versionmanifest.json.entryTypeand the fully-qualified plugin class name
Plugin builds but does not load
Check the runtime folder layout first. The most common issue is copying only the DLL and not the
whole plugin folder with manifest.json and bin/.
Next Steps
After the first plugin is working, move on to:
- Create Your First C# Admin Command for a focused command tutorial on top of the plugin path
- Plugin System for lifecycle and dependency model details
- Custom Persisted Entities if the plugin stores its own entities
- Console Commands for command design patterns