From bb91c5ae2331d7d13400c1cf58bf31a1728bfb80 Mon Sep 17 00:00:00 2001 From: Octavia Togami Date: Sat, 26 Sep 2026 20:58:44 -0700 Subject: [PATCH] Correct handling of tile position for block-attached entities This more closely matches MC's behavior Fixes #3023 --- .../function/entity/ExtentEntityCopy.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java index e6f72d24b1..90a9c19b7e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java @@ -100,35 +100,40 @@ public boolean apply(Entity entity) throws WorldEditException { if (state != null) { Location newLocation; Location location = entity.getLocation(); - // If the entity has stored the location in the NBT data, we use that location LinCompoundTag tag = state.getNbt(); - boolean hasTilePosition = false; + Vector3 tilePosition = null; if (tag != null) { if (tag.value().get("block_pos") instanceof LinIntArrayTag blockPos && blockPos.value().length == 3) { // New block_pos value - location = location.setPosition(Vector3.at( + tilePosition = Vector3.at( blockPos.value()[0], blockPos.value()[1], blockPos.value()[2] - ).add(0.5, 0.5, 0.5)); - hasTilePosition = true; + ); } else if (tag.value().get("TileX") instanceof LinNumberTag tagX && tag.value().get("TileY") instanceof LinNumberTag tagY && tag.value().get("TileZ") instanceof LinNumberTag tagZ ) { // Legacy TileX/Y/Z values - location = location.setPosition(Vector3.at( + tilePosition = Vector3.at( tagX.value().intValue(), tagY.value().intValue(), tagZ.value().intValue() - ).add(0.5, 0.5, 0.5)); - hasTilePosition = true; + ); } } + // Apply transform to tile position if we have one and either: + // 1. It's a painting, which might have snapping issues if it's an even size + // 2. We previously stored it at its tile position, not its entity position + boolean useTilePosition = tilePosition != null + && (state.getType() == EntityTypes.PAINTING || location.toVector().equals(tilePosition)); + if (useTilePosition) { + location = location.setPosition(tilePosition.add(0.5, 0.5, 0.5)); + } Vector3 pivot = from.round().add(0.5, 0.5, 0.5); Vector3 newPosition = transform.apply(location.toVector().subtract(pivot)); - if (hasTilePosition) { + if (useTilePosition) { newPosition = newPosition.subtract(0.5, 0.5, 0.5); } Vector3 newDirection;