RocketMod and OpenMod Plugin Basics
Unturned™ dedicated servers support two distinct plugin frameworks: RocketMod, the legacy system that has powered community servers since 2014, and OpenMod, the modern replacement that introduced NuGet-based packaging, dependency injection, and a plugin marketplace. Understanding both frameworks is necessary for any server operator who manages an existing RocketMod plugin library or for any developer who is writing new plugins and needs to choose which framework to target.
This article covers both frameworks from first principles. It explains the plugin lifecycle, event subscription model, and permission system for each framework, then walks through a complete hello-world plugin for each. The article closes with a structured comparison and a migration-consideration checklist for operators who are evaluating whether to move an existing RocketMod server to OpenMod.
57 Studios™ maintains a suite of server-side plugins for the Horizon Life RP community. The patterns documented here are drawn from production plugin authoring experience, not from documentation alone.

Prerequisites
- A working Unturned™ dedicated server. See Self-Hosting for installation guides.
- .NET 6.0 SDK or later (for OpenMod plugins). .NET Framework 4.7.2 (for RocketMod plugins on older hosts).
- Visual Studio 2022 or JetBrains Rider with C# support.
- Familiarity with C# basics: classes, interfaces, methods, attributes.
- Access to the server's file system (local or via SFTP/panel).
What you'll learn
- The architectural difference between RocketMod and OpenMod and when each is appropriate.
- How plugins are loaded, reloaded, and unloaded in each framework.
- How to subscribe to Unturned and framework events from plugin code.
- How the permission system works in each framework and how to enforce permissions in commands.
- The NuGet package structure for OpenMod plugins and how to declare dependencies.
- How to write, build, and deploy a hello-world plugin for RocketMod and for OpenMod.
- What to evaluate when deciding whether to migrate a RocketMod plugin to OpenMod.
Framework overview
RocketMod
RocketMod is the plugin framework that has been active since Unturned™ gained dedicated server support. It exposes Unturned's C# API through a set of convenience abstractions — the RocketPlugin base class, IRocketCommand interface, and RocketPermissionsManager — and manages plugin load order through a folder-based discovery system. RocketMod plugins target .NET Framework and compile to .dll files that are dropped into the Rocket/Plugins/ directory.
RocketMod is no longer actively developed. Security patches and compatibility fixes for new Unturned™ versions are maintained by the community, but the framework's feature set is fixed. For new server projects, OpenMod is the recommended target. For existing RocketMod deployments with a large plugin library, RocketMod remains operational and stable.
OpenMod
OpenMod is the successor framework. It was designed from the ground up to address the structural limitations of RocketMod: dependency management through NuGet, a proper IoC container (Autofac), structured logging (Microsoft.Extensions.Logging), and a plugin marketplace (openmod.io) that handles versioning and distribution. OpenMod plugins target .NET 6.0 or later and are distributed as NuGet packages.
OpenMod runs on top of Unturned™ through its own loader, which is installed separately from RocketMod. Both frameworks can coexist on a single server during a migration period, but running both simultaneously adds overhead and occasional namespace conflicts. The cohort recommendation is to run one framework per server in production.
RocketMod deep dive
Installation
Download source
The RocketMod module files are distributed by the community, not by Smartly Dressed Games directly. Download from the RocketMod GitHub release page and verify the release date matches your Unturned version before installing.
RocketMod is installed by placing the Rocket.Unturned.dll loader and its dependencies into the server's Modules/Rocket.Unturned/ directory. On first server start, RocketMod creates the Rocket/ folder structure:
Rocket/
├── Libraries/ ← RocketMod core DLLs
├── Permissions/ ← permissions.xml and groups.xml
│ ├── permissions.xml
│ └── groups.xml
├── Plugins/ ← plugin DLLs go here
│ └── MyPlugin.dll
├── Translations/ ← per-plugin translation files
│ └── MyPlugin/
│ └── English.xml
└── Rocket.config.xml ← global RocketMod configPlugin lifecycle
Every RocketMod plugin extends RocketPlugin<TConfiguration> where TConfiguration is a serializable class that maps to the plugin's XML configuration file. The lifecycle methods are:
| Method | When called | Purpose |
|---|---|---|
Load() | Server startup, after all modules are initialized | Initialize state, subscribe to events, register commands |
Unload() | Server shutdown or /rocket unload <plugin> command | Deregister events, flush state, release resources |
Reload() | /rocket reload <plugin> command | Calls Unload() then Load() on the same instance |
The Load() and Unload() methods are protected override void methods. They do not return tasks; RocketMod's plugin lifecycle is synchronous.
Always unsubscribe in Unload
Every event subscription in Load() must have a corresponding unsubscription in Unload(). If a plugin subscribes but does not unsubscribe, /rocket reload will result in double-firing — the handler registered in the first load is still active when the second load subscribes again. Double-firing produces duplicate messages, double-applied effects, and hard-to-trace logic errors.
Event subscription
RocketMod exposes Unturned's events through the UnturnedPlayerEvents and UnturnedServer static classes, as well as through the Rocket.Unturned.Events namespace. Events follow the standard C# event pattern.
Common events used in production plugins:
| Event | Class | Fires when |
|---|---|---|
OnPlayerConnected | UnturnedPlayerEvents | A player joins the server |
OnPlayerDisconnected | UnturnedPlayerEvents | A player leaves the server |
OnPlayerDeath | UnturnedPlayerEvents | A player dies (any cause) |
OnPlayerChatted | UnturnedPlayerEvents | A player sends a chat message |
OnPlayerRevive | UnturnedPlayerEvents | A player is respawned |
OnServerShutdown | UnturnedServer | The server is shutting down cleanly |
Subscription and desubscription follow the standard += / -= delegate pattern. Always desubscribe in Unload() to prevent memory leaks and double-firing after a reload.
Permission system
RocketMod's permission system is file-based. Permissions are defined in Rocket/Permissions/permissions.xml and groups.xml. The R.Permissions static class provides runtime permission checks.
Checking a permission in a command:
csharp
if (!R.Permissions.HasPermission(caller, "myplugin.mycommand"))
{
caller.SendChat("You do not have permission to use this command.", Color.red);
return;
}Defining a permission in permissions.xml:
xml
<Permission Cooldown="0">myplugin.mycommand</Permission>Permissions are assigned to groups in groups.xml:
xml
<Group>
<Id>admin</Id>
<DisplayName>Admin</DisplayName>
<Permissions>
<Permission Cooldown="0">myplugin.mycommand</Permission>
</Permissions>
</Group>Players are assigned to groups by a server admin using /rocket player <player> <group> or by editing players.xml directly.
Permission naming convention
The cohort's convention for permission names is <pluginname>.<commandname> — all lowercase, dot-separated. This convention prevents naming collisions across plugins and makes permissions readable in permission audits.
::: note AllowedCaller matters for console safety Set AllowedCaller to AllowedCaller.Player on any command that requires a valid player context (inventory access, teleportation, chat send). Setting it to Both on a player-context command and then calling it from the server console without a null check on caller will throw a NullReferenceException. :::
IRocketCommand interface
Commands in RocketMod are separate classes that implement IRocketCommand. The interface requires:
| Property / Method | Type | Purpose |
|---|---|---|
Name | string | The command keyword (e.g., "heal") |
Help | string | Short description shown in /help |
Syntax | string | Usage syntax shown in /help <command> |
Aliases | List<string> | Alternative command keywords |
Permissions | List<string> | Required permissions (empty = no restriction) |
AllowedCaller | AllowedCaller | Player, Console, or Both |
Execute(IRocketPlayer caller, string[] command) | void | Command body |
RocketMod hello-world plugin
The following is a cohort-validated minimal RocketMod plugin that greets players on join and exposes a /greet command.
Project structure:
MyGreeterPlugin/
├── MyGreeterPlugin.csproj
├── MyGreeterPlugin.cs ← plugin entry point
├── MyGreeterConfiguration.cs ← configuration class
└── Commands/
└── GreetCommand.cs ← /greet commandMyGreeterPlugin.csproj:
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<AssemblyName>MyGreeterPlugin</AssemblyName>
<RootNamespace>MyGreeterPlugin</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Reference Include="Rocket.Core">
<HintPath>libs\Rocket.Core.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Rocket.Unturned">
<HintPath>libs\Rocket.Unturned.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>libs\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
</Project>MyGreeterConfiguration.cs:
csharp
using Rocket.API;
namespace MyGreeterPlugin
{
public class MyGreeterConfiguration : IRocketPluginConfiguration
{
public string GreetingMessage;
public void LoadDefaults()
{
GreetingMessage = "Welcome to the server!";
}
}
}MyGreeterPlugin.cs:
csharp
using Rocket.Core.Plugins;
using Rocket.Unturned.Events;
using Rocket.Unturned.Player;
using UnityEngine;
namespace MyGreeterPlugin
{
public class MyGreeterPlugin : RocketPlugin<MyGreeterConfiguration>
{
public static MyGreeterPlugin Instance { get; private set; }
protected override void Load()
{
Instance = this;
UnturnedPlayerEvents.OnPlayerConnected += OnPlayerConnected;
Rocket.Core.Logging.Logger.Log($"[MyGreeterPlugin] Loaded. Greeting: {Configuration.Instance.GreetingMessage}");
}
protected override void Unload()
{
UnturnedPlayerEvents.OnPlayerConnected -= OnPlayerConnected;
Instance = null;
Rocket.Core.Logging.Logger.Log("[MyGreeterPlugin] Unloaded.");
}
private void OnPlayerConnected(UnturnedPlayer player)
{
player.SendChat(Configuration.Instance.GreetingMessage, Color.green);
}
}
}GreetCommand.cs:
csharp
using Rocket.API;
using Rocket.Unturned.Chat;
using System.Collections.Generic;
namespace MyGreeterPlugin.Commands
{
public class GreetCommand : IRocketCommand
{
public string Name => "greet";
public string Help => "Displays the server greeting message.";
public string Syntax => "/greet";
public List<string> Aliases => new List<string>();
public List<string> Permissions => new List<string> { "mygreetertplugin.greet" };
public AllowedCaller AllowedCaller => AllowedCaller.Player;
public void Execute(IRocketPlayer caller, string[] command)
{
UnturnedChat.Say(caller, MyGreeterPlugin.Instance.Configuration.Instance.GreetingMessage);
}
}
}Build the project with dotnet build -c Release, copy the output MyGreeterPlugin.dll into Rocket/Plugins/, and restart the server or run /rocket reload.
DLL dependencies
If your plugin references third-party DLLs that are not already present on the server, copy those DLLs into Rocket/Libraries/. RocketMod's assembly resolver looks there after the standard .NET resolution path.
OpenMod deep dive
Installation
Verify the Unturned version before installing OpenMod
OpenMod releases are tied to specific Unturned game version ranges. Installing an OpenMod module that is too old for the current Unturned version will produce a module initialization failure at server start. Check the release notes on the OpenMod GitHub repository to confirm compatibility before installing.
OpenMod is installed via the Unturned™ module system. Download the latest OpenMod.Unturned.Module-*.zip from the Smartly Dressed Games documentation site and extract it into Modules/OpenMod.Unturned/. On first server start, OpenMod creates its folder structure:
openmod/
├── config.yaml ← OpenMod core config
├── datastore/ ← plugin-managed persistent data
├── logs/ ← structured log output
└── plugins/ ← installed plugin packages (NuGet)
└── MyPlugin.1.0.0/
├── MyPlugin.dll
└── openmod.yaml ← plugin manifest.NET runtime requirement
OpenMod targets .NET 6.0 or later. The server host must have the .NET 6.0 Runtime installed. On Linux, install with the package manager (apt install dotnet-runtime-6.0 on Debian/Ubuntu). On Windows, download the .NET 6.0 Runtime from Microsoft and install it before starting the server.
Runtime version mismatch
If the server host has only .NET Framework or an older .NET Core version, OpenMod will fail to initialize. Verify the runtime with dotnet --version on the host before deploying OpenMod plugins. The output must be 6.0.x or higher.
Plugin lifecycle
OpenMod plugins implement the IPlugin interface through the OpenModUnrealPlugin or OpenModUnturnedPlugin base class. The lifecycle is fully asynchronous:
| Method | When called | Purpose |
|---|---|---|
OnLoadAsync() | Plugin start | Register services, subscribe to events, initialize async resources |
OnUnloadAsync() | Plugin stop or openmod unload <plugin> | Deregister services, flush state, cancel running tasks |
Both methods return Task. Long-running initialization (e.g., database connection, HTTP endpoint validation) is appropriate inside OnLoadAsync() without blocking the server.
NuGet package structure
An OpenMod plugin is distributed as a NuGet .nupkg. The openmod.yaml manifest inside the package declares the plugin's identity, version, and dependencies:
yaml
id: MyStudio.MyGreeterPlugin
version: 1.0.0
author: 57 Studios
description: Greets players on join.
level: intermediate
time: "40-60 minutes"
platforms:
- cross-platform
tools:
- "Unturned Server"
dependencies:
- OpenMod.Unturned: ^4.0.0The .csproj references the OpenMod packages via NuGet:
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>MyStudio.MyGreeterPlugin</AssemblyName>
<Version>1.0.0</Version>
<PackageId>MyStudio.MyGreeterPlugin</PackageId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenMod.Unturned" Version="4.*" />
<PackageReference Include="OpenMod.Extensions.Games.Abstractions" Version="4.*" />
<PackageReference Include="Autofac" Version="6.*" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.*" />
</ItemGroup>
</Project>IoC container and dependency injection
::: note Why dependency injection over static singletons RocketMod plugins use static singletons (e.g., MyPlugin.Instance) to share state between the plugin and its commands. OpenMod eliminates this pattern by injecting services through constructors. The benefit is testability and isolation: each class declares what it needs, and the container provides it. The trade-off is that developers unfamiliar with IoC patterns need to learn constructor injection before writing OpenMod plugins. :::
OpenMod's IoC container is built with Autofac. Services registered by OpenMod core (logging, permission checking, player management, configuration) are injected into plugin constructors. Plugin code does not use static singletons; instead, every dependency is declared as a constructor parameter.
Event subscription
OpenMod uses an event bus based on the IEventBus service. Event handlers are registered by decorating a class with [EventListener] and registering it through the IoC container.
csharp
[EventListener]
public class PlayerConnectedHandler : IEventListener<UnturnedPlayerConnectedEvent>
{
private readonly ILogger<PlayerConnectedHandler> _logger;
public PlayerConnectedHandler(ILogger<PlayerConnectedHandler> logger)
{
_logger = logger;
}
public async Task HandleEventAsync(object sender, UnturnedPlayerConnectedEvent @event)
{
_logger.LogInformation($"Player {event.Player.SteamId} connected.");
await Task.CompletedTask;
}
}Register the handler in the plugin's OnLoadAsync():
csharp
var eventBus = ServiceProvider.GetRequiredService<IEventBus>();
eventBus.Subscribe<UnturnedPlayerConnectedEvent>(ServiceProvider, HandleEventAsync);Common OpenMod Unturned events:
| Event | Fires when |
|---|---|
UnturnedPlayerConnectedEvent | A player joins the server |
UnturnedPlayerDisconnectedEvent | A player leaves the server |
UnturnedPlayerDeathEvent | A player dies |
UnturnedPlayerChattedEvent | A player sends a chat message |
UnturnedPlayerDamagedEvent | A player takes damage |
Permission system
OpenMod's permission system is provider-based. The default provider reads permissions from YAML files in openmod/datastore/. Permissions are checked via the injected IPermissionChecker service:
csharp
var result = await _permissionChecker.CheckPermissionAsync(actor, "myplugin:mycommand");
if (result != PermissionGrantResult.Grant)
{
await actor.PrintMessageAsync("You do not have permission.", Color.Red);
return;
}Permission format: OpenMod uses colon-separated permission strings — <pluginid>:<permissionname>. This is different from RocketMod's dot-separated format.
Assigning permissions via console:
openmod permissions add <playerSteamId or groupId> myplugin:mycommand
openmod permissions add <playerSteamId or groupId> myplugin.*The wildcard * grants all permissions under the plugin's namespace.
Migrating permission names
When migrating from RocketMod to OpenMod, remember that RocketMod uses dot-separated permission names (e.g., myplugin.mycommand) while OpenMod uses colon-separated names (e.g., myplugin:mycommand). You need to re-assign permissions in OpenMod format after migration.
OpenMod hello-world plugin
Plugin entry point:
csharp
using Cysharp.Threading.Tasks;
using Microsoft.Extensions.Logging;
using OpenMod.API.Plugins;
using OpenMod.Unturned.Plugins;
using System;
[assembly: PluginMetadata("MyStudio.MyGreeterPlugin",
DisplayName = "MyGreeter Plugin",
Description = "Greets players on join.")]
namespace MyGreeterPlugin
{
public class MyGreeterPlugin : OpenModUnturnedPlugin
{
private readonly ILogger<MyGreeterPlugin> _logger;
private readonly IEventBus _eventBus;
public MyGreeterPlugin(
ILogger<MyGreeterPlugin> logger,
IEventBus eventBus,
IServiceProvider serviceProvider)
: base(serviceProvider)
{
_logger = logger;
_eventBus = eventBus;
}
protected override async UniTask OnLoadAsync()
{
_eventBus.Subscribe<UnturnedPlayerConnectedEvent>(
ServiceProvider, OnPlayerConnected);
_logger.LogInformation("[MyGreeterPlugin] Loaded.");
await UniTask.CompletedTask;
}
protected override async UniTask OnUnloadAsync()
{
_eventBus.Unsubscribe(ServiceProvider);
_logger.LogInformation("[MyGreeterPlugin] Unloaded.");
await UniTask.CompletedTask;
}
private async Task OnPlayerConnected(
IEventBus sender, UnturnedPlayerConnectedEvent @event)
{
await @event.Player.PrintMessageAsync("Welcome to the server!", Color.green);
}
}
}Build and deploy:
powershell
dotnet build -c Release
dotnet pack -c Release -o ./dist
# Copy the .nupkg to the server's openmod/plugins/ directory, then:
openmod install MyStudio.MyGreeterPlugin --source ./distOr on the server console:
openmod install MyStudio.MyGreeterPluginIf the package is published to openmod.io, the install command fetches it automatically. For local development, use --source to point at a local NuGet feed or folder.
Hot-reload during development
OpenMod supports plugin reload without a full server restart. After rebuilding and re-packing the NuGet, run openmod reload MyStudio.MyGreeterPlugin in the server console. This calls OnUnloadAsync() followed by OnLoadAsync() on the refreshed assembly. For production servers, schedule reloads during low-activity periods to avoid service interruption.

Framework comparison
| Attribute | RocketMod | OpenMod |
|---|---|---|
| .NET target | Framework 4.7.2 | .NET 6.0+ |
| Plugin distribution | Manual .dll drop | NuGet package |
| Configuration format | XML | YAML |
| Lifecycle | Synchronous (Load/Unload) | Async (OnLoadAsync/OnUnloadAsync) |
| Dependency management | Manual reference copying | NuGet dependency resolution |
| IoC container | None (static singletons) | Autofac |
| Permission format | plugin.command (dot) | plugin:command (colon) |
| Command registration | IRocketCommand class | ICommand class |
| Event system | C# delegate events | IEventBus service |
| Active development | Community maintenance only | Active development |
| Plugin marketplace | None | openmod.io |
| Coexistence | Can run alongside OpenMod | Can run alongside RocketMod |
RocketMod-to-OpenMod migration considerations
When migration is worth evaluating
Migration is worth evaluating when one or more of the following is true:
- The server runs a small, manageable plugin library (fewer than 10 plugins) that the operator controls or has source access to.
- New plugins being developed are exclusively available on OpenMod.
- The .NET runtime version on the host must be updated for other reasons, making it a natural migration window.
- The existing RocketMod plugin library has compatibility issues with the current version of Unturned™.
When to stay on RocketMod
RocketMod remains the practical choice when:
- The server depends on RocketMod-only plugins whose authors have not migrated and whose source is not available.
- The operator does not have developer capacity to port the plugins.
- The server is stable and there is no active reason to undertake the migration risk.
Migration checklist
The following checklist structures the migration decision. Work through it in order before committing to migration:
Key technical changes in a port:
| RocketMod pattern | OpenMod equivalent |
|---|---|
RocketPlugin<TConfig> | OpenModUnturnedPlugin |
Configuration.Instance.Field | Injected IConfiguration via GetSection() |
UnturnedPlayerEvents.OnPlayerConnected += ... | IEventBus.Subscribe<UnturnedPlayerConnectedEvent> |
R.Permissions.HasPermission(caller, "perm") | IPermissionChecker.CheckPermissionAsync(actor, "perm") |
IRocketCommand | ICommand from OpenMod.API.Commands |
Rocket.Core.Logging.Logger.Log(...) | Injected ILogger<T> |
Static Instance singleton | Constructor injection |
Synchronous Load() / Unload() | async Task OnLoadAsync() / async Task OnUnloadAsync() |
Permission reassignment after migration
Switching from RocketMod to OpenMod does not carry over permission assignments. Every player and group permission must be re-assigned in OpenMod format after migration. Export the RocketMod players.xml and groups.xml before migration and use them as the reference for re-assignment. The cohort recommendation is to write a migration script that reads the RocketMod XML and issues the equivalent openmod permissions add commands automatically.
Back up before migration
Before starting any migration from RocketMod to OpenMod on a live server, make a full backup of the Rocket/ directory and the server's player save files. The migration process involves removing the RocketMod module and re-assigning all permissions, which cannot be automatically reversed. A backup is the only recovery path if the migration needs to be rolled back.
Common errors and diagnostics
| Symptom | Framework | Cause | Resolution |
|---|---|---|---|
| Plugin DLL not loaded at server start | RocketMod | DLL in wrong folder | Confirm DLL is in Rocket/Plugins/, not a subdirectory |
Could not load file or assembly error | RocketMod | Dependency DLL missing | Copy dependency to Rocket/Libraries/ |
| Plugin loads but commands not recognized | RocketMod | Command class not registered | Confirm command class implements IRocketCommand and is in same assembly |
.NET runtime version not supported | OpenMod | Host has old runtime | Install .NET 6.0 Runtime on host |
Package not found on openmod install | OpenMod | Package not on openmod.io | Use --source to point at local NuGet folder |
Autofac.Core.DependencyResolutionException | OpenMod | Missing service registration | Check that all constructor parameters are registered services |
| Permission check always denies | Both | Permission not assigned to player or group | Assign the permission via /rocket player or openmod permissions add |
Events fire twice after /rocket reload | RocketMod | Double subscription | Ensure -= in Unload() matches += in Load() |
| Plugin works in single-player but not server | Either | Assembly reference mismatch | Rebuild against the server's exact DLL versions |
| Configuration not persisting between reloads | RocketMod | Save() not called after mutation | Call Configuration.Save() after any config change |

Assembly version mismatch is the most common production failure
The most frequent cause of plugin failures in production (not development) is an assembly reference mismatch. Development workstations often have a different version of Assembly-CSharp.dll than the server. Always rebuild the plugin against DLLs extracted from the production server after each Unturned update, not against DLLs from the development machine's local Unturned install.
Frequently asked questions
What is the difference between RocketMod and OpenMod?
RocketMod is the legacy plugin framework that targets .NET Framework 4.7.2 and uses a folder-drop distribution model. It is no longer actively developed, but it remains operational and stable. OpenMod is the modern replacement that targets .NET 6.0, distributes plugins as NuGet packages, uses an Autofac IoC container, and has an active development and plugin marketplace. New plugins should target OpenMod. Existing RocketMod deployments can remain on RocketMod unless there is a specific reason to migrate.
Can I run RocketMod and OpenMod at the same time?
Yes. Both frameworks can coexist on a single server during a migration period. However, running both simultaneously adds overhead and occasional namespace conflicts. The cohort recommendation is to run one framework per server in production and to use the coexistence mode only as a transitional state during a migration.
What .NET version does OpenMod require?
OpenMod targets .NET 6.0 or later. The server host must have the .NET 6.0 Runtime installed. Verify with dotnet --version before deploying. Older .NET Framework versions are not compatible with OpenMod.
How do I reload a RocketMod plugin without restarting the server?
Use the console command /rocket reload <pluginName> where <pluginName> is the plugin's assembly name without the .dll extension. For example: /rocket reload MyGreeterPlugin. This calls Unload() and then Load() on the same plugin instance.
How do I install an OpenMod plugin from a local file?
Use the openmod install command with the --source parameter pointing at a local folder or NuGet feed:
openmod install MyStudio.MyGreeterPlugin --source C:\plugins\nupkgThe folder must contain the .nupkg file. For a local NuGet feed, point --source at the feed URL instead.
How do I define a command that works from both the player chat and the server console?
In RocketMod, set AllowedCaller to AllowedCaller.Both. In OpenMod, the command actor system handles this transparently — the same ICommand implementation receives calls from player actors and console actors. Check the actor type at runtime if the command behavior needs to differ between callers.
How do I configure plugin settings?
In RocketMod, create a class implementing IRocketPluginConfiguration with public fields. RocketMod serializes and deserializes it as XML in Rocket/Plugins/<PluginName>/<PluginName>.configuration.xml. In OpenMod, inject IConfiguration and read values from the plugin's YAML config file in openmod/datastore/<pluginid>/.
What is the correct way to send a message to a player from a plugin?
In RocketMod, use UnturnedChat.Say(player, "message", color) or player.SendChat("message", color). In OpenMod, use the async await player.PrintMessageAsync("message", color) method on the UnturnedPlayer actor object.
How do I schedule a repeating task in a plugin?
In RocketMod, use Unity's InvokeRepeating on a MonoBehaviour, or use R.Tasks for deferred tasks. In OpenMod, use System.Threading.Tasks with a CancellationToken from the plugin's lifetime, or use the IScheduler service to schedule periodic tasks. Always cancel the task in OnUnloadAsync().
Why does my RocketMod plugin's configuration reset on every reload?
The configuration is being loaded from disk every time Load() is called. If the plugin modifies configuration fields at runtime without calling Configuration.Save(), those changes are lost on the next load. Call Configuration.Save() immediately after mutating any configuration field to persist the change to the XML file.
Can a RocketMod plugin call Unturned's C# API directly?
Yes. RocketMod exposes the Assembly-CSharp.dll through its reference set. Plugin code can call Unturned's internal API the same way it calls RocketMod's API. Reference Assembly-CSharp.dll from the server's Unturned_Data/Managed/ directory. Note that Unturned's internal API can change between game updates, which can break plugins that depend on it directly.
How do I publish an OpenMod plugin to the openmod.io marketplace?
Create an account at openmod.io, configure your NuGet package with the correct openmod.yaml manifest, and push the package to the openmod.io NuGet feed endpoint. The marketplace indexes the package automatically. The cohort recommendation is to publish to a private NuGet feed first for internal testing, then push to openmod.io for public distribution.
What is the UniTask type in OpenMod code examples?
UniTask is an optimized task type from the Cysharp.Threading.Tasks NuGet package that OpenMod uses for its async plugin lifecycle methods. It is functionally equivalent to Task for most plugin code. Use await UniTask.CompletedTask as the async no-op equivalent of return Task.CompletedTask.
Appendix A: RocketMod configuration XML reference
RocketMod's global configuration lives at Rocket/Rocket.config.xml. The fields relevant to plugin developers are:
| Field | Default | Purpose |
|---|---|---|
RCON_Enabled | false | Enable the RocketMod RCON interface |
RCON_Port | 27115 | RCON listener port |
RCON_Password | (empty) | RCON password; set before enabling |
AutomaticShutdown_Enabled | false | Enable scheduled server restarts |
AutomaticShutdown_Interval | 0 | Restart interval in seconds |
LanguageCode | en | Language for built-in RocketMod messages |
Plugin-specific configuration is in Rocket/Plugins/<PluginName>/<PluginName>.configuration.xml. The format is determined by the plugin's IRocketPluginConfiguration class.
Example plugin configuration XML:
xml
<?xml version="1.0" encoding="utf-8"?>
<MyGreeterConfiguration>
<GreetingMessage>Welcome to the server!</GreetingMessage>
</MyGreeterConfiguration>Appendix B: OpenMod YAML configuration reference
OpenMod plugin configuration lives at openmod/plugins/<pluginId>/config.yaml. The format is standard YAML. A plugin reads configuration values via the injected IConfiguration service:
csharp
var message = _configuration.GetValue<string>("greeting_message", "Welcome!");Example plugin config.yaml:
yaml
greeting_message: "Welcome to the server!"
max_daily_rewards: 5
reward_cooldown_minutes: 60OpenMod's configuration system supports nested sections, lists, and type-safe binding to C# record classes.
Appendix C: Useful references
| Resource | URL | Purpose |
|---|---|---|
| Smartly Dressed Games modding docs | https://docs.smartlydressedgames.com/en/stable/ | Official Unturned modding and server documentation |
| Unturned on Steam | https://store.steampowered.com/app/304930/Unturned/ | Official game page; verify current game version |
| OpenMod GitHub | github.com/openmod/openmod | OpenMod source, issues, releases |
| openmod.io marketplace | openmod.io | Published OpenMod plugins |
| NuGet package manager | nuget.org | OpenMod and dependency packages |
Cross-references
- Workshop Content on Dedicated Servers — loading Workshop mods that plugins may depend on.
- Custom NPCs, Dialogues, and Quests — the next article; NPC quest systems that plugins can interact with.
- Server Commands Reference — built-in server commands, including RocketMod management commands.
- Self-Hosting — server installation prerequisites.
- Smartly Dressed Games Modding Documentation — official server module and plugin documentation.
- Unturned on Steam — game page for version reference.
Document history
| Version | Date | Author | Notes |
|---|---|---|---|
| 1.0 | 2025-05-18 | 57 Studios | Initial publication. RocketMod and OpenMod coverage, hello-world walkthroughs, migration checklist. |
