Extend terrain storage
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.Only recording matches? Use the recording service. You do not need a custom terrain provider just to capture an arena or select participants.
Storage provider
ImplementChunkStorageProvider 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 getWorldSource() when its own platform also supplies live snapshots.
Implementing this interface alone does not install a backend into Reverse. Optional chunk factories are explicitly wired into the runtime’s provider chain; there is no third-party service scan. The storage backend selected by storage.backend uses the distinct StorageProviderFactory/StorageBackend contracts and the built-in StorageFactory registry described in catalog and storage. The public facades expose borrowed stores, not a universal provider-replacement method.
World source
ImplementWorldSource 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. The current SnapshotSession contract exposes chunk reads and initial payloads only: it has no close() method and is not AutoCloseable. Do not write integration code that assumes stopping a recording invokes a snapshot-close hook; resource-owning source implementations need explicit lifecycle ownership in the runtime wiring.
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.

