Skip to content

Fix entity rotation: handle yaw=0 + correct shortest-path math#478

Open
domdomegg wants to merge 1 commit into
PrismarineJS:masterfrom
domdomegg:fix-entity-rotation
Open

Fix entity rotation: handle yaw=0 + correct shortest-path math#478
domdomegg wants to merge 1 commit into
PrismarineJS:masterfrom
domdomegg:fix-entity-rotation

Conversation

@domdomegg

@domdomegg domdomegg commented May 1, 2026

Copy link
Copy Markdown

The entity rotation update path had two latent bugs that caused player models to snap to wrong orientations:

  1. if (entity.yaw) skips yaw=00 is a perfectly valid yaw (facing south in MC convention), but the truthy check treats it as falsy and skips the rotation update entirely, leaving the model at whatever rotation it had from a previous frame.

  2. Shortest-path math regression((2 * da) % 2π) - da doesn't always pick the geodesic on the unit circle when entity.yaw - e.rotation.y lands in certain quadrants, so models occasionally rotate the long way (≈2π−ε in the opposite direction).

Fix

-    if (entity.yaw) {
-      const da = (entity.yaw - e.rotation.y) % (Math.PI * 2)
-      const dy = 2 * da % (Math.PI * 2) - da
+    if (typeof entity.yaw === 'number' && Number.isFinite(entity.yaw)) {
+      const TAU = Math.PI * 2
+      const norm = ((entity.yaw - e.rotation.y) % TAU + TAU) % TAU
+      const dy = norm > Math.PI ? norm - TAU : norm
       new TWEEN.Tween(e.rotation).to({ y: e.rotation.y + dy }, 50).start()
     }

The new formula is the textbook "wrap to (-π, π]" shortest-rotation calculation. The same change is applied to the bot's own model in lib/index.js.

Note

The most user-visible "models snap on jump" symptom of this code path was actually rooted in a mineflayer bug (PrismarineJS/mineflayer#3896sync_entity_position not converting notchian yaw → radians). With that mineflayer fix in place, these viewer issues are still observable but more subtly: models facing exactly north (yaw=0) keep stale rotation, and occasional long-way-round spins on large yaw deltas. Worth fixing here too.

The entity rotation update path had two latent bugs:

1. `if (entity.yaw)` skipped the update when yaw === 0 (a perfectly
   valid value meaning 'facing south'). The model retained whatever
   rotation it had from a previous frame.

2. The `((2 * da) % 2π) - da` shortest-path formula doesn't always
   pick the geodesic on the unit circle when `yaw - rotation` lands
   in certain quadrants, so models occasionally rotated the long way.

Replace the truthy check with a `typeof === 'number' && isFinite`
test and use the textbook 'wrap to (-π, π]' formula instead. Same
change applied to the bot-self rotation path in lib/index.js.
@zardoy

zardoy commented May 2, 2026

Copy link
Copy Markdown
Contributor

@sandexzx do you think we need to port this?

@sandexzx

sandexzx commented May 2, 2026

Copy link
Copy Markdown

@sandexzx do you think we need to port this?

@zardoy Yeah, makes sense to port. We have the exact same code on our side in minecraft-renderer (src/three/entities.ts, the entity rotation tween) — both issues reproduce: entities facing yaw === 0
stay frozen because of the truthy check, and the shortest-path formula occasionally rotates the long way around.

That fix looks good — typeof === 'number' && isFinite is the right guard, and the ((d % TAU + TAU) % TAU) then wrap-to-(-π, π] is the standard correct formula for the shortest signed angular
delta.

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.

3 participants