Skip to content

Fix #323: lay pose renders at world bottom on Folia - #325

Open
RICE0707 wants to merge 3 commits into
gecolay:mainfrom
BrilliantTeam:main
Open

Fix #323: lay pose renders at world bottom on Folia#325
RICE0707 wants to merge 3 commits into
gecolay:mainfrom
BrilliantTeam:main

Conversation

@RICE0707

Copy link
Copy Markdown

Problem

On Folia, when a player uses /lay, viewers see the laying model snapped to
the bottom of the world (Y = minHeight) instead of at the seat (#323).
Not reproducible on Paper — this only occurs on Folia.

Root cause

The laying model is a packet-only fake NPC (a ServerPlayer that is never
added to the world), so its position on each viewer's client is determined
entirely by the packets GSit sends — and after the initial spawn bundle, the old
code never re-sends a position packet.

The /lay trick works like this:

  1. A fake bed is placed at the world's minimum height.
  2. The NPC is put into the SLEEPING pose with its sleep position pointing at
    that bed, so the client snaps the sleeping entity down to Y = minHeight.
  3. The same bundle then includes a teleport packet, after the metadata, to
    pull the NPC back up to the seat.

This is a timing-sensitive correction: the snap-to-bed is a client-side reaction
to the sleep metadata, and the teleport-back is a single, one-shot fix.

  • On Paper (single main thread, one deterministic packet flush per tick) the
    teleport reliably lands as the final position, so the model stays at the seat.
  • On Folia, entity ticking and packet flushing run on region threads with
    looser relative timing. The single in-bundle teleport can lose the race
    against the client applying the sleeping state, and because nothing re-sends a
    position afterwards, the NPC is left stuck at world bottom.

The fix already ships in every module from v1_21_2 onward: after the spawn
bundle, the teleport is re-sent on the next 1 and 2 ticks through the
entity scheduler (guarded by height < 1), which reliably wins the race and
restores the seat position on Folia. The double, cross-tick re-send is what
makes it robust against the timing race.

Modules v1_19_4 through v1_21 — every Folia-capable version predating
v1_21_2 — never received this correction, so they reproduce the bug. (Folia
only exists from 1.19.4+, so older modules cannot run on Folia and are not
affected.)

Fix

Backport the exact v1_21_2 correction to the affected Folia-capable modules:

  • v1_19_4, v1_20, v1_20_2, v1_20_3, v1_20_5, v1_21

Each gains a height field (using that module's own positioning basis) and the
delayed teleportNpcPacket re-send in addViewerPlayer, identical to the
already-shipped v1_21_2 implementation.

Fixes #323

RICE0707 and others added 3 commits July 15, 2026 16:13
* What went wrong:
Configuration cache state could not be cached: field `classpath` of task `:v1_21_11:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: error writing value of type 'org.gradle.api.internal.artifacts.configurations.DefaultResolvableConfiguration'
> Could not resolve all files for configuration ':v1_21_11:compileClasspath'.
   > Could not find net.kyori:adventure-text-serializer-ansi:.
     Required by:
         project ':v1_21_11' > io.papermc.paper:dev-bundle:1.21.11-R0.1-SNAPSHOT:20260511.115010-91
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.

[Bug][Folia] 1.21.2 clients cannot see laying 1.21.1 players correctly with ViaVersion (Folia Entity Scheduler issue)

1 participant