Skip to content

fix(place): drop reach-around placement, click only where the ray hits - #64

Merged
notchyves merged 1 commit into
theselauraproject:mainfrom
NaySurGithub:fix/vanilla-placement
Nov 6, 2026
Merged

notchyves merged 1 commit into
theselauraproject:mainfrom
NaySurGithub:fix/vanilla-placement

Conversation

@NaySurGithub

Copy link
Copy Markdown
Contributor

Problem

About 27 seconds into a Solo SkyWars game on CubeCraft, the account was temp-banned: "Unfair Advantage (Sentinel Automatic Cheat Detection)". A packet capture of the whole game (Perlin's packetlog) shows the player bridging during the 10 seconds before the disconnect.

For every InventoryTransaction / UseItemOn sent in the game, I rebuilt the look ray from the eye position and the yaw/pitch of the preceding PlayerAuthInput. I then measured the angle between that ray and the point the transaction claims was clicked (blockPosition + clickPos):

Time Face clickPos Distance (blocks) Angle off the look ray
21:53:18.555 5 [0.977, 0.855, 0.735] 1.60 1.7°
21:53:23.749 4 [0.023, 0.356, 0.402] 3.43 0.0°
21:53:35.972 4 [0, 0.999, 0.523] 1.87 3.2°
21:53:36.589 4 [0, 0.999, 0.494] 1.91 1.4°
21:53:37.171 1 [0.305, 1, 0.473] 1.93 0.2°
21:53:38.366 4 [0, 0.999, 0.425] 1.85 4.5°
21:53:38.917 4 [0, 0.999, 0.39] 1.82 6.2°
21:53:40.512 4 [0, 0.999, 0.352] 1.69 17.1°
21:53:41.046 4 [0, 0.999, 0.337] 1.84 4.8°
21:53:41.573 4 [0, 0.999, 0.312] 1.91 1.2°
21:53:42.147 5 [1, 0.04, 0.23] 5.45 0.5°
21:53:42.544 1 [0.164, 1, 0.234] 4.31 0.5°
21:53:42.704 1 [0.773, 1, 0.251] 3.17 0.9°
21:53:42.870 5 [1, 0.563, 0.257] 3.70 0.9°
21:53:43.537 1 [0.07, 1, 0.26] 3.13 0.0°
21:53:44.011 — disconnect: temp ban — —

Rows with a real ray hit are within about 1° (float rounding, plus the 50 ms between input and transaction). The suspicious run (21:53:35 → 21:53:41) is different on two counts:

  • face 4 (side) with clickPos.y = 0.999 every time: the click is pinned to the top edge of the block's side;
  • up to 17° between the look ray and the claimed click point.

That is the reach-around assist (Bridging::FindTarget). When the look ray hits nothing, it picks the block under the player, projects the ray onto it and clamps the point to that block's edge (clickY → 0.999). The client then sends a click the player's view never reached, on every block of the bridge. To an anticheat, that is scaffolding.

What the vanilla client sends

The behaviour below comes from IDA on the Windows client 1.26.60.5. The build lambda is 0x143771570, called from GameMode vtable slot 6 (0x14376B290), which builds the ItemUseInventoryTransaction. It reads the player's HitResult through level->vfunc(2680), with the type at +24 and face / BlockPos / mPos at +28..+56:

Case Click position written at +252
Block hit (type 0) HitResult::mPos - BlockPos: the real ray hit on the face
No block hit (type 2 ENTITY_OUT_OF_RANGE / 3 NO_HIT) BlockPos - BlockPos = (0, 0, 0)
Continuing an earlier build (GameMode+65 set and 0x145E90D10(player)) the hit stored at GameMode+108 on the first placement

None of these produces an edge-clamped click that the look ray does not reach. No string or code path for a placement assist exists in the client.

Change

  • PerlinInteraction.cpp: the reach-around block is removed. A fresh press places only on the crosshair block, with the click taken from the ray hit (clickOf: point - block).
  • Bridging.{hpp,cpp} are deleted, since they had no other user.
  • tests/bridging_selftest.cpp → tests/heldbuild_selftest.cpp: the reach-around cases are dropped, and every HeldBuild / BlockUse check is kept (held repeat delays, build line, body overlap, slab/stair rules).
  • docs/bedrock-bridging.md and docs/bedrock-native-placement.md: document the vanilla click rules above.

Held repeats (HeldBuild, ported from _tickBuildAction / continueBuildBlock) are unchanged. Two things there still need checking against the table:

  • without a ray hit, it already sends the block position (click (0, 0, 0)), as vanilla does;
  • when it snaps to the last placed block, it does not yet reuse the first placement's click as vanilla does (GameMode+108). That needs its own follow-up.

Testing

  • The build and heldbuild_selftest were still running when this PR was opened, so the code has not been compiled or run yet.
  • Still to do: a live bridging session on the same server with a capture, checking that every UseItemOn stays on the look ray.

The reach-around assist placed against the block under the player when the
look ray hit nothing, and made up a click on that block's edge (y 0.999).
The vanilla client takes the build click from its HitResult only, so these
clicks did not match the player's view and got flagged as scaffolding.

Keeps the held-build tests as heldbuild_selftest.
@notchyves
notchyves merged commit 128d293 into theselauraproject:main Nov 6, 2026
11 checks passed
notchyves pushed a commit that referenced this pull request Nov 6, 2026
… edge (#72)

Bedrock bridging works by looking down past the edge of the block you
stand on: the ray hits nothing, yet the game targets that block. #64
removed Perlin's invented reach-around (and its made-up click), which
left no way to place there at all.

The game's pick (0x144E090C0) does it when nothing is in reach and the
look direction's y is below -0.7: it clips blocks from the eye 2 blocks
straight down, keeps that hit point, and sets the face from the look's
dominant horizontal axis; the hit carries a flag (+0x84) that held
repeats check before reusing it (0x14376B8D0). Perlin now does the same,
so the outline shows and the click is mPos - BlockPos (top of the block)
like the game's.
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