mojira.dev
MC-276817

Teleport command offsets are applied incorrectly

Relative teleport commands appear to be measured as relative to the target but applied relative to the context, resulting in unexpected behavior. For example, this function:

tp @s ~5 ~ ~
tp @s ~ ~ ~

Expected behavior: player is not moved at all
Actual behavior: player is moved 5 blocks in the negative X direction

It appears as though the player moves +5X, then the second command tries to put them back to the context position, which is a relative move of -5X from their current position, but it applies that -5X to the context position instead of the player's position, resulting in the player moving from +5X to -5X.

For another example:

tp @s ~ ~ ~5
tp @s ~ ~ ~5

Expected behavior: player is moved 5 blocks in the positive Z direction
Actual behavior: player is not moved at all

Again, it appears that the player moves +5Z, then the second command tries to put them there again, which is a relative move of 0Z from the new position, but it applies that 0Z to the context position, putting the player back where they started.

Attachments

Comments 5

I also tested the teleport command in different coordination.

Example 4:

// example_4a.mcfunction
tp @s ~3 ~ ~
tp @s ~ ~3 ~
tp @s ~ ~ ~3

Expected: Player is teleported 3 blocks east, 3 blocks south and 3 blocks up from the previous position.

Actual: Player is teleported 3 blocks east, 3 blocks south and 3 blocks down.

// example_4b.mcfunction
tp @s ~ ~3 ~
tp @s ~3 ~ ~
tp @s ~ ~ ~3

Expected: Should be the same expectation.

Actual: Player is teleported 3 blocks west, 3 blocks south and 3 blocks up.

// example_4c.mcfunction
tp @s ~3 ~ ~
tp @s ~ ~3 ~
tp @s ~ ~ ~3
tp @s ~ ~ ~3

Expected: Player is teleported 3 blocks east, 6 blocks south and 3 blocks up from the previous position.

Actual: Player is teleported 3 blocks west, 3 blocks up, and 3 blocks south and north

In Example 4, this bug is confirmed.

 

Example 5:

// example_5a.mcfunction
tp @s ~3 ~ ~
execute at @s run tp @s ~ ~3 ~
execute at @s run tp @s ~ ~ ~3

This works as expected, but if I try to catch the position:

// example_5b.mcfunction
tp @s ~3 ~ ~
tellraw @a [ "1-a: ", {"nbt": "Pos[0]", "entity": "@s"}, ", ", {"nbt": "Pos[1]", "entity": "@s"}, ", ", {"nbt": "Pos[2]", "entity": "@s"} ]
execute at @s run tp @s ~ ~3 ~
tellraw @a [ "1-a: ", {"nbt": "Pos[0]", "entity": "@s"}, ", ", {"nbt": "Pos[1]", "entity": "@s"}, ", ", {"nbt": "Pos[2]", "entity": "@s"} ]
execute at @s run tp @s ~ ~ ~3
execute at @s run tellraw @a [ "1-a: ", {"nbt": "Pos[0]", "entity": "@s"}, ", ", {"nbt": "Pos[1]", "entity": "@s"}, ", ", {"nbt": "Pos[2]", "entity": "@s"} ]

Expected: Pos[] follows the player. If players is teleported 3 blocks up, Pos[] is also 3 blocks up from the previous position,
Actual: Pos[] is twice as far as the actual position of the player, and it doesn't change with execute at @s run heading.

In Example 5, we can know that the position caught gets twice as far as expected after teleport commands in a function.

Example 4 has a wrong expected result. The function context should start at the same position for all three commands, which means that the tp @s ~ ~ ~3 at the end should be left unaffected by the previous commands.

So the expected result for Example 4 is that the player should be teleported 3 blocks south, and not have their position changed otherwise.

Please attach a working datapack which includes any required functions to reproduce this issue.

I can confirm this issue. I've tested it a bit further, and updated the description to be somewhat more clear.
EDIT: also attached a datapack providing the two example functions

Code analysis by @ErrorCraft (text slightly edited by me, and translated mappings from yarn to official):

In ServerPlayer::changeDimension (which is used even when the dimension is not changed) it first sets the Player's position via Entity::teleportSetPosition by resolving it. So a y position of -60.0 with a teleport command with ~10 will set the player's position to -50.0. It then calls the ServerGamePacketListenerImpl::teleport method, which coincidentally calls these same methods! However, since it uses the Player's position, it now uses -50.0 instead of the expected -60.0! So the final y coordinate is -40.0, rather than -50.0, because it applies the offset a second time. However, on the client the player gets moved -50.0, so this actually causes a desync.

Now... What causes it to go back to its original position after executing the command a second time? The Player's y coordinate is now -40.0 and we want to teleport 10 blocks upwards from -60.0, which is -50.0. It calculates the position by subtracting the entity's position from the calculated position. This results in the following calculation: -50.0 - -40.0 which equals -10.0, the exact opposite of what we wanted, which causes it to teleport the Player 10 blocks down twice as described before.

Because this happens in the changeDimension override in ServerPlayer, it means that the issue only affects Players, which is something we can observe too if we try it out on another entity like a Pig; The Pig's position is set correctly.

This issue (or two, though one causes the other) is fixed by removing the Entity::teleportSetPosition call in the ServerPlayer::changeDimension method.

To reproduce the desync issue, just go in Survival mode and invoke the /teleport ~ ~10 ~ command while standing still and observe the fall damage. Then compare to the damage taken by falling from 20 blocks high. Falling from 10 blocks the fall damage is less.

MulverineX

Shugoh

Confirmed

Platform

Very Important

Commands

24w38a

24w39a

Retrieved