Submodules and entry points

A converter gives authors PM:Patch("MyThing", ...). That works, but it is the raw form. The stock content gets nicer entry points: PM.Parts:Patch(...) in Lua and Patching.Mod.PatchPart(...) in C#, each with the converter and label filled in. This page covers adding those for your own content.

A typed Lua submodule

To give authors PM.MyThing:Patch(...), add a submodule. Mark a class with [PatchManagerModule("MyThing")] and [MoonSharpUserData], and give it the required constructor (a PatchManagerCore followed by a Universe):

using MoonSharp.Interpreter;
using PatchManager.LuaPatching;
using PatchManager.LuaPatching.Attributes;
using PatchManager.LuaPatching.Builtin;

[PatchManagerModule("MyThing")]
[MoonSharpUserData]
public class MyThingLuaModule
{
    private readonly PatchManagerCore _core;
    private readonly Universe _universe;

    public MyThingLuaModule(PatchManagerCore pmc, Universe universe)
    {
        _core = pmc;
        _universe = universe;
    }

    public PatchDefinition Patch(ScriptExecutionContext context, string name)
        => _core.Patch(context, "MyThing", "my_label", name);
}

PatchManager instantiates one of these and exposes it on the global PM table under the name you gave, so authors write PM.MyThing:Patch("..."). A method that authors call takes a ScriptExecutionContext as its first parameter (the scripting layer supplies it automatically) followed by the author-facing arguments. This class is exactly how PM.Parts is built, so the stock submodules are the reference to copy. Add whatever creation or convenience helpers your content needs as more methods.

Pass the ScriptExecutionContext to _core. _core.Patch(context, ...) and _core.New(context, converter, label, name, value) record the calling mod and the open PM:WithFeature scope, if there is one. This record gates the patch or asset on the features of the author. Make each creation helper return the LuaAsset that _core.New returns. Authors can then add :Feature and :NoFeature to it. The New overload without a context creates an asset that no mod owns. No feature gates that asset.

A fluent C# entry point

To let C# patch authors write Patching.Mod.PatchMyThing("...") instead of the generic Patching.Build<T>, add an extension method on PmScope and a PatchAttribute subclass that bakes your converter and label. This mirrors exactly how the parts domain ships PatchPart and [PatchPart]:

using PatchManager.CSharpPatching;
using PatchManager.CSharpPatching.Attributes;

// The attribute, for the declarative [PMPatch] style.
public sealed class PatchMyThingAttribute : PatchAttribute
{
    public override string Converter => "MyThing";
    public override string Label => "my_label";
}

// The fluent extension, for the Patching.Mod style.
public static class MyThingPatchExtensions
{
    public static PatchBuilder<MyThingUserData> PatchMyThing(this PmScope scope, string name)
        => Patching.Build<MyThingUserData>(scope.ModId, "MyThing", "my_label", name);
}

With both in place, C# authors get the same two styles the stock domains offer. See Patching from C#.

Brand-new globals

Submodules hang off PM. If you need a brand-new global of your own, the way PM and J are top-level globals rather than members of something, that goes through the environment contributor mechanism instead of a submodule. It is a heavier extension point and rarely needed for content patching, but it is how the top-level globals get contributed to each patch script's environment. For content, a submodule under PM is almost always the right call, since it keeps everything a patch author touches under the one PM namespace they already know.

The full stack

Putting the three pages together, a complete custom content module is:

  1. A converter ([Converter]) so the content can be patched at all.

  2. Optionally, module data adapters for any of your own data types that benefit from typed access.

  3. A submodule ([PatchManagerModule]) and a C# entry point (PmScope extension plus PatchAttribute) so authors get polished, converter-filled-in entry points in both languages.

That is exactly the anatomy of the stock parts module, which is the best worked example to read.