--- /dev/null
+# Writing a plugin
+
+Impostor has support for plugins. This document will help you to setup a development environment for writing a plugin.
+
+- [1. Install .NET Core SDK](#1-install-net-core-sdk)
+- [2. Create a C# project](#2-create-a-c-project)
+- [3. Add the Impostor.Api library](#3-add-the-impostorapi-library)
+ - [Quick](#quick)
+ - [Visual Studio](#visual-studio)
+- [4. The plugin class](#4-the-plugin-class)
+- [5. Adding an event listener](#5-adding-an-event-listener)
+- [6. Build and run your plugin](#6-build-and-run-your-plugin)
+- [7. Extra](#7-extra)
+ - [Event listeners](#event-listeners)
+ - [Dependency injection](#dependency-injection)
+- [8. Missing/invalid data or want more functions?](#8-missinginvalid-data-or-want-more-functions)
+
+## 1. Install .NET Core SDK
+
+Download and install the latest .NET Core SDK.
+
+https://dotnet.microsoft.com/download
+
+## 2. Create a C# project
+
+The first step is creating a new C# project, it must be a **Class Library (.NET Standard)**. The target framework can be any of those compatible with .NET 5, which includes:
+
+- .NET Standard 2.0
+- .NET Standard 2.1
+- .NET Core 3.1
+- .NET 5
+
+For more information about compatibility, see https://docs.microsoft.com/en-us/dotnet/standard/net-standard.
+
+> At the moment of writing this document, I recommend you to use **.NET Standard 2.1** until .NET 5 is released officially. This should give you enough functionality. If not, upgrade to .NET Core 3.1.
+
+When the project has been created, you should have `Class.cs` and `Project.csproj` files. Your `Project.csproj` should look something like this.
+
+```xml
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.1</TargetFramework>
+ </PropertyGroup>
+</Project>
+```
+
+## 3. Add the Impostor.Api library
+
+You only have to follow the instructions of one below.
+
+### Quick
+
+Install the `Impostor.Api` NuGet package.
+Make sure to get a prerelease if you are writing a plugin for a dev release of the server.
+
+### Visual Studio
+
+1. Right click your project.
+2. Click `Manage NuGet Packages`.
+3. Click `Browse`.
+4. Next to the search bar, enable `Include prerelease`.
+5. Search for `Impostor.Api`.
+6. Click the `Impostor.Api` result and press install on the right side.
+
+## 4. The plugin class
+
+Now the `Impostor.Api` is installed, you need to create a class for your plugin. A plugin **must** contain exactly one. See the code below for an example.
+
+```csharp
+using System.Threading.Tasks;
+using Impostor.Api.Events.Managers;
+using Impostor.Api.Plugins;
+using Microsoft.Extensions.Logging;
+
+namespace Impostor.Plugins.Example
+{
+ /// <summary>
+ /// The metadata information of your plugin, this is required.
+ /// </summary>
+ [ImpostorPlugin(
+ package: "gg.impostor.example",
+ name: "Example",
+ author: "AeonLucid",
+ version: "1.0.0")]
+ public class ExamplePlugin : PluginBase // This is also required ": PluginBase".
+ {
+ /// <summary>
+ /// A logger that works seamlessly with the server.
+ /// </summary>
+ private readonly ILogger<ExamplePlugin> _logger;
+
+ /// <summary>
+ /// The constructor of the plugin. There are a few parameters you can add here and they
+ /// will be injected automatically by the server, two examples are used here.
+ ///
+ /// They are not necessary but very recommended.
+ /// </summary>
+ /// <param name="logger">
+ /// A logger to write messages in the console.
+ /// </param>
+ /// <param name="eventManager">
+ /// An event manager to register event listeners.
+ /// Useful if you want your plugin to interact with the game.
+ /// </param>
+ public ExamplePlugin(ILogger<ExamplePlugin> logger, IEventManager eventManager)
+ {
+ _logger = logger;
+ }
+
+ /// <summary>
+ /// This is called when your plugin is enabled by the server.
+ /// </summary>
+ /// <returns></returns>
+ public override ValueTask EnableAsync()
+ {
+ _logger.LogInformation("Example is being enabled.");
+ return default;
+ }
+
+ /// <summary>
+ /// This is called when your plugin is disabled by the server.
+ /// Most likely because it is shutting down, this is the place to clean up any managed resources.
+ /// </summary>
+ /// <returns></returns>
+ public override ValueTask DisableAsync()
+ {
+ _logger.LogInformation("Example is being disabled.");
+ return default;
+ }
+ }
+}
+```
+
+## 5. Adding an event listener
+
+Currently you should have a plugin that loads and does nothing. In order to get some actual functionality, you need to add an event listener.
+
+Create a new class called `GameEventListener`. Example code:
+
+```csharp
+using Impostor.Api.Events;
+using Microsoft.Extensions.Logging;
+
+namespace Impostor.Plugins.Example.Handlers
+{
+ /// <summary>
+ /// A class that listens for two events.
+ /// It may be more but this is just an example.
+ ///
+ /// Make sure your class implements <see cref="IEventListener"/>.
+ /// </summary>
+ public class GameEventListener : IEventListener
+ {
+ private readonly ILogger<ExamplePlugin> _logger;
+
+ public GameEventListener(ILogger<ExamplePlugin> logger)
+ {
+ _logger = logger;
+ }
+
+ /// <summary>
+ /// An example event listener.
+ /// </summary>
+ /// <param name="e">
+ /// The event you want to listen for.
+ /// </param>
+ [EventListener]
+ public void OnGameStarted(GameStartedEvent e)
+ {
+ _logger.LogInformation($"Game is starting.");
+
+ // This prints out for all players if they are impostor or crewmate.
+ foreach (var player in e.Game.Players)
+ {
+ var info = player.Character.PlayerInfo;
+ var isImpostor = info.IsImpostor;
+ if (isImpostor)
+ {
+ _logger.LogInformation($"- {info.PlayerName} is an impostor.");
+ }
+ else
+ {
+ _logger.LogInformation($"- {info.PlayerName} is a crewmate.");
+ }
+ }
+ }
+
+ [EventListener]
+ public void OnGameEnded(GameEndedEvent e)
+ {
+ _logger.LogInformation($"Game has ended.");
+ }
+
+ [EventListener]
+ public void OnPlayerChat(PlayerChatEvent e)
+ {
+ _logger.LogInformation($"{e.PlayerControl.PlayerInfo.PlayerName} said {e.Message}");
+ }
+ }
+}
+```
+
+## 6. Build and run your plugin
+
+Now your plugin is ready to be tested.
+
+1. Right click your project and press `Build`.
+2. Right click your project and press `Open Folder in File Explorer`.
+3. Go to `bin/Debug/netstandard2.1/`.
+4. In this directory, you should find your plugin named `Project.dll`.
+5. Copy the `Project.dll` to the `plugins` directory in your Impostor server directory.
+6. (Re)start your Impostor server.
+7. Open Among Us, create a game and send a chat message. In the console you should see your plugin being loaded and the messages from the example.
+
+## 7. Extra
+
+Some extra information that might be useful for those developing plugins.
+
+### Event listeners
+
+- You can have multiple event listener on the same event.
+- An event listener can be given a priority `[EventListener(EventPriority.Normal)]` and is called in order.
+- It is not recommended to block for a long time inside `EventListener` because the events are called from inside the packet handlers. Blocking too long causes the client to time out. You should create a new `Task` for operations that will take a lot of time.
+
+### Dependency injection
+
+- The main plugin class is constructed by the `IServiceProvider` of the server and can inject everything the server uses. A few examples are:
+ - `ILogger<T>`
+ - `IEventManager`
+ - `IClientManager`
+ - `IOptions<ServerConfig>`
+ - `IOptions<ServerRedirectorConfig>`
+- You can add your own classes and `EventListener` implementation to the `IServiceProvider` by creating a new class and implementing `IPluginStartup`. Make sure to register them as a singleton `services.AddSingleton<IEventListener, GameEventListener>();`.
+
+## 8. Missing/invalid data or want more functions?
+
+The `Impostor.Api` is currently in beta. There are a lot of things still missing and we would like to hear from you what you need to develop a plugin.
+
+Create an issue:
+
+- [Suggest a function](https://github.com/Impostor/Impostor/issues/new?template=api-suggestion.md)
+- [Data is invalid](https://github.com/Impostor/Impostor/issues/new?template=api-invalid.md)
+- [Data is unavailable](https://github.com/Impostor/Impostor/issues/new?template=api-missing.md)
+- [Other](https://github.com/Impostor/Impostor/issues/new?template=api-other.md)
\ No newline at end of file