diff --git a/src/main/java/net/minestom/server/collision/BlockCollision.java b/src/main/java/net/minestom/server/collision/BlockCollision.java index f4974963f..eea920543 100644 --- a/src/main/java/net/minestom/server/collision/BlockCollision.java +++ b/src/main/java/net/minestom/server/collision/BlockCollision.java @@ -329,7 +329,7 @@ final class BlockCollision { Vec.ZERO, finalResult.collidedShapePosition, finalResult.blockType); } - static boolean canPlaceBlockAt(Instance instance, Point blockPos, Block b) { + static Entity canPlaceBlockAt(Instance instance, Point blockPos, Block b) { for (Entity entity : instance.getNearbyEntities(blockPos, 3)) { final EntityType type = entity.getEntityType(); if (type == EntityType.ITEM) @@ -348,9 +348,9 @@ final class BlockCollision { } else { intersects = b.registry().collisionShape().intersectBox(entity.getPosition().sub(blockPos), entity.getBoundingBox()); } - if (intersects) return false; + if (intersects) return entity; } - return true; + return null; } /** diff --git a/src/main/java/net/minestom/server/collision/CollisionUtils.java b/src/main/java/net/minestom/server/collision/CollisionUtils.java index 5b567d20f..4bc6974ca 100644 --- a/src/main/java/net/minestom/server/collision/CollisionUtils.java +++ b/src/main/java/net/minestom/server/collision/CollisionUtils.java @@ -80,7 +80,7 @@ public final class CollisionUtils { return handlePhysics(entity, entityVelocity, null); } - public static boolean canPlaceBlockAt(Instance instance, Point blockPos, Block b) { + public static Entity canPlaceBlockAt(Instance instance, Point blockPos, Block b) { return BlockCollision.canPlaceBlockAt(instance, blockPos, b); } diff --git a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java index 0525ba5b2..d0394b7cf 100644 --- a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java +++ b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java @@ -3,6 +3,7 @@ package net.minestom.server.listener; import net.minestom.server.MinecraftServer; import net.minestom.server.collision.CollisionUtils; import net.minestom.server.coordinate.Point; +import net.minestom.server.entity.Entity; import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.event.EventDispatcher; @@ -105,9 +106,18 @@ public class BlockPlacementListener { } final Block placedBlock = useMaterial.block(); - if (!CollisionUtils.canPlaceBlockAt(instance, placementPosition, placedBlock)) { + Entity collisionEntity = CollisionUtils.canPlaceBlockAt(instance, placementPosition, placedBlock); + if (collisionEntity != null) { + // If a player is trying to place a block on themselves, the client will send a block change but will not set the block on the client + // For this reason, the block doesn't need to be updated for the client + + // Client also doesn't predict placement of blocks on entities, but we need to refresh for cases where bounding boxes on the server don't match the client + if (collisionEntity != player) + refresh(player, chunk); + return; } + // BlockPlaceEvent check PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent(player, placedBlock, blockFace, placementPosition, packet.hand()); playerBlockPlaceEvent.consumeBlock(player.getGameMode() != GameMode.CREATIVE); diff --git a/src/test/java/net/minestom/server/collision/PlacementCollisionIntegrationTest.java b/src/test/java/net/minestom/server/collision/PlacementCollisionIntegrationTest.java index abd5674f4..62b5205e0 100644 --- a/src/test/java/net/minestom/server/collision/PlacementCollisionIntegrationTest.java +++ b/src/test/java/net/minestom/server/collision/PlacementCollisionIntegrationTest.java @@ -9,8 +9,7 @@ import net.minestom.server.entity.EntityType; import net.minestom.server.instance.block.Block; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; @EnvTest public class PlacementCollisionIntegrationTest { @@ -18,27 +17,27 @@ public class PlacementCollisionIntegrationTest { @Test public void empty(Env env) { var instance = env.createFlatInstance(); - assertTrue(BlockCollision.canPlaceBlockAt(instance, new Vec(0, 40, 0), Block.STONE)); + assertNull(BlockCollision.canPlaceBlockAt(instance, new Vec(0, 40, 0), Block.STONE)); } @Test public void entityBlock(Env env) { var instance = env.createFlatInstance(); new Entity(EntityType.ZOMBIE).setInstance(instance, new Pos(0, 40, 0)).join(); - assertFalse(BlockCollision.canPlaceBlockAt(instance, new Vec(0, 40, 0), Block.STONE)); + assertNotNull(BlockCollision.canPlaceBlockAt(instance, new Vec(0, 40, 0), Block.STONE)); } @Test public void slab(Env env) { var instance = env.createFlatInstance(); new Entity(EntityType.ZOMBIE).setInstance(instance, new Pos(0, 40.75, 0)).join(); - assertTrue(BlockCollision.canPlaceBlockAt(instance, new Vec(0, 40, 0), Block.STONE_SLAB)); + assertNull(BlockCollision.canPlaceBlockAt(instance, new Vec(0, 40, 0), Block.STONE_SLAB)); } @Test public void belowPlayer(Env env) { var instance = env.createFlatInstance(); env.createPlayer(instance, new Pos(5.7, -8, 6.389)); - assertTrue(BlockCollision.canPlaceBlockAt(instance, new Vec(5, -9, 6), Block.STONE)); + assertNull(BlockCollision.canPlaceBlockAt(instance, new Vec(5, -9, 6), Block.STONE)); } }