Skip to content

test: pre-generate the nether chunks the nether test touches - #4037

Closed
u9g wants to merge 1 commit into
masterfrom
test/cheap-nether-gen
Closed

test: pre-generate the nether chunks the nether test touches#4037
u9g wants to merge 1 commit into
masterfrom
test/cheap-nether-gen

Conversation

@u9g

@u9g u9g commented Aug 31, 2026

Copy link
Copy Markdown
Member

Problem

The nether external test's duration swings wildly on CI because the nether is full noise generation (level-type=FLAT only applies to the overworld). Measured across one CI run: 3.7-4.7s (1.8.8-1.11.2), 9.5-14.4s (1.20.2-1.21.1), 10.9-22.6s (1.16.5-1.19), 14-18.7s (1.21.8-26.1). That variance trips the new duration-regression gate from #4028 (>1.5x and >5s over master's baseline) on unrelated PRs.

Mechanism (from Mojang-mapped sources)

  • Portal travel into a fresh nether always ends in PortalForcer.createPortal, whose destination scan reads block states across a 33x33-block spiral — each ungenerated chunk it touches is noise-generated synchronously on the main thread while the test's clock runs. The test then waits on the chunks around nether (0,128,0) (waitForChunksToLoad + the block-below poll), which stream at server view-distance 8. Both costs are nether gen centered on nether (0,~,0).
  • /forceload add is itself synchronous on every version: ServerLevel.setChunkForced calls getChunk, blocking the main thread until the chunks are FULL (1.16.5 ServerLevel.java:1240, same in 1.21.8). Issued while tests run it starves their 5s timeouts (reproduced on 1.16.5: resetState's teleport times out). So the warm-up runs in the server-setup hook before the bot connects — status pings are answered from the network thread with a cached response, so the stall lands harmlessly inside the hook's 120s budget. 7x7 chunks around nether (0,0) covers the portal scan and every chunk the test's waits touch; the rest of the view-distance ring streams in without blocking anything the test waits on.
  • With generation out of the way, the /tp 0 128 0 exposed a race the gen delay used to hide: the tp lands while the client is still streaming pre-tp positions, the server rolls each one back ("moved too quickly"), and while any rollback awaits its confirm the server silently drops use_item packets (ServerGamePacketListenerImpl.handleUseItemOn requires awaitingPositionFromClient == null — 1.16.5 line 1019), losing the sign placement (reproduced 2/3 on 1.14.4). The test now settles with chat-echo round trips until one completes with no new forcedMove: the echo proves the server processed every packet sent before it, teleport confirms included, so a placement can no longer be dropped. Event-driven, no sleeps.

Change

  • test/externalTest.js: execute in minecraft:the_nether run forceload add -48 -48 63 63 from the server console during setup, gated on supportFeature('hasExecuteCommand') (1.14+; older versions have no forceload but their nether gen is already the cheap pre-1.16 kind).
  • test/externalTests/nether.js: position-settle loop before placing the sign.

Timings (local, nether test, 2-3 runs each)

version master (ms) this branch (ms)
1.8.8 5986 / 7004 4030 / 4077
1.14.4 5195 / 5842 / 4665 5187 / 4282 / 3826
1.16.5 4338 / 4113 1807 / 1804
1.20.2 3871 / 4031 1654 / 1597
26.1 5264 / 4959 1979 / 2007

Beyond the raw drop, the test's duration no longer depends on noise-gen throughput, which is what actually varies across CI runners. The pre-existing sign-spot flake (blocked sign position) is separate and addressed by #4021.

@u9g
u9g force-pushed the test/cheap-nether-gen branch from 1c62f28 to bee5a11 Compare August 31, 2026 14:08
@rom1504

rom1504 commented Sep 4, 2026

Copy link
Copy Markdown
Member

CI fails

@u9g
u9g force-pushed the test/cheap-nether-gen branch from bee5a11 to b78bc29 Compare September 4, 2026 18:16
The nether test's duration on CI swings 4-23s across version groups
because portal travel generates the destination nether chunks
synchronously while the test's clock runs, tripping the duration
regression gate. Forceload those chunks (7x7 around nether 0,0) during
server setup instead, before the bot connects: /forceload add blocks
the server main thread until the chunks are FULL on every version
(ServerLevel.setChunkForced calls getChunk), so issued any later it
starves in-test 5s timeouts.

With generation out of the way the tp to the nether roof lands while
the client is still streaming pre-tp positions; the server rolls each
one back (moved too quickly) and silently drops use_item packets until
the last rollback is confirmed (handleUseItemOn requires
awaitingPositionFromClient == null), which lost the sign placement.
Settle with chat-echo round trips until one completes with no new
forcedMove: the echo proves the server processed every packet sent
before it, confirms included.
@u9g

u9g commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

Closing: profiling shows the forceload only relocates the nether generation from the test into server setup (2-7s added to setup vs 2-6s removed from the test locally), while all four servers on a CI runner then stall at the same moment during startup — the 32s freeze on 1.18.2/1.19 that killed the bot's login on every run here. The nether test noise is better handled in the duration comparison than by pre-generating chunks.

@u9g u9g closed this Sep 5, 2026
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