The bug
When you place a painting in a Structure and save it using the Save Structure Block, and then Load the structure, the painting will sometimes load one block off.
All paintings with even height are loaded off in +Y axis
A painting with even width is off if
it looks towards +Z when loaded, then it's loaded off in +X
it looks towards -X when loaded, then it's loaded off in +Z
Affects rotating/mirroring
Cause
The problem seems to be that the Pos
tag of hanging entities refers to the center of the entity, however when it is created the passed position is the position of its corner. This could probably be solved by using the TileX
, TileY
and TileZ
values for hanging entities.
Linked issues
is duplicated by 6
relates to 3
Attachments
Comments 19
Testing with Mirrors:
Using the Z-axis mirror (< >), you get the same results for paintings and items frames that you get without mirrors. Painting misplaced at 0 rotation and 270 rotation. Item Frames misplaced at 90 and 270 rotations.
Using the X-axis mirror (^ v), the paintings are all correctly placed at every rotation. However, the Item Frames are misplaced at the 90 and 270 rotations again.
Possibly fixed in 1.10-pre1, but difficult to verify with MC-102894.
The problem seems to be that the Pos
tag of hanging entities refers to the center of the entity, however when it is created the passed position is the position of its corner. This could probably be solved by using the TileX
, TileY
and TileZ
values for hanging entities.
Confirmed in 1.20.4
[media]Potential Solution?
In StructureTemplate#placeEntities before Mob#finalizeSpawn, it might be possible to run some kind of structure placement validation check which corrects the positioning of the entity (and any future entities that may have a similar issue?) prior to finalization.
By converting the painting variant's width and height to block sizes (Variant#getWidth() / 16), if the height is even, offset the painting Y block pos down by 1. If the width is even, and the painting direction is west or south, offset by Direction#getClockwise().getNormal(), then set the painting pos to that position.
The mixin code I wrote to test this solution (in 1.20.2) looks like this: (MojMap)
@Mixin(StructureTemplate.class)
public class StructureTemplateMixin {
// Injects into StructureTemplate#placeEntities, inside the lambda of createEntityIgnoreException
@Inject(method = "method_17917", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;moveTo(DDDFF)V", shift = At.Shift.AFTER))
private static void fixPaintingPlacement(Rotation rotation, Mirror mirror, Vec3 vec3, boolean bl, ServerLevelAccessor level, CompoundTag tag, Entity entity, CallbackInfo ci) {
if (!(entity instanceof Painting painting)) {
return;
}
var pos = new BlockPos.MutableBlockPos();
pos.set(painting.getPos());
var variant = painting.getVariant().value();
var width = variant.getWidth() / 16;
var height = variant.getHeight() / 16;
var direction = painting.getDirection();
// paintings with an even height seem to always be moved upwards...
if (height % 2 == 0) {
pos.move(0, -1, 0);
}
// paintings with an even width seem to be moved in the clockwise direction of their facing direction,
// if they're west or south.
if (width % 2 == 0 && (direction == Direction.WEST || direction == Direction.SOUTH)) {
var moveTo = direction.getClockWise().getNormal();
pos.move(moveTo);
}
painting.setPos(pos.getCenter());
}
}
Possibly related to this, Item Frames have a similar issue.
When the structure is rotated when loading, the item frame (with item in it) will be floating in front of the wall.
This happens at the 90 and 270 degree rotations.
I added additional screenshots showing the floating item frames.