Float alignment (Paper 1.21.5)

Description

If an entity is inside a block only slightly, it doesn't collide with the block but gets effected if it gets pushed for example and even gets velocity if it's a slime block that gets moved.

Reason

The "does not fully collide while slightly clipped" behavior is driven by epsilon-based contraction/tolerance in the collision solver. Collision tests intentionally treat very small overlaps as non-blocking (1.0E-7 scale), which lets float-aligned states persist.

Collision path used by all entities

Path: net/minecraft/world/entity/Entity.java (974-977, 1026-1028, 1053-1057)

Vec3 vec3 = vec.lengthSqr() == 0.0 ? vec : collideBoundingBox(this, vec, boundingBox, this.level(), entityCollisions);
...
List<VoxelShape> list = collectColliders(entity, level, potentialHits, collisionBox.expandTowards(vec));
return collideWithShapes(vec, collisionBox, list);
...
double d1 = Shapes.collide(axis, entityBB.move(vec3), shapes, d);

So block collision resolution always funnels through Shapes.collide(...) and per-shape VoxelShape.collide(...).

Epsilon tolerance in shared shape collision

Path: net/minecraft/world/phys/shapes/Shapes.java (22, 228-235)

public static final double EPSILON = 1.0E-7;
...
if (Math.abs(desiredOffset) < 1.0E-7) {
    return 0.0;
}
desiredOffset = voxelShape.collide(movementAxis, collisionBox, desiredOffset);

Very small movement deltas are treated as zero, and collision math uses the same epsilon scale globally.

Effective "small contraction" in voxel collision queries

Path: net/minecraft/world/phys/shapes/VoxelShape.java (262-267, 275-276, 289-291)

int i = this.findIndex(axis, d1 + 1.0E-7);
int i1 = this.findIndex(axis, d - 1.0E-7);
int max = Math.max(0, this.findIndex(axis1, collisionBox.min(axis1) + 1.0E-7));
int min = Math.min(this.shape.getSize(axis1), this.findIndex(axis1, collisionBox.max(axis1) - 1.0E-7) + 1);
int max1 = Math.max(0, this.findIndex(axis2, collisionBox.min(axis2) + 1.0E-7));
int min1 = Math.min(this.shape.getSize(axis2), this.findIndex(axis2, collisionBox.max(axis2) - 1.0E-7) + 1);
...
if (d2 >= -1.0E-7) {
    desiredOffset = Math.min(desiredOffset, d2);
}
...
if (d2 <= 1.0E-7) {
    desiredOffset = Math.max(desiredOffset, d2);
}

The collision box is sampled with +EPSILON on mins and -EPSILON on maxes. That is effectively a tiny inward contraction in collision indexing, plus near-zero penetration tolerance.

Why this matches the observed behavior

When piston logic leaves an entity in a tiny overlap state, collision resolution does not always immediately "hard-separate" it from that block face. Movement on another axis can still interact with nearby blocks and produce pushes, while the original slight overlap can appear non-blocking until a movement/collision case forces a stronger correction.

Root cause statement

Float alignment persistence comes from epsilon-based collision tolerances in the shared shape solver (Shapes/VoxelShape). The solver intentionally contracts/snap-tolerates by 1.0E-7, so tiny overlaps are not treated as strict blocking collisions.