Controller Plugins
The Nexus Controller supports extending functionality through plugins. Plugins are .NET assemblies that inherit from ControllerPluginAPI and can integrate custom services, UI components, and logic into the controller application.
Overview
Controller plugins are loaded at startup and can:
- Provide custom services and business logic
- Add UI components to the controller
- Interact with the Nexus network and databases
- Hook into the controller’s dependency injection system
Plugins can be distributed as either:
- Folder Plugins — A directory containing DLLs and a
manifest.xml - Zip Plugins — A
.zipfile containing DLLs and amanifest.xml
Getting Started
Prerequisites
- .NET 8 SDK or later
- Visual Studio or similar C# IDE
- Reference to the Nexus NuGet packages (or build from source)
Creating a Plugin Project
- Create a new Class Library project targeting
.NET 8:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<!-- Reference the NGController project or NuGet packages -->
<ProjectReference Include="path/to/NGController/NGController.csproj" />
</ItemGroup>
</Project>
- Create a plugin class that inherits from
ControllerPluginAPI:
using NGController.PluginAPI;
using System;
using System.Windows.Controls;
namespace MyPlugin
{
public class MyControllerPlugin : ControllerPluginAPI
{
public override Version PluginVersion => new Version(1, 0, 0);
public override UserControl UserControl => new MyPluginUserControl();
public override async Task StartService()
{
// Initialize your plugin here
await base.StartService();
}
public override async Task StopService()
{
// Cleanup when the controller shuts down
await base.StopService();
}
public override void StartNetworking()
{
// Set up networking if needed
base.StartNetworking();
}
}
}
- Create a manifest file at the root of your plugin folder:
<?xml version="1.0"?>
<ControllerPluginManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PluginName>My Plugin</PluginName>
<PluginVersion>1.0.0</PluginVersion>
<MinControllerVersion>3.0.0</MinControllerVersion>
<MaxControllerVersion>9.9.9</MaxControllerVersion>
<PluginGUID>12345678-1234-1234-1234-123456789012</PluginGUID>
<IsBeta>false</IsBeta>
<Author>Your Name</Author>
<Description>A description of what your plugin does</Description>
<AuthorURL>https://example.com</AuthorURL>
<PluginURL>https://github.com/your-repo</PluginURL>
</ControllerPluginManifest>
Plugin Structure
ControllerPluginAPI
The base class all plugins must inherit from. It extends ControllerServiceAPI and provides the core plugin functionality.
Key Properties:
| Property | Type | Description |
|---|---|---|
PluginVersion |
Version |
The version of your plugin |
UserControl |
UserControl |
Optional WPF control for your plugin’s UI |
Key Methods:
| Method | Description |
|---|---|
StartService() |
Called when the controller starts. Use for initialization. |
StopService() |
Called when the controller shuts down. Use for cleanup. |
StartNetworking() |
Called to set up any network listeners or subscriptions. |
Manifest File
The manifest.xml file describes your plugin. Required fields:
| Field | Type | Description |
|---|---|---|
PluginName |
string |
Display name of your plugin |
PluginVersion |
Version |
Current version (format: Major.Minor.Patch) |
MinControllerVersion |
Version |
Minimum controller version required |
MaxControllerVersion |
Version |
Maximum controller version supported |
PluginGUID |
Guid |
Unique identifier for your plugin |
IsBeta |
bool |
Whether this is a beta release |
Author |
string |
Plugin author name |
Description |
string |
Plugin description |
AuthorURL |
string |
Author’s website |
PluginURL |
string |
Plugin repository or download page |
Installation
Folder Plugin
- Create a folder in the controller’s
Pluginsdirectory (e.g.,Plugins/MyPlugin) - Copy your plugin’s DLLs into that folder
- Add a
manifest.xmlto the folder root - Restart the controller
Example structure:
Plugins/
└── MyPlugin/
├── manifest.xml
├── MyPlugin.dll
└── MyPlugin.pdb
Zip Plugin
- Create a
.zipfile containing your DLLs andmanifest.xml - Place the
.zipin the controller’sPluginsdirectory - Restart the controller
Example structure:
Plugins/
└── MyPlugin.zip
├── manifest.xml
├── MyPlugin.dll
└── MyPlugin.pdb
Advanced Features
Dependency Injection
Your plugin can use the dependency injection container to access other services:
public class MyControllerPlugin : ControllerPluginAPI
{
private readonly DiscordService _discordService;
public MyControllerPlugin(DiscordService discordService)
{
_discordService = discordService;
}
public override async Task StartService()
{
// Use injected service
await base.StartService();
}
}
Adding a UI Component
Create a WPF UserControl for your plugin:
using System.Windows.Controls;
namespace MyPlugin
{
public partial class MyPluginUserControl : UserControl
{
public MyPluginUserControl()
{
InitializeComponent();
}
}
}
Then return it from your plugin class:
public override UserControl UserControl => new MyPluginUserControl();
Accessing the Database
If your plugin needs database access, you can inject ControllerSQLService:
using NGController.Services;
public class MyControllerPlugin : ControllerPluginAPI
{
private readonly ControllerSQLService _sqlService;
public MyControllerPlugin(ControllerSQLService sqlService)
{
_sqlService = sqlService;
}
public override async Task StartService()
{
if (_sqlService.TryOpenConnection(out var connection))
{
using (connection)
{
// Access your database tables
}
}
await base.StartService();
}
}
Network Communication
Subscribe to network events:
using NexusAPI.Networking;
public override void StartNetworking()
{
NetSubscriber.TrySubscribe(
MessageType.ChatSync,
HandleChatMessage
);
base.StartNetworking();
}
private void HandleChatMessage(Msg message)
{
// Handle the incoming message
}
Version Compatibility
Plugins specify minimum and maximum controller versions they support:
<MinControllerVersion>3.0.0</MinControllerVersion>
<MaxControllerVersion>3.5.0</MaxControllerVersion>
The controller will only load plugins that support its version. Use appropriate version ranges to ensure compatibility.
Best Practices
- Use meaningful GUIDs — Generate a unique GUID for each plugin using
System.Guid.NewGuid() - Version your plugin — Follow semantic versioning (Major.Minor.Patch)
- Include symbol files — Ship
.pdbfiles with your DLLs for better debugging - Document dependencies — Clearly state what controller version and features your plugin requires
- Handle errors gracefully — Log exceptions and provide meaningful error messages
- Clean up resources — Implement
StopService()to release resources - Test thoroughly — Test your plugin on the minimum and maximum controller versions you support
Example Plugin
A complete minimal example:
manifest.xml:
<?xml version="1.0"?>
<ControllerPluginManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PluginName>Hello World Plugin</PluginName>
<PluginVersion>1.0.0</PluginVersion>
<MinControllerVersion>3.0.0</MinControllerVersion>
<MaxControllerVersion>9.9.9</MaxControllerVersion>
<PluginGUID>a1b2c3d4-a1b2-a1b2-a1b2-a1b2c3d4e5f6</PluginGUID>
<IsBeta>false</IsBeta>
<Author>Example Author</Author>
<Description>A simple hello world plugin</Description>
<AuthorURL>https://example.com</AuthorURL>
<PluginURL>https://github.com/example/hello-world-plugin</PluginURL>
</ControllerPluginManifest>
HelloWorldPlugin.cs:
using NGController.PluginAPI;
using NLog;
using System;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace HelloWorldPlugin
{
public class HelloWorldPlugin : ControllerPluginAPI
{
private static Logger log = LogManager.GetCurrentClassLogger();
public override Version PluginVersion => new Version(1, 0, 0);
public override UserControl UserControl => new HelloWorldControl();
public override async Task StartService()
{
log.Info("Hello World Plugin started!");
await base.StartService();
}
public override async Task StopService()
{
log.Info("Hello World Plugin stopping!");
await base.StopService();
}
}
}
HelloWorldControl.xaml:
<UserControl x:Class="HelloWorldPlugin.HelloWorldControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Margin="10">
<TextBlock Text="Hello from My Plugin!" FontSize="16" FontWeight="Bold"/>
<TextBlock Text="This is my custom UI" Margin="0,10,0,0"/>
</StackPanel>
</UserControl>
Available Services
The controller provides several built-in services that you can inject into your plugin to access common functionality. These services are registered in the dependency injection container and available for constructor injection.
Service Reference
| Service | Purpose |
|---|---|
ControllerConfigService |
Access and manage controller configuration (database settings, Discord config, application paths) |
ControllerSQLService |
Query and manage the PostgreSQL database; execute migrations and manage database connections |
NexusService |
Core Nexus networking; access network poller, subscriber, publisher, and request-reply systems |
DiscordService |
Integrate with Discord; send messages, manage commands, and interact with Discord guilds and channels |
ScriptService |
Manage Nexus scripts and prefabs; access script configuration and prefab data |
ISnackbarService |
Display notifications and UI feedback to the user through snackbar messages |
PageService |
Navigate to and retrieve WPF pages from the UI layer |
ApplicationHostService |
Access application-level information and lifecycle management |
AutoUpdateService |
Check for and manage application updates |
Usage Examples
Database Access
Inject ControllerSQLService to access the database:
using NGController.Services;
using NGController.PluginAPI;
public class MyControllerPlugin : ControllerPluginAPI
{
private readonly ControllerSQLService _sqlService;
public MyControllerPlugin(ControllerSQLService sqlService)
{
_sqlService = sqlService;
}
public override async Task StartService()
{
if (_sqlService.TryOpenConnection(out var connection))
{
using (connection)
{
// Execute queries or use Entity Framework Core
var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM your_table LIMIT 10";
// ... execute command
}
}
await base.StartService();
}
}
Discord Integration
Inject DiscordService to send messages or perform Discord actions:
using NGController.Services;
using NGController.PluginAPI;
using Discord.WebSocket;
public class MyControllerPlugin : ControllerPluginAPI
{
private readonly DiscordService _discordService;
public MyControllerPlugin(DiscordService discordService)
{
_discordService = discordService;
}
public override async Task StartService()
{
// Access Discord client for sending messages, managing roles, etc.
var discordClient = DiscordService.dClient;
if (discordClient?.ConnectionState == Discord.ConnectionState.Connected)
{
// Send messages to channels
var channel = discordClient.GetChannel(channelId) as SocketTextChannel;
await channel?.SendMessageAsync("Plugin started!");
}
await base.StartService();
}
}
Configuration Access
Inject ControllerConfigService to access application configuration:
using NGController.Services;
using NGController.PluginAPI;
public class MyControllerPlugin : ControllerPluginAPI
{
private readonly ControllerConfigService _configService;
public MyControllerPlugin(ControllerConfigService configService)
{
_configService = configService;
}
public override async Task StartService()
{
var config = _configService.MainConfigs;
var scriptsFolder = ControllerConfigService.ScriptsFolder;
var configStoragePath = ControllerConfigService.ConfigStoragePath;
// Use configuration values for your plugin
await base.StartService();
}
}
Nexus Networking
Inject NexusService to access network communication:
using NGController.Services;
using NGController.PluginAPI;
using NexusAPI.Networking;
public class MyControllerPlugin : ControllerPluginAPI
{
private readonly NexusService _nexusService;
public MyControllerPlugin(NexusService nexusService)
{
_nexusService = nexusService;
}
public override void StartNetworking()
{
// Subscribe to network messages
NexusService.msgSubscriber?.TrySubscribe(
MessageType.ChatSync,
HandleChatMessage
);
base.StartNetworking();
}
private void HandleChatMessage(Msg message)
{
// Handle incoming network message
}
}
Publishing Messages
To send data to the Nexus network, create a message object, populate it with data, and publish it using .Publish(). Inject ControllerConfigService to access configuration data like gates, clusters, or servers:
using NGController.Services;
using NGController.PluginAPI;
using NexusAPI.Messages;
using NexusAPI.Structures;
public class MyControllerPlugin : ControllerPluginAPI
{
private readonly ControllerConfigService _configService;
public MyControllerPlugin(ControllerConfigService configService)
{
_configService = configService;
}
public override async Task StartService()
{
// Example: Publish a gate configuration update
var msg = new GateUpdateMessage();
msg.allGates = _configService.MainConfigs.ConfiguredGates.Select(x => x.Serializable).ToArray();
// Inject my own custom gates or modify existing ones before publishing. You can also save these changes back to the config file if desired.
msg.Publish(MessageType.GateSync);
await base.StartService();
}
}
Real-world example from the Gates configuration page: The message will be distributed to all connected nodes in the cluster, allowing them to receive and process the updated data.
Multiple Service Injection
You can inject multiple services into a single plugin:
using NGController.Services;
using NGController.PluginAPI;
using Wpf.Ui;
public class AdvancedPlugin : ControllerPluginAPI
{
private readonly ControllerSQLService _sqlService;
private readonly DiscordService _discordService;
private readonly ControllerConfigService _configService;
public AdvancedPlugin(
ControllerSQLService sqlService,
DiscordService discordService,
ControllerConfigService configService
)
{
_sqlService = sqlService;
_discordService = discordService;
_configService = configService;
}
public override async Task StartService()
{
await base.StartService();
}
}
Troubleshooting
Plugin Won’t Load
- Check that
manifest.xmlis valid XML and in the correct location - Verify the manifest is not malformed (missing required fields)
- Check the controller logs for specific error messages
- Ensure the plugin’s minimum/maximum controller versions match
Version Incompatibility
If you see an error like “Plugin min controller version is 3.5.0”:
- Your controller version is too old for the plugin
- Update the controller or use an older plugin version
Missing Dependencies
If your plugin fails to load:
- Verify all required DLLs are in the plugin folder
- Check that dependency versions match what’s expected
- Include
.pdbfiles for better error diagnostics
See Also
- Controller Services — How to extend controller functionality
- Nexus Scripting API — For server-side plugins
- Mod API — For Space Engineers mod integration