Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-120545

Teleportation wraps rotation angles causing first-person hand to rotate incorrectly

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • None
    • Minecraft 1.12.1, Minecraft 1.12.2 Pre-Release 1, Minecraft 1.12.2 Pre-Release 2, Minecraft 1.12.2, Minecraft 17w43a, Minecraft 17w43b, Minecraft 17w46a, Minecraft 17w48a, Minecraft 17w50a, Minecraft 18w01a, Minecraft 18w03b, Minecraft 18w06a, Minecraft 18w09a, Minecraft 18w10d, Minecraft 18w14b, Minecraft 1.13-pre1, Minecraft 1.13-pre3, Minecraft 1.13-pre6, Minecraft 1.13-pre8, Minecraft 1.13-pre10, Minecraft 1.13, Minecraft 18w30b, Minecraft 18w31a, Minecraft 1.13.1, Minecraft 1.13.2, Minecraft 18w44a, 1.16.3, 1.16.4, 20w48a, 21w03a, 21w05b, 1.17.1, 21w42a, 22w15a, 1.19.1 Release Candidate 2, 1.19.2, 1.19.3, 1.19.4 Pre-release 1
    • Confirmed
    • Commands, Player Animation
    • Low
    • Platform

      /tp and /teleport forcibly wrap player rotation, which causes the first person hand/held item to rotate incorrectly.

      Case 1

      (Does not work since 1.19.3)
      If you rotate in circles several times, and then run /tp @p ~ ~ ~ or /teleport @p ~ ~ ~, your hand will spin in circles several times extremely quickly, even though the actual visual angle will not change.

      Case 2

      If, after running /tp @p ~ ~ ~ 0 ~ to ensure consistency, you run /tp @p ~ ~ ~ ~90 ~, your hand will make 3 small rotations from the left and then one big rotation from the right, even though in each case you've been making small rotations from the left. The inverse happens with /tp @p ~ ~ ~ ~-90 ~.

      Cause

      The hand rotation interpolates between Entity.prevRotationYaw and Entity.rotationYaw. This would normally be fine, but these values are wrapped differently. rotationYaw is modulo'd by 360 within setRotation, while prevRotationYaw is left unwrapped by setPositionAndRotation (the one that the player position packet handler uses) except for a +/- 360 change based off of the difference from the old value.

      Potential fix

      A fix by Moulberry can be found in this comment.

      This can also be hack-fixed by simply removing both cases of angle wrapping:

      double d0 = (double)(this.prevRotationYaw - yaw);
      
      if (d0 < -180.0D)
      {
          this.prevRotationYaw += 360.0F;
      }
      
      if (d0 >= 180.0D)
      {
          this.prevRotationYaw -= 360.0F;
      }
      

      in Entity.setPositionAndRotation (Entity.a(DDDFF)V) should be removed completely (without wrapping it doesn't make sense).

      protected void setRotation(float yaw, float pitch)
      {
          this.rotationYaw = yaw; // % 360
          this.rotationPitch = pitch; // % 360
      }
      

      The % 360 should be removed from setRotation.

      The problem with this fix is that now there is no limit to the angle. That isn't too big of a problem (as clientside there already is no limit - F3 displays the wrapped version), but it could be mitigated by calling MathHelper.wrapDegrees when reading/writing the entity from/to NBT (as there won't be any previous rotations to worry about there).

      Demo

      See this video. This video was recorded in my MCP test install, and there are some things that are non-standard, but this bug has been tested in vanilla. Of important note is the unwrapped angle in F3, displayed in red. This is the exact value of rotationYaw, without calling wrapDegrees. The game was also slowed down to .25x the normal speed using cheat engine, again for visibility.

            Unassigned Unassigned
            pokechu22 [Mod] Pokechu22
            Votes:
            33 Vote for this issue
            Watchers:
            21 Start watching this issue

              Created:
              Updated:
              CHK: