Creating xPilot Plugins
Developer documentation only. This page is for developers who want to create xPilot client plugins for other users. If you only want to install or use xPilot, you do not need to follow these steps.
xPilot plugins let developers extend the xPilot client with custom behavior for other xPilot users. Plugins are .NET assemblies loaded by the client at startup, and they can react to xPilot events, post debug messages to the client, request network actions, send text messages, and control a few simulator-facing features such as Mode C, ident, and PTT.
This page is for developers who want to create and distribute plugins that run inside the xPilot client. It covers the plugin interface, project setup, lifecycle events, and broker methods available to plugins.
Plugins run inside the xPilot client process. Keep plugin code lightweight, handle exceptions carefully, and avoid blocking event handlers.
xPilot client plugins are different from the X-Plane
Resources/plugins/xPilotplugin. Client plugins are managed .NET assemblies installed into the xPilot application dataPluginsfolder. The plugin assembly itself is a.dllon Windows, macOS, and Linux.
Requirements
Create a C# class library that targets the same .NET version as the xPilot plugin interface. The current xPilot.Plugins interface assembly targets net10.0.
Your plugin DLL must contain at least one public, non-abstract class that implements Vatsim.Xpilot.Plugins.IPlugin and has a public parameterless constructor. xPilot scans only top-level plugin DLLs in the plugins folder and initializes every compatible plugin type it finds.
Create the Project
Clone the xPilot.Plugins project from the xPilot GitHub page, then create your plugin project next to it:
git clone https://github.com/xpilot-project/xPilot.Plugins.git
dotnet new classlib -n MyXpilotPlugin
For this example, place the projects next to each other like this:
PluginDevelopment/
MyXpilotPlugin/
MyXpilotPlugin.csproj
xPilot.Plugins/
xPilot.Plugins.csproj
Update your plugin project file to target net10.0 and reference the cloned xPilot.Plugins project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../xPilot.Plugins/xPilot.Plugins.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>
</Project>
Use the xPilot.Plugins branch or tag that matches the xPilot version you are targeting.
Implement IPlugin
Implement IPlugin.Name and IPlugin.Initialize. Store the broker passed to Initialize if you need to call xPilot later, and subscribe to events from inside Initialize.
using System;
using Vatsim.Xpilot.Plugins;
using Vatsim.Xpilot.Plugins.Events;
using Vatsim.Xpilot.Plugins.Exceptions;
namespace MyXpilotPlugin;
public sealed class MyPlugin : IPlugin
{
private IBroker? _broker;
public string Name => "My xPilot Plugin";
public void Initialize(IBroker broker)
{
_broker = broker;
broker.NetworkConnected += OnNetworkConnected;
broker.NetworkDisconnected += OnNetworkDisconnected;
broker.PrivateMessageReceived += OnPrivateMessageReceived;
broker.SessionEnded += OnSessionEnded;
broker.PostDebugMessage($"{Name} loaded.");
}
private void OnNetworkConnected(object? sender, NetworkConnectedEventArgs e)
{
_broker?.PostDebugMessage($"Connected as {e.Callsign}.");
try
{
_broker?.RequestMetar("KJFK");
}
catch (NotConnectedException)
{
// The connection closed before the request was sent.
}
}
private void OnNetworkDisconnected(object? sender, EventArgs e)
{
_broker?.PostDebugMessage("Disconnected from VATSIM.");
}
private void OnPrivateMessageReceived(object? sender, PrivateMessageReceivedEventArgs e)
{
_broker?.PostDebugMessage($"Private message from {e.From}: {e.Message}");
}
private void OnSessionEnded(object? sender, EventArgs e)
{
if (_broker == null)
return;
_broker.NetworkConnected -= OnNetworkConnected;
_broker.NetworkDisconnected -= OnNetworkDisconnected;
_broker.PrivateMessageReceived -= OnPrivateMessageReceived;
_broker.SessionEnded -= OnSessionEnded;
}
}
Broker Events
The IBroker interface exposes these events:
| Event | Use |
|---|---|
SessionEnded | xPilot is closing. Unsubscribe from events and stop background work here. |
NetworkConnected / NetworkDisconnected
| The VATSIM network session changed state. |
PrivateMessageReceived / PrivateMessageSent
| Private text messages were received or sent. |
RadioMessageReceived / RadioMessageSent
| Text radio messages were received or sent. |
BroadcastMessageReceived | A network broadcast was received. |
MetarReceived | A requested METAR was received. |
AtisReceived | Requested controller information or text ATIS was received. |
ControllerAdded / ControllerDeleted
| A controller appeared or disappeared. |
ControllerFrequencyChanged | A controller changed primary frequency. |
ControllerLocationChanged | A controller position changed. |
SelcalAlertReceived | A SELCAL alert was received. |
AircraftAdded / AircraftUpdated / AircraftDeleted
| Nearby aircraft were added, updated, or removed from the simulator session. |
Radio frequencies exposed by message events are integers with the leading 1 and decimal point removed. For example, 123.725 is represented as 23725.
Broker Methods
Plugins can call these methods through IBroker:
| Method | Use |
|---|---|
RequestConnect(callsign, typeCode, selcalCode) | Connect as a pilot. |
RequestConnectAsObserver(callsign) | Connect as an observer. |
RequestConnectAsTowerView() | Connect in tower view mode. |
RequestDisconnect() | Disconnect from the network. |
RequestMetar(station) | Request a METAR. |
RequestAtis(callsign) | Request controller information or text ATIS. |
SendPrivateMessage(to, message) | Send a private message. |
SendRadioMessage(message) | Send a text radio message on the current transmit radio or radios. |
PostDebugMessage(message) | Write a debug message to the xPilot message area. |
SetModeC(modeC) | Toggle Mode C. |
SquawkIdent() | Trigger ident. |
SetPtt(pressed) | Set PTT pressed or released. |
Some methods throw state exceptions. Handle AlreadyConnectedException, NotConnectedException, and SimNotReadyException where appropriate instead of assuming the client state cannot change between an event and your method call.
Build the Plugin
For distribution, publish a release build:
dotnet publish -c Release
Use the output from bin/Release/net10.0/publish/. Include your plugin DLL and any dependency DLLs your plugin requires.
Managed .NET plugins only need to be compiled once. The same plugin DLL can run on Windows, macOS, and Linux as long as the plugin and its dependencies are platform-independent. It is still recommended to test your plugin on all three xPilot platforms before publishing it.
Install the Plugin
For end-user installation steps, see Installing Plugins.
Open the xPilot plugins folder with the .plugins command in the xPilot command line. You can also create the folder manually if it doesn’t exist:
| Platform | Plugins folder |
|---|---|
| Windows | %LOCALAPPDATA%\org.vatsim.xpilot\Plugins |
| macOS | ~/Library/Application Support/org.vatsim.xpilot/Plugins |
| Linux | ~/.local/share/org.vatsim.xpilot/Plugins |
Copy your plugin DLL, plus any dependency DLLs or native libraries, directly into that folder. Restart xPilot after copying files. xPilot loads plugins only during startup.
If the plugin does not load, check the xPilot logs with the .logs command. Loading and initialization failures are written there.
Publishing Suggestions
Package your plugin as a zip file that users can extract directly into the xPilot Plugins folder. Include a short README with the supported xPilot version, installation steps, and any required configuration.