DistantHorizonsSupportPumpkin v0.0.12
Unofficial Distant Horizons server-side support for Pumpkin
See the plugin guide and API reference on the documentation site.
Installation
Put distant-horizons-support-pumpkin.wasm from the plugin's GitHub release in your server's plugins/ folder and restart. Hot reload cannot ask for permissions, so the first load needs a restart.
Permissions
Pumpkin asks for these on the server console the first time the plugin loads.
| Permission | Why |
|---|---|
fs.read.data | Read settings and cached LOD terrain. |
fs.write.data | Write settings and persist captured LOD terrain. |
http.outbound | Check Pumpkin Market for plugin updates. |
Commands
| Command | Description | Permission | Default access |
|---|---|---|---|
/dhs status | Show connected DH clients and pending requests | DistantHorizonsSupportPumpkin:command.dhs.status | operators (level 3) |
/dhs cache status | Show memory and disk cache usage | DistantHorizonsSupportPumpkin:command.dhs.cache.status | operators (level 3) |
/dhs cache clear | Clear both cache tiers | DistantHorizonsSupportPumpkin:command.dhs.cache.clear | operators (level 3) |
/dhs cache memory clear | Clear the in-memory cache | DistantHorizonsSupportPumpkin:command.dhs.cache.memory.clear | operators (level 3) |
/dhs cache disk clear | Clear the disk cache | DistantHorizonsSupportPumpkin:command.dhs.cache.disk.clear | operators (level 3) |
/dhs cache recover <x> <z> | Back up and rebuild one cached LOD section from loaded terrain at block coordinates | DistantHorizonsSupportPumpkin:command.dhs.cache.recover | operators (level 3) |
/dhs map | Show cached LOD sections at your position or block coordinates (default radius 4; maximum 16_384 sections) | DistantHorizonsSupportPumpkin:command.dhs.map | operators (level 3) |
/dhs map <radius> | Show cached LOD sections at your position or block coordinates (default radius 4; maximum 16_384 sections) | DistantHorizonsSupportPumpkin:command.dhs.map | operators (level 3) |
/dhs map <x> <z> | Show cached LOD sections at your position or block coordinates (default radius 4; maximum 16_384 sections) | DistantHorizonsSupportPumpkin:command.dhs.map | operators (level 3) |
/dhs map <x> <z> <radius> | Show cached LOD sections at your position or block coordinates (default radius 4; maximum 16_384 sections) | DistantHorizonsSupportPumpkin:command.dhs.map | operators (level 3) |
/dhs generate | Force-build LOD sections at your position or block coordinates (default radius 0; maximum 16_384 sections) | DistantHorizonsSupportPumpkin:command.dhs.generate | operators (level 3) |
/dhs generate <radius> | Force-build LOD sections at your position or block coordinates (default radius 0; maximum 16_384 sections) | DistantHorizonsSupportPumpkin:command.dhs.generate | operators (level 3) |
/dhs generate <x> <z> | Force-build LOD sections at your position or block coordinates (default radius 0; maximum 16_384 sections) | DistantHorizonsSupportPumpkin:command.dhs.generate | operators (level 3) |
/dhs generate <x> <z> <radius> | Force-build LOD sections at your position or block coordinates (default radius 0; maximum 16_384 sections) | DistantHorizonsSupportPumpkin:command.dhs.generate | operators (level 3) |
Configuration
Settings live in plugins/data/DistantHorizonsSupportPumpkin/config.toml. The plugin creates the file if needed, adds settings from the schema and removes settings no longer defined there. Values for existing settings are preserved.
| Option | Type | Default | Description |
|---|---|---|---|
support.server_key | string | "" | Optional globally unique server key. Empty uses the client connection address. |
support.render_distance | integer | 128 | Maximum LOD request radius in chunks, limited by the world border. |
support.generation_requests_per_second | integer | 20 | Maximum terrain generation requests per second advertised to each DH client. DH uses this for pacing and concurrency; pending_requests also caps it. |
support.sync_requests_per_second | integer | 50 | Maximum cached LOD synchronization requests per second advertised to each DH client. DH uses this for pacing and concurrency; pending_requests also caps it. |
support.requests_per_player | integer | 2 | Maximum active terrain captures per player. Misses waiting for a slot and cached responses bypass this limit. |
support.pending_requests | integer | 16 | Maximum in-flight DH requests and queued responses across all players. |
support.blocks_per_tick | integer | 8192 | Maximum block samples per server tick across all LOD requests. Actual work adapts to server MSPT. |
support.packets_per_tick | integer | 2 | Maximum transfer packets for newly captured LODs per server tick across all players. |
support.cached_requests_per_tick | integer | 8 | Maximum cached LOD requests checked per server tick across all players. |
support.cached_packets_per_tick | integer | 64 | Maximum transfer packets for cached LODs per server tick across all players. |
support.memory_cache_entries | integer | 512 | Maximum LOD sections cached in memory. Set to 0 to disable; any negative value means unlimited. |
support.disk_cache_entries | integer | 4096 | Maximum LOD sections cached on disk. Set to 0 to disable; any negative value means unlimited. |
support.refresh_seconds | integer | 30 | Rebuild cached sections after this age when all their chunks are loaded. |
worlds."<world>".height | integer | none | World height in blocks above its minimum Y. |
worlds."<world>".sample_biomes_3d | boolean | false | Sample the biome at every captured height instead of using the surface biome for the whole column. Adds one world lookup per sampled block. |
A fresh install gets this file:
# DistantHorizonsSupportPumpkin configuration.
# Changes apply after a server restart. Invalid files are left untouched.
# Distant Horizons Support limits.
[support]
# Optional globally unique server key. Empty uses the client connection address.
server_key = ""
# Maximum LOD request radius in chunks, limited by the world border.
render_distance = 128
# Maximum terrain generation requests per second advertised to each DH client. DH uses this for
# pacing and concurrency; pending_requests also caps it.
generation_requests_per_second = 20
# Maximum cached LOD synchronization requests per second advertised to each DH client. DH uses this
# for pacing and concurrency; pending_requests also caps it.
sync_requests_per_second = 50
# Maximum active terrain captures per player. Misses waiting for a slot and cached responses bypass
# this limit.
requests_per_player = 2
# Maximum in-flight DH requests and queued responses across all players.
pending_requests = 16
# Maximum block samples per server tick across all LOD requests. Actual work adapts to server MSPT.
blocks_per_tick = 8192
# Maximum transfer packets for newly captured LODs per server tick across all players.
packets_per_tick = 2
# Maximum cached LOD requests checked per server tick across all players.
cached_requests_per_tick = 8
# Maximum transfer packets for cached LODs per server tick across all players.
cached_packets_per_tick = 64
# Maximum LOD sections cached in memory. Set to 0 to disable; any negative value means unlimited.
memory_cache_entries = 512
# Maximum LOD sections cached on disk. Set to 0 to disable; any negative value means unlimited.
disk_cache_entries = 4096
# Rebuild cached sections after this age when all their chunks are loaded.
refresh_seconds = 30
# Per-world terrain capture settings. Vanilla dimensions use their standard heights; unknown
# dimensions require a height override.
#
# [worlds."world"]
# height = 384 # World height in blocks above its minimum Y.
# sample_biomes_3d = false # Sample the biome at every captured height instead of using the surface biome for the whole column. Adds one world lookup per sampled block.Compatibility and terrain
Targets Distant Horizons 3.3.4, using network protocol 16 on Java Edition. Bedrock players do not open DH sessions. Install the Distant Horizons client mod separately. A client using another DH protocol is disconnected with an incompatibility message. The protocol format follows Distant Horizons core 3.3.4.
The plugin builds LOD sections covering 64 × 64 blocks across 16 server chunks. All 16 chunks must be loaded before sampling and remain available for each capture attempt. The plugin cannot load or generate distant chunks. Requests for unavailable chunks remain pending and retry with exponential backoff until the section is loaded or the client cancels; genuinely empty loaded terrain remains valid. By default, each vertical column uses its surface biome. Set sample_biomes_3d = true under that world in [worlds."<world>"] to sample cave and other vertical biomes; this adds a biome lookup for each sampled block. Player edits, crop growth and fire spread invalidate affected cached sections. Mutations without exact world and changed-position data from the pinned Pumpkin API may remain cached until refresh. Captures are limited to 131072 material segments per section, transfer packets carry at most 30000 data bytes; finite-bandwidth fragments fit within a 20-second credit window including the 13-byte protocol overhead; a finite client bandwidth bucket can accumulate one 30013-byte DH fragment message.
It captures material changes, caves, biomes and lighting from loaded terrain. Completed sections are cached according to the configured memory and disk limits; disk entries persist across restarts. Sections without a cache entry remain pending while their chunks are unavailable, with exponential backoff between availability checks. They can be captured once all required chunks are loaded; these requests still count against the configured pending-request limit. Sampling and transfers run within the configured tick budgets; an uncached section may take many ticks to complete. Terrain reads use Pumpkin's world-level accessors, avoiding weak chunk handles that can expire during a multi-tick capture. Air above the heightmap is collapsed without reading every empty block. Memory and disk cache limits are independent; their current defaults and how to disable them are listed in the generated Configuration section. When upgrading from earlier versions, the former shared cache limit is copied to both cache limits.
Pumpkin reports a missing chunk as WIT option-none; the pinned QuickJS runtime exposes that value as null, although generated TypeScript declarations say undefined. Both forms now block capture before any block sampling. A genuinely loaded section containing only air remains valid and is still cached. Existing DHP1 cache entries do not record whether their source chunks were loaded, so the plugin does not infer corruption from an all-air payload or remove entries automatically.
To rebuild one operator-selected server cache entry, run /dhs cache recover <x> <z> as a Java player, using block coordinates in the current world. The plugin checks that all 16 chunks for that LOD section are loaded before changing the cache. If any are unavailable, the command defers and preserves the entry. Otherwise it writes the selected DHP1 entry under cache-recovery/, records its key in the matching .dhm file, removes only that section from the active cache, and starts a forced capture. The backups remain in the plugin data directory and are not automatically pruned. This command changes only the server plugin cache; it does not edit a client's profile or cache. If chunks unload after preflight, the backup remains and the forced job will skip the unavailable capture rather than store fallback air.
The generated Commands section lists cache inspection, clearing, selected cache recovery, LOD map and forced-capture commands. Run /dhs map to see cached sections around your current position, or /dhs map <x> <z> to inspect block coordinates in your current world. Add <radius> after the command or coordinates to set the radius, up to 16_384 sections. Each map cell is one 64 × 64 block LOD section (four by four server chunks) for views up to radius 10; larger views are downsampled to 21 × 21 cells to fit in Minecraft chat. These commands need a player so the plugin can resolve the current world.
Use /dhs generate or /dhs generate <x> <z> to force a capture into the cache. Add <radius> after the command or coordinates to set the radius, up to 16_384 sections. Sections already in the cache are skipped; the command errors when every requested section is already generated. A forced job gets up to 32,768 block samples per tick and pauses ordinary capture sampling while it runs; cached request delivery and transfers continue. Progress is reported in chat about every 10%, and /dhs status shows the active job. Only one forced job can run at a time. This command captures terrain that is already loaded: it does not load or generate Minecraft chunks, and sections with unavailable chunks are skipped.
The configured blocks_per_tick is the maximum capture batch. The current controller adapts sample batches using Pumpkin's reported MSPT and the measured cost of earlier capture steps, with a 5 ms reserve; it allows no samples at 45 reported MSPT. On the pinned Pumpkin release, reported MSPT is measured before tick-end handlers, so that reserve does not bound this plugin's full handler cost. These existing limits remain provisional until the real-runtime comparison is complete. Cached LOD transfers continue while capture sampling is paused. /dhs status reports the capture budget, reported MSPT, this handler's last elapsed time, logical queued response bytes retained and DH packet bytes sent. At zero sample budget, uncached requests still receive a bounded availability preflight. Unavailable terrain stays pending and its availability checks back off exponentially; loaded requests remain queued without spending block samples. Partially built requests recheck availability before sampling resumes. The preflight uses the existing cached_requests_per_tick bound.
Finite client bandwidth settings shape fragment sizes. At 1 KB/s, each full DH fragment message uses at most 20,000 bytes of credit, including its 13-byte protocol envelope; this leaves 10 seconds of margin under the DH client's 30-second receiver-buffer expiry. Unsent fragments are resized when the client changes its rate. While a finite-rate transfer has an in-progress receiver buffer, the server finishes that transfer before sending another response for the same client; other clients continue to receive round-robin service. Cancellation discards a queued transfer and releases its retained payload.
Cached sections are refreshed after their configured age when their chunks are loaded. Otherwise the last cached capture is returned. Block placement and breaking invalidate the affected server cache; other world changes are picked up on a later refresh. Live push updates are disabled, and clients can synchronize cached terrain on login. Delete the plugin's cache folder while the server is stopped after replacing a world or changing its height. Vanilla world heights are assumed unless an override is configured.
Diagnosing pending requests
/dhs status includes worker ticks, capture progress, the sample budget, reported MSPT, the last tick-end handler elapsed time, queued responses and logical bytes retained, DH packet bytes sent, and served/rejected/cancelled request counts. Counts are cumulative since plugin load. Pumpkin's MSPT excludes this tick-end handler on the pinned server release.
The DH client's ETA is based on time since request submission, including queue wait; it is not a network-throughput measurement. Chunk ... is not loaded means the requested terrain is unavailable to capture, while LOD request limit reached means the bounded request queue is full. A configuration change receives an independently negotiated generation/sync acknowledgement; the server's response also carries the client's bandwidth limit for outgoing transfers. Zero bandwidth means unlimited.
Pending requests can persist while the client continually submits new sections. Increasing worker ticks and capture progress show that the worker is advancing; increasing rejections with Chunk ... is not loaded mean that the requested sections cannot be captured yet. Previously captured sections can still be served from cache, including while forced capture is active. If worker ticks stay at zero, the worker is not being dispatched. Increasing cancellations indicate that the client is discarding requests.
Attribution and license
This is an unofficial Pumpkin adaptation of the Distant Horizons server plugin, originally developed by Jim C K Flaten and the upstream contributors. It is not an official Distant Horizons Team release or endorsed by that team.
Protocol code that follows upstream implementations retains its upstream copyright notices. This plugin is licensed under GPL-3.0-or-later. The separate @pumpkin-plugins/terrain package is MIT-licensed and contains protocol-neutral terrain capture and chunk-access code; it does not include the DH codec or DTO encoder. The protocol format follows the Distant Horizons core linked in the generated compatibility section, Copyright (C) 2020 James Seibel, originally under LGPL-3.0-only. The initial server-plugin reference revision is 56a01110579b94c9946130092500c00df7c31925.
TODO
- [ ] Connect the shared
ChunkLoaderandTerrainGeneratorproviders when Pumpkin exposes those APIs. The current adapter reports loading and generation as unavailable, and waits when a future provider reports pending work.