Use when working with the Distant Horizons Support (DHS) server plugin — configuring LOD generation, running or debugging pregen, understanding chunk lifecycle, diagnosing stalls or performance issues, or deciding config values like generate_new_chunks, builder_type, scheduler_threads
How this skill is triggered — by the user, by Claude, or both
Slash command
/minecraft-papermc-server:dhs-referenceThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Reference for the DHS server plugin (v0.12.0). Source: `https://gitlab.com/distant-horizons-team/distant-horizons-server-plugin`
Reference for the DHS server plugin (v0.12.0). Source: https://gitlab.com/distant-horizons-team/distant-horizons-server-plugin
DHS generates Level-of-Detail (LOD) terrain data server-side and sends it to Distant Horizons clients via plugin messaging. Clients render distant terrain from these LODs instead of loading full chunks. LODs are stored in SQLite (plugins/DHSupport/data.sqlite).
Client requests LOD (or pregen spiral reaches position)
|
v
Check SQLite for existing LOD --> found? return it
| not found
v
Load 4x4 = 16 chunks for this section (64x64 blocks)
|-- generate_new_chunks=true: getChunkAtAsync(x, z, true) [generates if missing]
|-- generate_new_chunks=false: getChunkAtAsync(x, z, false) [load only, reject if missing]
v
All chunks loaded? (CompletableFuture.allOf)
|-- Any rejected + no dummy_chunk config: abort, return null
|-- Any rejected + dummy_chunk configured: switch to dummy mode
v
Queue builder on worker thread pool (DHSupport-Worker-N)
|-- FullBuilder: scans entire Y column, captures all detail
|-- FastOverworldBuilder: stops at solid ground, skips deep underground
v
Encode LOD data (LZMA2 compression) + beacon data
v
Save to SQLite (single-threaded async executor)
v
Discard the 16 loaded chunks (unloadChunk(x, z, false) -- no save)
Each LOD covers a 64x64 block section at detail level 6. The section coordinate system is blockCoord / 64.
/dhs pregen start <world> <centerX> <centerZ> <radius> [force]
radius is in chunks (converted to blocks internally)(radius/2)^2 — e.g., radius 625 = 97,656 LODsforce is specifiedfull_data_request_concurrency_limit (default 20)CompletableFuture.anyOf(requests).join() when at capacity/dhs pregen status <world> shows:
"Time remaining: infinite" means momentary CPS is 0 (division by zero). The pregen may still be running.
Silent death (no try-catch): PreGenerator.run() has no exception handling. If any LOD generation throws, CompletableFuture.anyOf(...).join() propagates a CompletionException that kills the worker thread. The run flag stays true, so status falsely reports "running" while the thread is dead. Verify by checking for DHSupport-Worker-* threads in jstack, or check if data.sqlite mtime is advancing.
No persistence across restarts: Pregen state is in-memory only. Server restart = pregen lost. Re-running skips already-generated LODs (fast), so progress isn't lost, just the running task.
Stale CPS display: The "5.35 CPS" in status can be a cached value from the last active period. Check dhs status (global) for the real-time Current generation speed.
See references/config-and-internals.md for full code-traced details on every option.
| Config | Default | What It Actually Does |
|---|---|---|
generate_new_chunks | true | true = getChunkAtAsync(x,z,true) generates missing chunks. false = load-only, rejects missing chunks. Chunks are discarded after LOD build (not saved to world) |
builder_type | FullBuilder | FullBuilder: full Y scan, all detail, slower. FastOverworldBuilder: stops at solid ground, ~2-3x faster, good for overworld. None: disables building |
scheduler_threads | 4 | Worker thread pool size for LOD generation. Also used by pregen thread itself |
full_data_request_concurrency_limit | 20 | Max in-flight LOD builds during pregen. Higher = more parallel chunk loads |
render_distance | 1024 | Max LOD render distance (chunks). Clients capped to this |
real_time_updates_enabled | true | Push LOD updates when blocks change near players |
real_time_update_radius | 128 | How close (blocks) a player must be to receive live updates |
lod_refresh_interval | 15 | Seconds between processing world-change LOD invalidations |
material_map | (ores/plants) | Block substitution during LOD build (e.g., hide diamond ore as stone) |
dummy_chunk | disabled | Fallback column data when chunks can't be loaded |
border_center_x/z, border_radius | - | Custom LOD generation boundary (per-world) |
trust_height_map | true | false = ignore heightmap, return maxY-1 for all blocks (debug escape hatch) |
sample_biomes_3d | false | true = sample biome at each Y level (for 3D biome worlds like nether) |
fast_underfill | true | (FastOverworldBuilder) Extend bottom block to y=0 for visual completeness |
scan_to_sea_level | false | (FastOverworldBuilder) true = keep scanning down to sea level even after hitting ground |
| World Type | Recommended Builder | Why |
|---|---|---|
| Overworld (vanilla) | FastOverworldBuilder | 2-3x faster; solid ground assumption holds |
| Nether | FullBuilder | No "solid ground" — need full scan for caves/lava |
| End | FullBuilder | Floating islands need full scan |
| Custom (floating islands) | FullBuilder + scan_to_sea_level: true | Prevents underfill from hiding overhangs |
| Datapack terrain | FullBuilder | Custom generation may violate fast builder assumptions |
ThreadPoolExecutor (core/max = scheduler_threads). Used for LOD builds AND the pregen loop itself. With scheduler_threads: 8, pregen occupies 1 thread leaving 7 for builders — so full_data_request_concurrency_limit above 7-8 just queues futures without increasing parallelism. Pool uses allowCoreThreadTimeOut(true) with 60s timeout — idle worker threads are destroyed after 60 seconds. This means after a pregen stall or completion, all DHSupport-Worker threads disappear. Zero workers in jstack = pool timed out (strong signal the pregen thread died)canReadWorldAsync()=true allows off-thread reads)runOnRegionThread maps to Bukkit.getScheduler().runTask()DhSupport.java:414): All 16 chunk loads use key worldX+"x"+worldZ instead of chunkX+"x"+chunkZ. Only last chunk tracked; 15 orphaned. Chunk discard only cleans 1 of 16PreGenerator.java): No try-catch in run(). Any exception kills thread silentlyBukkit.getPlayer() can return null if player disconnects mid-requestresult.next() return value not checked; NPE if no rows| Command | What It Shows |
|---|---|
dhs status | Global: generation enabled, builder type, connected DH players, real-time CPS |
dhs status <player> | Player's DH config, LODs sent count |
dhs pregen status <world> | Progress %, completed/total, elapsed, ETA |
dhs pregen start <world> <cx> <cz> <radius> [force] | Start pregen (radius in chunks) |
dhs pregen stop <world> | Stop pregen |
dhs pause / dhs unpause | Pause all LOD generation globally |
dhs reload | Reload config.yml |
dhs worlds | List all worlds |
dhs trim <world> <cx> <cz> <radius> | Delete LODs outside area |
dhs bench <world> [iterations] [concurrency] | Benchmark LOD build speed |
dhs pregen status <world> — is it "running"?dhs status — check Current generation speed. If 0.00 CPS but status says running, pregen thread is likely deadjstack <pid> | grep DHSupport — are worker threads alive? Zero threads = pool timed out after thread deathdata.sqlite mtime — is it advancing? (stat plugins/DHSupport/data.sqlite)dhs pregen stop <world> then dhs pregen start — it skips existing LODsreferences/config-and-internals.md — Full config tracing, threading details, client protocol, database schema, all bugs with code referenceshttps://gitlab.com/distant-horizons-team/distant-horizons-server-pluginnpx claudepluginhub disqt/minecraft-claude-marketplace --plugin minecraft-papermc-serverGenerates Dungeondraft battle maps with procedural terrain, rectangular rooms, corridors, and polygon layouts from scene descriptions via YAML configs.
Tests RHDH plugins locally using rhdh-local-setup. Manages plugin lifecycle, modes, health checks, and troubleshooting.
Guides Minecraft server plugin development using Bukkit, Spigot, and Paper APIs, covering events, commands, NMS internals, performance optimization, and integrations with databases and Docker.