# Module data adapters

A [part module](/doc/module-data-OTSWqA1Rgt) holds data entries (`Data_Engine`, `Data_Drag`, and so on). By default a patch author reaches a data entry as raw JSON. A module data adapter gives one of those data types a strongly-typed Lua wrapper instead, the way `Data_Engine` gets the typed `engineModes` helper rather than a bare array. If your C# mod adds a new part module with its own data type, an adapter is how you give patch authors first-class access to it.

## Do not adapt stock game types

Read this before anything else.


:::warning
**Never register a module data adapter for a stock game data type.** Adapters for the base game's data types (`Data_Engine`, `Data_Deployable`, and every other type Redux ships) are owned by Redux. The whole modding ecosystem patches those types through the contract Redux's adapters define. If your mod registers its own adapter for a game type and changes that behavior, you change the contract out from under every other mod that relies on it, and you break them. There is no per-mod isolation here: an adapter is registered globally against the data type, so the last one to register wins and everything breaks.

:::

Register adapters **only for data types your own mod defines.** Those are yours to define. If you want typed access to a stock data type that Redux has not adapted yet, that is a request to Redux, not something to do yourself.

## Writing an adapter for your own data type

An adapter is a `[MoonSharpUserData]` wrapper carrying `[ModuleDataAdapter(typeof(YourDataType))]`, with a constructor that takes the data entry's `JObject`. This is the stock `Data_Engine` adapter, shown as the form to follow:

```csharp
[MoonSharpUserData]
[ModuleDataAdapter(typeof(Data_Engine))]
public class EngineUserData : ExtensibleJsonUserData
{
    private DynValue _modes;

    public EngineUserData(JObject moduleData) : base(moduleData)
    {
        _modes = UserData.Create(new ModesUserData(RequireArray(Token["engineModes"], "engineModes")));
    }

    public override IEnumerable<string> GetExtraAndOverriddenKeys()
    {
        yield return "engineModes";
    }

    public override DynValue TryToGet(string property) =>
        property == "engineModes" ? _modes : null;

    // ... TryToSet / TryToRemove as needed ...
}
```

The pattern: derive from `ExtensibleJsonUserData` so all the raw JSON fields still work, then override the handful of keys you want to surface as typed helpers. `GetExtraAndOverriddenKeys`, `TryToGet`, `TryToSet`, and `TryToRemove` let you intercept specific properties (here `engineModes`) and hand back a typed wrapper, while everything you do not intercept falls through to the normal JSON behavior. `[ModuleDataAdapter(typeof(...))]` can list more than one data type if one adapter fits several.

PatchManager discovers the adapter at load and uses it whenever the parts converter encounters a matching data entry, so a patch author's `module:PatchData("YourDataType", function(data) ... end)` receives your typed wrapper instead of raw JSON.

## When to bother

An adapter is worth it when your data type has a structure that is awkward as raw JSON: a list that should be addressable by name, a nested form that benefits from helper methods. If your data type is a flat bag of fields, the raw JSON access authors already get is fine, and an adapter adds little. Adapt when it genuinely makes the author's patch clearer, and only ever for your own types.