Documentation menu

Docs / Plugin development

Build a plugin.

Client plugins are in-process C# components. They can read game state, replace behavior, patch engine methods and draw their own interface. This is more than a workshop mod, and it starts with a normal .NET project.

C# / .NET 10 Template: CringePlugins.Templates Package type: CringePlugin

What a plugin is

SE Launcher loads plugins next to the game, not inside the game's mod sandbox. A plugin can use internal and private APIs, patch methods with Harmony, hook engine systems and render through Dear ImGui. That power is why the project asks for public source and a review before a plugin reaches the official PluginHub.

You will need C# experience and an IDE that can load a .NET project. If you are new to C#, start with the Microsoft C# documentation.

Set up

Install the templates

Install the official template package from the project's NuGet feed. The template adds a Space Engineers plugin project to your IDE's new-project menu.

Terminal
dotnet new install CringePlugins.Templates --nuget-source https://ng.zznty.net/v3/index.json

Create a project from the template, then start a debug session. The template is set up so the game launches under the debugger with the plugin loaded. In Visual Studio and VS Code that is usually F5; in Rider, Shift + F9 for debug or Shift + F10 for run.

Local plugins

For quick iteration, a plugin can be loaded from the launcher's local plugin folder. Local plugins show up in the in-game plugin list as local, and the context menu can reload one without restarting the game. On Windows the folder is %APPDATA%\CringeLauncher\plugins.

Lifecycle

The base interface is VRage.Plugins.IPlugin. The launcher creates the plugin, calls Init, then Update on the game loop, and finally Dispose.

C# / IPlugin
using VRage.Plugins;

public class Plugin : IPlugin
{
    public void Init(object gameInstance)
    {
    }

    public void Update()
    {
    }

    public void Dispose()
    {
    }
}

If your plugin needs dependency injection, implement IPluginWithServices instead. It gives you an IPluginContext with the plugin metadata and the loaded plugin map, and lets you register services during plugin setup.

Configuration

Use the launcher's config handler instead of inventing another settings file. It stores one JSON document per config ID, validates values against a generated schema, and broadcasts changes to every consumer that registered the same config.

C# / plugin config
public class Config
{
    public string UserName { get; set; } = "John";
}

public void Init(object gameInstance)
{
    var handler = MySandboxGame.Services.GetRequiredService<ConfigHandler>();
    using var config = handler.RegisterConfig<Config>("test");

    Log.Info("Hello, {UserName}", config.Value.UserName);
}
  • The config class must be public, have a parameterless constructor, and use public getters and setters.
  • RegisterConfig<T>(id) returns a disposable reference.
  • Assigning a new instance to .Value persists it and notifies other consumers. Mutating a property alone does not.
  • Dispose the reference when the plugin no longer needs it.

UI with ImGui

Dear ImGui is the preferred UI system and what the launcher itself uses. Implement IRenderComponent and register it with the render handler. The launcher calls OnFrame() while it composes the frame.

C# / ImGui component
using CringePlugins.Abstractions;
using ImGuiNET;

public class TestRenderComponent : IRenderComponent
{
    public void OnFrame()
    {
        if (ImGui.Begin("Test Window"))
        {
            ImGui.Button("Test");
            ImGui.End();
        }
    }
}

public void Init(object gameInstance)
{
    RenderHandler.Current.RegisterComponent(new TestRenderComponent());
}

ImGui gives you docking, floating viewports, tables and the usual immediate-mode widgets. For exploration, call ImGui.ShowDemoWindow() once; it exposes the full widget set with live examples.

Other UI systems are available when the job calls for them: the native VRage sandbox GUI (MyGuiScreenBase), EmptyKeys MVVM-XAML, and Windows Forms or WPF for external windows. They require more setup and are not the recommended path for an in-game panel.

Patching game code

Harmony is part of the launcher environment. Use it for runtime patches and give every plugin a unique Harmony ID so two plugins do not fight over the same patch slot. Patches coexist unless they try to replace the same behavior outright.

C# / Harmony patch
using HarmonyLib;
using NLog;
using Sandbox.Game.World;

[HarmonyPatch(typeof(MySession), nameof(MySession.SaveEnded))]
public static class SavePatch
{
    private static readonly Logger Log = LogManager.GetCurrentClassLogger();

    public static void Prefix()
    {
        Log.Info("Hello world!");
    }
}

Apply the assembly's patches during plugin initialization:

C# / apply patches
public void Init(object gameInstance)
{
    new Harmony("TestPlugin").PatchAll(typeof(Plugin).Assembly);
}

Read the Harmony documentation before replacing a method. Prefixes and postfixes are usually enough; transpilers are harder to keep compatible across game updates.

Build and publish

Plugins are distributed as NuGet packages. Client plugins use the CringePlugin package type; dedicated plugins use CringePluginDedicated and are only loaded in --dedicated mode.

There are two supported routes:

  • Official PluginHub — the recommended route for public plugins. The launcher's built-in browser can see the package, and users get launcher-managed updates. Submissions are reviewed and must follow the publishing guidelines.
  • Your own NuGet source — add a source and package entry to the launcher's packages.json. You control distribution and updates, and you are responsible for hosting, versioning and compatibility.

The official guidelines and the current submission rules live on the wiki. The short version follows.

Publishing rules

  • Public, human-readable source code hosted on GitHub. No obfuscated or minified source.
  • Reasonable performance. A rendering plugin may cost more, but it must be justified by what it does.
  • Do not modify, restrict or extend the APIs available to in-game mods and scripts.
  • Touch only the files your plugin needs. Never modify core game files, mod assets or unrelated data.
  • No multiplayer gameplay advantages: no extended ore detectors, mass inventory automation or automatic jump-drive chains.
  • No loader modifications, external executables, process control or auto-update logic.
  • Native libraries must be open source or from a trusted signed source, and distributed through NuGet.
  • Comply with the Keen Software House EULA.
The launcher owns updates. Do not implement a separate updater inside a plugin. Version and publish through your NuGet feed or the official PluginHub, and let the launcher resolve the package.

Questions about a submission, a compatibility problem or an API that is not documented yet? Ask in the Discord server. The wiki has the longer guides on decompiling the game, the config system, ImGui and Harmony.