Skip to content

Fix connections of fences, glass panes, iron bars and walls, and door states of LevelDB worlds - #2290

Draft
zisunny104 wants to merge 9 commits into
CloudburstMC:masterfrom
zisunny104:fix/connection-states
Draft

zisunny104 wants to merge 9 commits into
CloudburstMC:masterfrom
zisunny104:fix/connection-states

Conversation

@zisunny104

@zisunny104 zisunny104 commented Oct 14, 2026 •

Copy link
Copy Markdown

Fences, glass panes, iron bars and walls loaded from a world are drawn as separate posts since the 26.50 palette, and some door states of a LevelDB world are loaded as a missing block. This makes the server send and work out the right states.

Problem

Since 26.50 the four minecraft:connection_* states of fences, glass panes and iron bars are part of the block state (Fence on the wiki: connection changes now update neighbours "for parity with Java Edition"). Nukkit only knows the unconnected state, so when a block is placed next to a fence the client first predicts the connection and the server then overwrites it with the unconnected state. In game every fence ends up with an arm towards the block placed after it and none towards the one before it, and glass panes stay as separate posts.

The connections are not saved in a LevelDB chunk, and a world converted from the anvil format has none for walls either, so every fence, pane, bars and wall of a loaded world stays a separate post until a neighbouring block changes.

The shipped LevelDB palette also has no legacy block for a part of the door states (for example the top half of a door with some of the facings, or an open bottom half with the hinge set). They were loaded as the default block of the palette, so doors of a LevelDB world showed up as a cherry wood block or as nothing.

Changes

  • New ConnectionBlock interface. The four connections are stored in the block data above the type bits (wood type, colour). The bit order is the order of the 16 states in the vanilla palette: north is the lowest bit, then east, south and west, so the runtime id of a state is the runtime id of the unconnected state plus the connection bits (checked against the canonical block palette of the Cloudburst data repository for glass panes, stained and hard glass panes, iron bars and several fence types).
  • BlockFence, BlockThin (glass panes, stained glass panes, iron bars) implement it. The connections are worked out when the block is placed and when a neighbour changes. Glass panes and iron bars get a data field (BlockThin had none); the items only keep the type bits.
  • GlobalBlockPalette registers the 16 states of every fence, pane and iron bars type. The old LevelDB palette has no connection states, so there all of them map to the unconnected state and the wood type or colour is kept when saving.
  • The block state kept in a LevelDB chunk keeps the connections in memory (BlockStateMapping), otherwise they were lost as soon as the chunk was sent to the client again. The state written to the disk is still the unconnected one.
  • ConnectionUpdater works the connections out again when a chunk is loaded, and fixes the blocks next to the border of chunks that were already loaded. It now also does this for walls, using BlockWall.autoConfigureState, which can take the neighbouring blocks from a function so that it works for a chunk that is not part of a loaded level yet.
  • Connection rules, checked against vanilla: panes connect to every kind of pane, iron bars, walls and glass and to full solid blocks; fences connect to fences of the same kind (wooden fences do not connect to nether brick fences and the other way round), full solid blocks and fence gates. Fences and panes connect to the solid back of stairs but not to their sides or steps, and a fence only connects to a gate in the direction the gate is as wide as.
  • NukkitLegacyMapper gives a state without a legacy block the data the game gives it itself: for the two halves of a door the facing and the open bit for the bottom half and the hinge for the top half, for any other state the legacy block of the state of the same name that has the most properties in common, so that the wood type of a sideways wood block and the growth of a stem are kept. The state itself is not changed, so what is sent to the client and saved stays the same.

Not changed

  • Placing a wall and updating it when a neighbour changes already worked out its connections (#2277); only the update when a chunk is loaded is new.
  • The anvil format has no room for the connection bits, worlds in that format keep the old behaviour.
  • Blocks placed by world generators may not be updated until a neighbour changes.

Testing

  • Compiled, and checked with small test programs that load the palettes and chunks: runtime ids of the 16 states, block data and items, what is saved to the LevelDB palette, the chunk load update including the chunk border for fences, panes, iron bars and walls, and the connection rules above.
  • All 640 door states of the palette (20 door types with 32 states each) now map to a door; before 9 of the 32 states of a door mapped to the default block. Scanning the whole palette for states that map to the default block leaves only cherry wood and mangrove wood, which this PR does not change.
  • Checked in game on a local server with a test area of fences, glass panes, iron bars, walls, gates, stairs and doors: built without any neighbour update, then the server was stopped and started again. The connections are worked out when the chunks load, see the screenshots. The doors of the test area (the row at the back of the last screenshot) are drawn as doors.
  • Every state of the LevelDB palette (14,150) was mapped to its legacy block and back. 8,956 come back as the same state; the others are the same block with a property the legacy data cannot hold (hanging signs, the hinge of a door half, the facing of a sideways wood block). Before, 34 states came back as another block; now only the cherry and mangrove wood blocks do, which have no legacy block to borrow.
  • Not checked in game: blocks at different heights, and the stair and gate cases one by one.

Initial chunk delivery ordering (2026-10-18)

On first entry into a world, an asynchronous chunk snapshot can be captured before an adjacent chunk loads. Loading that neighbour corrects and sends the pane/fence connection states, but the older snapshot can finish later and overwrite those updates. Previously, the callback rejected that snapshot only for caching, while still sending it to players. Entering the same area again can therefore look correct even though the first entry did not.

Pending sends now retain the source chunk identity and its network version. A callback for an outdated or replaced chunk is discarded, leaving the players queued for a fresh snapshot on the next tick. The network version is separate from the disk dirty counter, so saving a chunk or clearing its connection-only dirty flag cannot make an old snapshot look current. This applies with chunk caching both enabled and disabled.

Validation: five regression tests exercise the actual request/callback methods with controlled provider completion: a connection update during encoding and subsequent retry, dirty-counter reset during encoding, stale completion with caching enabled, chunk replacement, and unchanged delivery. The previous core delivers stale results in the non-cached failure cases; the fixed implementation passes all five. The cached stale-result test also checks that rejection happens before compression. In-game validation of the reported first-entry/re-entry sequence is still pending; this PR remains a draft.

首次進入世界的區塊傳送順序(2026-10-18)

首次進入時,非同步區塊快照可能早於鄰區塊載入;鄰區塊載入後雖然已修正並送出玻璃片/柵欄連接,稍後完成的舊快照仍可能覆蓋它。原本只禁止快取這份過期結果,仍會把它送給玩家,因此第二次進入反而可能正常。

現在會核對原區塊實例與獨立的網路版本,捨棄過期結果,保留等待玩家並於下一個 tick 重新編碼。存檔或清除 dirty flag 不會重設網路版本;快取開啟與關閉都適用。5 個回歸測試已通過;首次進入/再次傳送的遊戲內驗證尚待進行,PR 維持草稿。

Screenshots

Before After
Wooden fences (the dark nether brick fence at the lower left is not connected to the wooden fences on purpose) 1-before-fences 2-after-fences
Test area: fences, panes, bars, walls, doors (the row at the back) 3-before-test-area 4-after-test-area

The test area was built without any neighbour update, so the saved blocks have no connections. "Before" is the area right after it was built; "After" is the same area after the server was stopped and started again, when the chunks are loaded and the connections are worked out. The test area pictures are taken from a similar angle; the two fence pictures are not the same spot.

Wooden fences and nether brick fences do not connect to each other, like in vanilla; fences only connect to fences of the same kind, to full solid blocks and to fence gates. In the "After" fence picture the nether brick fence next to the wooden ones is therefore left unconnected, which is the expected result.

Doors

Before this PR, some door states of a LevelDB world had no legacy block in the shipped palette and were loaded as the default block of the palette, a cherry wood block. In game a door is then missing a half: the doorway is filled with a wrong block, or with a block that shows nothing but still has an outline. 9 of the 32 states of every door type are affected (for example an open bottom half with the hinge set, or the top half of a door with some of the facings). With this PR they are loaded as doors.

The two pictures show the two ways the problem looks in game, both before this PR:

Shown as a wrong block Shown as nothing
door half shown as a wrong block door half shown as nothing, with an outline

In the right picture the block has an outline and the name at the bottom of the screen says it is a cherry wood block, but it is drawn as nothing. I did not take a picture of the same doors after the fix; the doors of the test area above (the row at the back) are drawn as doors.


玻璃片、柵欄、鐵柵欄與石牆從世界載入後會變成單獨的柱子,LevelDB 世界裡部分門的狀態會被載入成缺少的方塊。此 PR 讓伺服器送出並算出正確的狀態。

  • 柵欄、玻璃片、鐵柵欄:26.50 起連接狀態是方塊狀態的一部分,伺服器現在會送出,並在放置、鄰居變更與區塊載入時重算;跨區塊邊界也會處理。
  • 石牆:放置與鄰居更新原本就會算連接(fix(block): connect walls to neighbouring blocks #2277),這裡補上區塊載入時的重算。玻璃片與鐵柵欄也可以連到石牆。
  • 門:LevelDB 調色盤對部分門的狀態沒有對應的舊式方塊,會被載入成預設方塊(櫻花木方塊或空白)。現在給它們遊戲自己使用的資料,狀態本身不變。
  • 測試:編譯並用小程式檢查;調色盤 640 個門狀態都對得到門;在本機伺服器蓋測試區、重開後連接正確(見截圖)。沒有實測的部分已寫在英文的 Testing。
  • 截圖說明:木柵欄與地獄磚柵欄不會互相連接(與原版相同),所以柵欄截圖裡地獄磚柵欄沒有連到木柵欄是正確的結果。門的「修正前」圖片是本 PR 前的樣子,沒有同一扇門的修正後圖片,測試區後排的門已正常顯示。

🤖 Generated with Claude Code

zisunny104 and others added 4 commits October 15, 2026 02:16
Since the 26.50 palette the client takes the four connection states of
fences, glass panes and iron bars from the block state instead of working
them out itself. Nukkit always sent the unconnected state, so a neighbour
placed next to it overwrote the connection the client had just predicted
and the blocks were drawn as separate posts.

The connections are now stored in the block data above the type bits and
are sent as the matching state: the 16 states follow the unconnected state
in the palette, ordered north, east, south, west from the lowest bit. They
are recalculated when the block is placed and when a neighbour changes.
The LevelDB palette has no connection states, so every state maps to the
unconnected one there and the wood type or colour is kept on save.

Panes also connect to every kind of pane, bars and glass, and wooden
fences no longer connect to nether brick fences.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The LevelDB palette used to save chunks has no connection states, so the
state kept for a fence, glass pane or iron bars in memory lost its
connections. They were right when the block was placed, but missing when the
chunk was sent to the client again, and an update after removing a neighbour
was skipped because the stored and the new connections looked equal.

Keep the connections in the in-memory state and keep writing the unconnected
state to the disk.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The connections are not saved with the chunk, so fences, glass panes and
iron bars loaded from the disk had none. Work them out when the chunk is
loaded, and fix the blocks next to the border of the chunks that were already
loaded and send them to the players.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…their width

Fences and glass panes connect to the solid back of a stair, not to its
steps or sides; before they connected to every side because stairs count as
solid. A fence only connects to a fence gate in the direction the gate is as
wide as, not in front of it or behind it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 14, 2026 18:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

zisunny104 and others added 2 commits October 15, 2026 12:50
- A world converted from the anvil format has walls without connection states, they
  stayed separate posts until a neighbouring block was updated. ConnectionUpdater now
  works them out again with the other connection blocks, also across chunk borders.
- BlockWall.autoConfigureState can take the neighbouring blocks from a function, so
  the same logic is used for a block that is not part of a loaded level yet.
- Glass panes and iron bars connect to walls, like in the game.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The shipped palette has no legacy block for part of the states, for example the top half
of a door that faces south or a sideways wood block, so they were loaded as the default
block of the palette and showed up as a missing or a wrong block.

- The two halves of a door are given the data the game gives them itself: the facing and
  the open bit for the bottom half, the hinge for the top half.
- Any other state without a legacy block is given the block of a state of the same name.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@zisunny104 zisunny104 changed the title Send connection states for fences, glass panes and iron bars Fix connections of fences, glass panes, iron bars and walls, and door states of LevelDB worlds Oct 15, 2026
A state without a legacy block was given the id of a state of the same name and
the data 0. For the sideways wood blocks that meant the oak wood block, for the
stems that meant growth 0. It is now given the legacy block of the state of the
same name that has the most properties in common and a legacy block of its own,
so that the wood type and the growth of a stem are kept.

Co-Authored-By: Claude Sonnet 5.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.

2 participants