Push limit flaot alignment (Paper 1.21.5)
Description
If a tnt (or entity of the same size) is aligned against a block and gets moved with the piston entity push limit twice it gets aligned to the block 2 in front.
Reason
The piston/entity push cap is 0.51 per axis window, and piston movement logic naturally generates 0.51 pushes (0.5 + 0.01). Two full pushes produce 1.02 total displacement, which is enough to move a 0.98-wide TNT from one aligned state into clipping on the opposite side.
Code evidence for the 0.51 cap
Path: net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java (35-36)
private static final double PUSH_OFFSET = 0.01;
public static final double TICK_MOVEMENT = 0.51;
Path: net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java (116, 175)
double d = partialTick - piston.progress;
...
d4 = Math.min(d4, d) + 0.01;
With piston tick progression (+0.5 each tick), this yields up to 0.51 movement in a single piston movement step.
Path: net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java (328-331)
float f = blockEntity.progress + 0.5F;
moveCollidedEntities(level, pos, f, blockEntity);
moveStuckEntities(level, pos, f, blockEntity);
blockEntity.progress = f;
Code evidence for the entity-side limiter
Path: net/minecraft/world/entity/Entity.java (941-948, 966-971)
if (gameTime != this.pistonDeltasGameTime) {
Arrays.fill(this.pistonDeltas, 0.0);
this.pistonDeltasGameTime = gameTime;
}
...
double d = Mth.clamp(distance + this.pistonDeltas[ordinal], -0.51, 0.51);
distance = d - this.pistonDeltas[ordinal];
this.pistonDeltas[ordinal] = d;
This is the actual per-axis clamp gate. It resets when game time advances.
TNT size used in the alignment math
Path: net/minecraft/world/entity/EntityType.java (901-907)
public static final EntityType<PrimedTnt> TNT = register(
"tnt",
EntityType.Builder.<PrimedTnt>of(PrimedTnt::new, MobCategory.MISC)
...
.sized(0.98F, 0.98F)
Numeric walkthrough for (Block Air Air Block)
- TNT width is
0.98, so in one 1-block air cell it has0.02total clearance (0.01per side when centered). - One full piston push window can move it
0.51. - Two full push windows move it
1.02. 1.02is0.02more than one full block, so after starting perfectly aligned on one side, the entity ends up clipping into the opposite side.
Root cause statement
Push-limit float alignment is a deterministic consequence of 0.51 capped piston displacement windows combined with entity width (0.98). Two full pushes (1.02) overshoot a clean one-block transfer and force opposite-side clipping.