> For the complete documentation index, see [llms.txt](https://docs.onelitefeather.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.onelitefeather.net/background/how-a-brush-stroke-is-applied.md).

# How a brush stroke is applied

A right-click with the brush item goes through the steps below. The flowchart shows the order of the checks and the classes that perform them.

```mermaid
flowchart TD
    click["Right click with an item<br/>InteractListener.onClick"] --> use{"Has bettergopaint.use?"}
    use -- no --> stop1["Nothing happens"]
    use -- yes --> target["Target: clicked block<br/>or ray trace of 250 blocks"]
    target --> world{"World disabled and no<br/>bypass permission?"}
    world -- yes --> stop2["Nothing happens"]
    world -- no --> source{"ExportedBrushes.read:<br/>item stores brush settings?"}
    source -- yes --> stored["Settings stored on the item"]
    source -- no --> default{"Item is the<br/>default brush?"}
    default -- yes --> own["The player's PlayerBrush"]
    default -- no --> stop3["Nothing happens"]
    stored --> empty{"Palette empty?"}
    own --> empty
    empty -- yes --> stop4["Nothing happens"]
    empty -- no --> enabled{"Brush enabled?"}
    enabled -- no --> msg["Message: brush disabled"]
    enabled -- yes --> capture["StrokeContextFactory.capture<br/>on the player's thread"]
    capture --> prepare["Brush.prepare<br/>Paint Brush: collect the point,<br/>null unless sneaking"]
    prepare --> bind["Brush.bind<br/>Brush.Stroke"]
    bind --> run["actor.runAction<br/>StrokeRunner.run"]
    run --> session["createEditSession(actor)"]
    session --> build["Stroke.build: Brush.apply on<br/>masked(session, session.getMask())"]
    build --> remember["localSession.remember<br/>one history entry"]
```

The steps in order:

1. **Permission and world check.** The player needs `bettergopaint.use`. Worlds listed in `generic.disabled-worlds` are skipped unless the player has `bettergopaint.world.bypass`.
2. **Target.** Clicking a block uses that block. Clicking into the air ray-traces up to 250 blocks.
3. **Settings.** The default brush item uses the player's own brush. Any other item that stores brush settings uses them: the settings are in the item's data, and an item from BetterGoPaint 1.x keeps them in its lore until its first right-click converts it (see [Share a brush as an item](/for-builders/share-a-brush-as-an-item.md)). Any other item does nothing.
4. **Stroke context.** Still on the player's thread, the plugin captures everything the brush needs into an immutable stroke context: the target position, the position of the player's eyes (for the relative surface mode), a snapshot of the brush settings, the palette as a WorldEdit random pattern, the mask as a WorldEdit mask and a random generator seeded for this stroke. Materials are converted to WorldEdit block types here, once per stroke. The Paint Brush also collects its clicked points at this step and queues nothing until you sneak.
5. **Edit.** The stroke runs in WorldEdit's action queue, inside an edit session of the player's own WorldEdit session. The brush reads and writes blocks only through that edit session and never asks Bukkit for a world, block or location, so it is safe off the main thread. A brush reads everything it needs first and writes afterwards, so a block it has already changed cannot influence which block it picks next.

Because the stroke uses the player's WorldEdit session:

* **Undo.** The whole stroke is one history entry, so one `//undo` takes back everything a click painted.
* **Global mask.** A mask set with `//gmask` applies to brush strokes. With `//gmask grass_block` a sphere over grass and stone only changes the grass.
* **Limits.** The block change limit and the other FastAsyncWorldEdit limits of the player apply.

The BetterGoPaint mask from the brush menu is a separate, additional filter: with it enabled a stroke only changes blocks of the mask type. A block has to pass both masks.

Brush settings live in memory per player and are dropped when the player leaves the server.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.onelitefeather.net/background/how-a-brush-stroke-is-applied.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
