Skip to content

Batch chunk requests and add region level-of-detail - #4

Open
gh8sted wants to merge 3 commits into
OurSources:mainfrom
gh8sted:batched-chunk-loading
Open

Batch chunk requests and add region level-of-detail#4
gh8sted wants to merge 3 commits into
OurSources:mainfrom
gh8sted:batched-chunk-loading

Conversation

@gh8sted

@gh8sted gh8sted commented Aug 15, 2026

Copy link
Copy Markdown

Chunk requests and responses were one packet each. A client loading a screen at low zoom sent thousands of 8-byte requests and got thousands of separate messages back. This batches both directions and adds a coarse region format so a zoomed-out view can be painted before full detail arrives.

Requires the matching client change: OurSources/owop-client#NN. The protocol changes on both sides — neither half works alone.

Batched chunk requests

New request packet: [u16 guard][u16 count][u16 reserved] followed by count × (i32 x, i32 y). The length is always 6 + 8*count, which no existing packet length can equal, so it is checked ahead of the length-based switch without disturbing any existing case.

Response 0x0B carries [u16 count] followed by count × ([u16 len][0x02 packet]), where each embedded packet is byte-identical to a standalone one.

Chunks in regions that aren't loaded yet turned out to be the important case. Those go through awaitRegionLoad, which sent one message per chunk, so batching the request alone changed almost nothing in practice — on a cold client 81% of chunks (3,328 of 4,096) still arrived individually. Consecutive deferred chunk loads are now gathered into one message, flushed before any other action so ordering relative to pastes and erases is unchanged.

Measured on a cold client over a 64×64 chunk grid: 157ms → 90ms, with every chunk batched instead of 3,328 individual messages. An 8,100-chunk view previously never completed at all, stalling at roughly 5,800; it now finishes in 90ms across 50 messages.

Region level-of-detail (0x0C)

One averaged colour per chunk, so a whole region is 768 bytes instead of the ~200KB its 256 chunks cost at full detail. The request is a fixed 16 bytes describing a rectangle of regions. The downsample is cached on the Region and invalidated in flagDataModified. Cold regions defer exactly like chunk requests do.

Verified against full detail: 16,384 chunk averages compared, 0 mismatches, maximum channel error 0.

Limits

  • Requests are capped at 4,095 chunks and 1,024 regions — past maxPayloadLength a frame cannot arrive intact anyway
  • Response messages are capped at 512KB (chunks) and 256KB (LOD) so a single request cannot force a multi-megabyte send

Notes

tools/fillWorld.js is a development utility for generating large test worlds. It is in its own commit and can be dropped if you would rather not carry it.

Two caveats on the numbers above, both worth knowing before reading too much into them:

  • Transfer figures come from a test world of per-pixel random noise, which defeats the run-length encoding completely (~780 bytes per chunk). Real world data compresses far better, so the byte counts here are pessimistic.
  • LOD's benefit scales with view size. On a small cold area it is only about 1.4× faster, because both paths still load the same regions from disk and that dominates. It eliminates network transfer and client-side decode, not disk I/O.

gh8sted and others added 3 commits August 15, 2026 01:36
Adds a batched chunk request packet and a batched response (0x0B) so a client
can ask for many chunks in one message instead of one packet each, including
on the deferred path where a cold region previously answered one chunk per
message.

Adds region level-of-detail (0x0C): one averaged colour per chunk, so a whole
region costs 768 bytes rather than the ~200KB its 256 chunks cost at full
detail. The downsample is cached per region and invalidated on modification.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Writes regions straight into the LevelDB store using the server's own saveData,
so a large world can be generated for benchmarking chunk loading. Supports a
configurable radius and either per-pixel noise (worst case for the encoding) or
random blocks (compresses like a real world).

Kept as a separate commit since it is a development tool rather than part of
the server proper.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cuts the added commentary down to wire formats and non-obvious invariants.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant