> For the complete documentation index, see [llms.txt](https://docs.marioded.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.marioded.com/advanced-integrations/terrain-providers.md).

# Terrain providers

Implement a terrain backend or a consistent world snapshot source at the runtime boundary.

**Advanced integration** · For runtime/backend implementers

Terrain integrations have two distinct responsibilities: **where snapshots are stored** and **how a consistent initial world snapshot is obtained**. Choose the correct contract instead of combining both in an application-specific recording service.

| Responsibility                              | Contract               |
| ------------------------------------------- | ---------------------- |
| Store and retrieve chunk payloads           | `ChunkStorageProvider` |
| Recognize and snapshot a live world         | `WorldSource`          |
| Keep the world as it was at recording start | `SnapshotSession`      |

{% hint style="info" %}
**Only recording matches?** Use the [recording service](/api-how-to-guides/recording.md). You do not need a custom terrain provider just to capture an arena or select participants.
{% endhint %}

## Storage provider

Implement `ChunkStorageProvider` for a terrain backend. It identifies worlds by a string and chunks by `ChunkKey`. Core operations include existence checks, asynchronous load/save, listing chunk keys, flush, finalize-world, and asynchronous close.

The provider must be thread-safe. A missing chunk is represented by a null payload from `getChunk`, not a fabricated all-air chunk that silently changes the scene. Propagate failures through futures so the recording can report degraded terrain instead of claiming a good save.

`ChunkPayload`/`ChunkSectionData` describe protocol-neutral chunk content, with `BlockEntityPayload` for block entities and `TerrainIndex` for chunk-hash mappings and vertical bounds. Do not persist a playback server's version-specific block-state IDs in those shared contracts.

## Ownership routing

`ownsWorld(identifier)` is a synchronous ownership claim. Claim only identifiers your provider can serve completely. Do not infer a backend from UUID syntax, a `template:` prefix, or a filename-shaped string.

The router selects a provider per world. The first provider is the write target for registration, chunk writes, finalization, and flush. Reads go to the first claimant or fall back to the write target. It does not merge missing individual chunks across several providers.

Declare a read-only provider with `isReadOnly()` where appropriate. Complete pending operations during close and define your own resource ownership clearly.

## Factory and source contribution

`ChunkStorageProviderFactory.create()` returns an optional provider, allowing a backend whose external platform is absent to contribute nothing. A provider can expose an optional `worldSource()` when its own platform also supplies live snapshots.

Implementing this interface alone does not install a backend into Reverse. The public entry points do not expose a universal provider-replacement method. Use the contracts when building a compatible extension and check the integration hooks supported by your release before promising a new storage option to users.

## World source

Implement `WorldSource` when you can take an isolated initial snapshot of a Bukkit world. `supports(world)` should claim only worlds genuinely served by that source. Sources are tried by specificity, with the universal Bukkit source last.

`beginSnapshot(world, bounds)` runs on the main thread and returns a caller-owned `SnapshotSession`. Null bounds retain the source's normal world selection. Nonnull bounds restrict horizontally intersecting chunk columns; payloads keep full height. Filter before expensive conversion, and do not load or generate Bukkit chunks as a side effect of capture.

## Snapshot session

The key promise is t0 consistency: `getChunk` returns the chunk as it existed when the session began, even if the world changes later. Returning the current mutable world chunk after a delay violates this contract.

Possible implementation strategies depend on the platform: eager immutable conversion, an isolated platform clone, or copy-on-write coordinated with pre-mutation events. Whatever strategy you use must obey bounds on eager and lazy paths and close its resources when the session ends or setup fails.

`initialChunks()` can expose eagerly captured payloads. Async reads must not query unsafe Bukkit world state from arbitrary pool threads. Copy or snapshot on the required thread, then perform backend I/O asynchronously.

## Template behavior

An explicit nonempty template is selected at recorder start and bypasses live capture. World sources do not decide whether a user meant “template.” Template selection is session state; the replay persists the terrain identifier needed for playback.

Do not add a second manifest lineage format to communicate a template origin. If your integration needs catalog-level business metadata, design it at the catalog/application layer rather than overloading terrain-routing identifiers.

## Testing a provider

Verify missing worlds/chunks, two concurrent sessions on the same live world, mutation after t0, negative coordinates, bounded capture, backend failure during finalization, cleanup after failed startup, and playback on a machine without the original live world. A source that only works because the recorder machine's world folder still exists does not meet the portable-terrain use case.


---

# 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.marioded.com/advanced-integrations/terrain-providers.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.
