Skip to content

Load jump chunks over multiple server ticks#558

Draft
frostdev-ops wants to merge 1 commit into
LemADEC:MC1.12from
frostdev-ops:perf/jump-chunk-batching
Draft

Load jump chunks over multiple server ticks#558
frostdev-ops wants to merge 1 commit into
LemADEC:MC1.12from
frostdev-ops:perf/jump-chunk-batching

Conversation

@frostdev-ops

Copy link
Copy Markdown

Experimental motivation

Forcing every source and target chunk in one server tick can turn jump preparation into a watchdog-sized spike, especially when target terrain must generate.

Scope

  • Orders source and target chunks center-out.
  • Forces and releases at most G_CHUNKS_PER_TICK chunks per tick.
  • Waits for forced target chunks to report loaded.
  • Uses getLoadedChunk before skylight work and removes the (0,0) anchor ticket.
  • Adapts sequencer state returns and DeploySequencer; no navigation target or callback logic is included.

Evidence and limitations

  • Java 8 offline Gradle build: passed (test NO-SOURCE).
  • All pairwise merges and both aggregate build orders passed.
  • Tick-time and total-preparation benchmarks have not been run in a live Forge server yet. This remains draft until several ship footprints are measured and abort paths prove that every ticket is released.

Manual testing pending

Same-world and cross-dimension jumps, large ships, delayed target generation, every abort path, deployment, and ticket-leak inspection.

@frostdev-ops
frostdev-ops force-pushed the perf/jump-chunk-batching branch from 0b58eae to 641788e Compare July 11, 2026 16:34
@LemADEC

LemADEC commented Jul 14, 2026

Copy link
Copy Markdown
Owner

When forcing a chunk that isn't currently loaded, it doesn't load the chunk immediately, it just mark it to be persistent. It'll only load when you actually access it. As such, this change only slows down the sequencing, with no added benefits.
By extension, changing the chunk order only change the unloading sequence if we were to go above the limit which was checked earlier, so no effect expected here.
Also of note, a chunk never unloads immediately, as such covering getPossibleJumpDistance which a chunkloading ticket doesn't really help.

What you seem to describe is more likely either:
1- A general world generation issue : the mod loads target chunks but it takes time for them to generate, especially in space where generation is frequently rescheduled and takes its sweet time.
2- A known weakness in getPossibleJumpDistance which is already split over several tick to reduce the load, but still causes issues in some edge cases.

On other notes, I see you've changed the state_* methods so they all return values. That's a relatively impacting changes (many lines) that also duplicates the information already provided through properties of the object. So it seems only justified for your 2 modified methods. In such cases, instead of breaking all others, consider limiting to those 2 methods or, preferably, embrace the existing structure by adding a state check or non-persistent property.

To move further, I suggest you capture the log when the issue happens, there's profiler measures and state progression logged in there that'll tell you exactly what step is taking too much time.

Computing the possible jump distance and the collision checks read
target world blocks, which loads or generates every chunk on the path
synchronously inside a single tick. Jumping a large ship into an
ungenerated area could freeze the server long enough to trip watchdogs.

Add a PRELOAD_TARGET_CHUNKS sequencer state that loads or generates the
target envelope at a configurable chunks_per_tick pace, and delay the
jump distance check until the path is loaded. Chunk forcing tickets are
unchanged: they only mark chunks persistent and were never the cost.
@frostdev-ops
frostdev-ops force-pushed the perf/jump-chunk-batching branch from 641788e to 5a437a0 Compare July 15, 2026 02:35
@frostdev-ops

Copy link
Copy Markdown
Author

Checked the Forge source and you're right, forceChunk just adds the chunk to the ticket's requestedChunks and the forcedChunks multimap, nothing loads. So the old version of this PR was spreading out bookkeeping, not cost. My bad.

I've reworked it from scratch around your hypothesis 1, since that matches what I was actually seeing: getPossibleJumpDistance() and the collision checks read target-world blocks, and every getBlockState on an ungenerated chunk generates it synchronously. For a short-range space jump that's the whole path envelope inside one tick.

The new version is much smaller:

  • one new PRELOAD_TARGET_CHUNKS state between GET_INITIAL_VECTOR and ADJUST_JUMP_VECTOR that calls world.getChunk() (load-or-generate) over the target envelope at a configurable chunks_per_tick pace (default 4)
  • the getPossibleJumpDistance() call moved after the preload, so it runs against loaded chunks
  • chunkloading tickets untouched - single pass, exactly as before
  • reverted the state_* signature changes; only the new state returns a progress boolean, DeploySequencer untouched

Still owe you the log capture with the profiler numbers - I'll grab one from a jump into ungenerated space on the rebased build and post it here.

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.

2 participants