# The Do callback

`:Do` is where the change happens. Everything else in the chain decides when and whether your callback runs. The callback itself is handed the asset and changes it.

## You mutate in place

PatchManager calls your function once for each matched asset, passing the asset's current value. You modify that value directly. You do not return it, build a new one, or save anything. Whatever state the value is in when your function returns is what the next patch (and eventually the game) sees.

```lua
PM.Parts:Patch("EVAChuteMassless")
        :Named("parachute_1v_eva")
        :Do(function(part)
            part.mass = 0.0
            part.hideFromFlightPartsManager = true
        end)
```

## Called once per asset

If your patch matches several assets, the callback runs once for each, with that asset as the argument. This lets one patch handle a family of assets and branch on the one it was given:

```lua
local fairingInterstageLengthMaximums = {
    fairing_0v_procedural = 2.5,
    fairing_1v_procedural = 5.0,
    fairing_2v_procedural = 10.0,
    fairing_3v_procedural = 15.0,
    fairing_4v_procedural = 20.0
}

PM.Parts:Patch("ConfigureFairingInterstageNodes")
        :Named("fairing_0v_procedural", "fairing_1v_procedural", "fairing_2v_procedural",
               "fairing_3v_procedural", "fairing_4v_procedural")
        :Do(function(part)
            local max = fairingInterstageLengthMaximums[part.partName]
            -- ... use max for this particular fairing ...
        end)
```

The callback reads a field off the asset it was handed (`part.partName`) to look up the right value. The same code runs for all five fairings, setting the correct values for each.

## Reading and writing fields

Inside the callback the value behaves like a table. Read a field to inspect it, assign to set it. Numbers, strings, and booleans assign the way you expect:

```lua
part.crashTolerance = 30.0
part.atmosphericReentryVFXGradient = "Assets/.../EveReentry.png"
data.AllowInterstageNode = true
```

Nested values (vectors, colors, sub-objects) are tables too:

```lua
body.MineDustColor = { x = 0.53, y = 0.39, z = 0.29, w = 1.0 }
node.TechTreePosition = { x = 1600.0, y = 750.0 }
```

Lists have methods for adding, removing, and iterating, and a few JSON quirks are worth knowing (integers, empty arrays). Those are covered in [Working with JSON](/doc/working-with-json-fWfY8KrejF).

## Deleting an asset

Most callbacks return nothing. The one meaningful return value is the string `"remove"`, which tells PatchManager to delete the asset entirely:

```lua
:Do(function(asset)
    return "remove"
end)
```

Return exactly the lowercase string `"remove"`. Anything else, including returning nothing, keeps the asset. A removed asset is dropped, so patches that would have run against it afterward will not.

## You cannot register patches here


:::warning
Do not call `PM.Parts:Patch(...)` or `PM:New(...)` from inside a `Do` callback. Register all your patches at the top level of your script.

:::

The callback runs during asset loading, well after the registration step where your script first ran, so registration is already closed by the time it runs and the call fails. See [File layout and discovery](/doc/file-layout-and-discovery-vYcsLN5rtw).

## Keep callbacks honest

A callback should change the asset and nothing else. It runs as part of a build step that PatchManager [caches](/doc/caching-and-invalidation-QifFi4oev9), so side effects that reach outside the asset (writing files, depending on the current time, talking to other systems) will not behave the way you expect across cached and uncached runs. Read your inputs from the asset. Write your output to the asset. This also applies to a [config](/doc/config-integration-aTM54Tfqkz) value. The cache never rebuilds when a setting changes, so a callback that reads a setting gives a stale result. If content must be different between campaigns, put it in a [feature](/doc/features-geboWdIiDN). Gate the patch on the feature. Do not examine the feature inside the callback.

Though, there are cases where storing state in your patch file from a `:Do` callback can be a good idea, that is detailed in the [Patch lifecycle](/doc/the-patch-lifecycle-bV7YhyoMv8), but even then that data should only be used for later patching or stuff that otherwise does not matter if it is a `cached` or `uncached` run. Reset that state with `PM:OnBeginRun`, because the same script serves the run of each feature set.