PaperMC / PaperMC/Paper

Resyncing entity positions' every 60 ticks breaks entity display's `teleport_duration` interpolation if the position gets sync during a interpolation

Open
#11,694 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: needs triage version: 1.21.3
Dominant language
Java
Stars
12.7k
Forks
3.5k
Avg merge
3d 13h
Merged PRs (30d)
11

Description

Expected behavior

When spawning a display entity, delaying the teleport a bit (enough that it triggers the periodic pos/rot packet every 60 ticks lived), and then it with a set teleport_duration, the teleportation should work fine with a specific interpolation set.

By the way, THIS IS NOT A PAPER BUG, THIS IS A VANILLA BUG!!! ...but it would be nice if it is possible to fix this on Paper. :3

Observed/Actual behavior

The entity gets slow when it gets to the end of the interpolation.

https://github.com/user-attachments/assets/61b9d7b9-7074-444b-8c6f-afb5dd11b710

Steps/models to reproduce
  1. Spawn a display entity
  2. Set the teleportDuration to 59 ticks
  3. Wait 8 ticks (enough that it goes over the 60 tick position sync)
  4. Teleport the display entity
  5. Watch the entity
                    val cameraEntity = player.world.spawn(
                        originalCamera,
                        TextDisplay::class.java
                    ) {
                        it.teleportDuration = 59
                    }

                    Bukkit.broadcastMessage("Camera Entity: ${cameraEntity.entityId}")

                    cameraEntity.text(textComponent {
                        content("hello")
                    })

                    Bukkit.getScheduler().runTaskLater(
                        m,
                        Runnable {
                            cameraEntity.teleport(originalCamera)

                            Bukkit.getScheduler().runTaskLater(
                                m,
                                Runnable {
                                    cameraEntity.teleport(targetCamera)
                                },
                                4L
                            )
                        },
                        4L
                    )
Plugin and Datapack List

Not applicable

Paper version
This server is running SparklyPaper version 1.21.3-DEV-ver/1.21.3@6921e60 (2024-12-01T03:37:24Z) (Implementing API version 1.21.3-R0.1-SNAPSHOT)

(something something it is my own fork and stuff like that)

Other

This happens due to boolean flag2 = flag1 || this.tickCount % 60 == 0;, because it attempts to resync the position every 60 ticks, however I'm not sure how could that be fixed without tracking when the entity is interpolating on the client side. (which, due to circumstances, the entity may not be interpolating on the client when the teleport_duration is set, example: if you set the teleport duration and teleport the entity on the same tick)

Now, I must say that there is a workaround for this issue... and the workaround is "create a new entity display every 60 ticks".

This DOES NOT HAPPEN if you manually send the add entity + set data + teleport packets

                        val add = ClientboundAddEntityPacket(
                            newId,
                            UUID.randomUUID(),
                            -28.5,
                            108.0,
                            -58.5,
                            0f,
                            0f,
                            net.minecraft.world.entity.EntityType.TEXT_DISPLAY,
                            0,
                            Vec3.ZERO,
                            0.0
                        )

                        Bukkit.broadcastMessage(net.minecraft.world.entity.Display.DATA_POS_ROT_INTERPOLATION_DURATION_ID.id.toString())

                        val metadata = ClientboundSetEntityDataPacket(
                            newId,
                            listOf(
                                SynchedEntityData.DataValue(net.minecraft.world.entity.Display.DATA_POS_ROT_INTERPOLATION_DURATION_ID.id, EntityDataSerializers.INT, 59),
                                SynchedEntityData.DataValue(
                                    23,
                                    EntityDataSerializers.COMPONENT,
                                    io.papermc.paper.adventure.PaperAdventure.asVanilla(
                                        textComponent("test packet") {}
                                    )
                                )
                            )
                        )

                        val nmsPlayer = (player as CraftPlayer).handle
                        nmsPlayer.connection.sendPacket(add)
                        nmsPlayer.connection.sendPacket(metadata)

                        Bukkit.getScheduler().runTaskLater(
                            m,
                            Runnable {
                                Bukkit.getScheduler().runTaskLater(
                                    m,
                                    Runnable {
                                        nmsPlayer.connection.sendPacket(
                                            ClientboundTeleportEntityPacket(
                                                newId,
                                                PositionMoveRotation(
                                                    Vec3(
                                                        targetCamera.x,
                                                        targetCamera.y,
                                                        targetCamera.z,
                                                    ),
                                                    Vec3(
                                                        targetCamera.x,
                                                        targetCamera.y,
                                                        targetCamera.z,
                                                    ),
                                                    0.0f,
                                                    0.0f
                                                ),
                                                setOf(),
                                                false
                                            )
                                        )

                                        Bukkit.getScheduler().runTaskLater(
                                            m,
                                            Runnable {
                                                nmsPlayer.connection.sendPacket(
                                                    ClientboundTeleportEntityPacket(
                                                        newId,
                                                        PositionMoveRotation(
                                                            Vec3(
                                                                targetCamera.x + 10.0,
                                                                targetCamera.y,
                                                                targetCamera.z,
                                                            ),
                                                            Vec3(
                                                                targetCamera.x + 10.0,
                                                                targetCamera.y,
                                                                targetCamera.z,
                                                            ),
                                                            0.0f,
                                                            0.0f
                                                        ),
                                                        setOf(),
                                                        false
                                                    )
                                                )
                                            },
                                            59L
                                        )
                                    },
                                    4L
                                )
                            },
                            4L
                        )

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the display entity scenario with teleportDuration set to 59 ticks, then inspect the entity position-sync path containing flag2 = flag1 || this.tickCount % 60 == 0. Compare normal entity updates with the manual packet sequence described in the issue. Done means the periodic sync no longer disrupts client-side teleport interpolation while ordinary position synchronization still works.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
game-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.