diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java index ad8a529da7..54e7467d17 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java @@ -488,7 +488,7 @@ public abstract class CraftRegionAccessor implements RegionAccessor { } public T spawn(Location location, Class clazz, Consumer function, CreatureSpawnEvent.SpawnReason reason, boolean randomizeData) throws IllegalArgumentException { - net.minecraft.world.entity.Entity entity = createEntity(location, clazz); + net.minecraft.world.entity.Entity entity = createEntity(location, clazz, randomizeData); return addEntity(entity, reason, function, randomizeData); } @@ -522,6 +522,11 @@ public abstract class CraftRegionAccessor implements RegionAccessor { @SuppressWarnings("unchecked") public net.minecraft.world.entity.Entity createEntity(Location location, Class clazz) throws IllegalArgumentException { + return createEntity(location, clazz, true); + } + + @SuppressWarnings("unchecked") + public net.minecraft.world.entity.Entity createEntity(Location location, Class clazz, boolean randomizeData) throws IllegalArgumentException { if (location == null || clazz == null) { throw new IllegalArgumentException("Location or entity class cannot be null"); } @@ -813,52 +818,55 @@ public abstract class CraftRegionAccessor implements RegionAccessor { entity.setHeadRotation(yaw); // SPIGOT-3587 } } else if (Hanging.class.isAssignableFrom(clazz)) { - BlockFace face = BlockFace.SELF; - - int width = 16; // 1 full block, also painting smallest size. - int height = 16; // 1 full block, also painting smallest size. - - if (ItemFrame.class.isAssignableFrom(clazz)) { - width = 12; - height = 12; - } else if (LeashHitch.class.isAssignableFrom(clazz)) { - width = 9; - height = 9; - } - - BlockFace[] faces = new BlockFace[]{BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH, BlockFace.UP, BlockFace.DOWN}; - final BlockPosition pos = new BlockPosition(x, y, z); - for (BlockFace dir : faces) { - IBlockData nmsBlock = getHandle().getType(pos.shift(CraftBlock.blockFaceToNotch(dir))); - if (nmsBlock.getMaterial().isBuildable() || BlockDiodeAbstract.isDiode(nmsBlock)) { - boolean taken = false; - AxisAlignedBB bb = (ItemFrame.class.isAssignableFrom(clazz)) - ? EntityItemFrame.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height) - : EntityHanging.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height); - List list = (List) getHandle().getEntities(null, bb); - for (Iterator it = list.iterator(); !taken && it.hasNext();) { - net.minecraft.world.entity.Entity e = it.next(); - if (e instanceof EntityHanging) { - taken = true; // Hanging entities do not like hanging entities which intersect them. - } - } - - if (!taken) { - face = dir; - break; - } - } - } - if (LeashHitch.class.isAssignableFrom(clazz)) { + // SPIGOT-5732: LeashHitch has no direction and is always centered at a block entity = new EntityLeash(world, new BlockPosition(x, y, z)); } else { + BlockFace face = BlockFace.SELF; + BlockFace[] faces = new BlockFace[]{BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH}; + + int width = 16; // 1 full block, also painting smallest size. + int height = 16; // 1 full block, also painting smallest size. + + if (ItemFrame.class.isAssignableFrom(clazz)) { + width = 12; + height = 12; + faces = new BlockFace[]{BlockFace.EAST, BlockFace.NORTH, BlockFace.WEST, BlockFace.SOUTH, BlockFace.UP, BlockFace.DOWN}; + } + + final BlockPosition pos = new BlockPosition(x, y, z); + for (BlockFace dir : faces) { + IBlockData nmsBlock = getHandle().getType(pos.shift(CraftBlock.blockFaceToNotch(dir))); + if (nmsBlock.getMaterial().isBuildable() || BlockDiodeAbstract.isDiode(nmsBlock)) { + boolean taken = false; + AxisAlignedBB bb = (ItemFrame.class.isAssignableFrom(clazz)) + ? EntityItemFrame.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height) + : EntityHanging.calculateBoundingBox(null, pos, CraftBlock.blockFaceToNotch(dir).opposite(), width, height); + List list = (List) getHandle().getEntities(null, bb); + for (Iterator it = list.iterator(); !taken && it.hasNext(); ) { + net.minecraft.world.entity.Entity e = it.next(); + if (e instanceof EntityHanging) { + taken = true; // Hanging entities do not like hanging entities which intersect them. + } + } + + if (!taken) { + face = dir; + break; + } + } + } + // No valid face found - Preconditions.checkArgument(face != BlockFace.SELF, "Cannot spawn hanging entity for %s at %s (no free face)", clazz.getName(), location); + if (face == BlockFace.SELF) { + // SPIGOT-6387: Allow hanging entities to be placed in midair + face = BlockFace.SOUTH; + randomizeData = false; // Don't randomize if no valid face is found, prevents null painting + } EnumDirection dir = CraftBlock.blockFaceToNotch(face).opposite(); if (Painting.class.isAssignableFrom(clazz)) { - if (isNormalWorld()) { + if (isNormalWorld() && randomizeData) { entity = new EntityPainting(getHandle().getMinecraftWorld(), new BlockPosition(x, y, z), dir); } else { entity = new EntityPainting(EntityTypes.PAINTING, getHandle().getMinecraftWorld()); @@ -873,11 +881,6 @@ public abstract class CraftRegionAccessor implements RegionAccessor { } } } - - // survives call does not work during world generation - if (entity != null && isNormalWorld() && !((EntityHanging) entity).survives()) { - throw new IllegalArgumentException("Cannot spawn hanging entity for " + clazz.getName() + " at " + location); - } } else if (TNTPrimed.class.isAssignableFrom(clazz)) { entity = new EntityTNTPrimed(world, x, y, z, null); } else if (ExperienceOrb.class.isAssignableFrom(clazz)) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java index a98f981712..4f6b924f55 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHanging.java @@ -29,7 +29,6 @@ public class CraftHanging extends CraftEntity implements Hanging { EnumDirection dir = hanging.getDirection(); switch (face) { case SOUTH: - default: getHandle().setDirection(EnumDirection.SOUTH); break; case WEST: @@ -41,6 +40,8 @@ public class CraftHanging extends CraftEntity implements Hanging { case EAST: getHandle().setDirection(EnumDirection.EAST); break; + default: + throw new IllegalArgumentException(String.format("%s is not a valid facing direction", face)); } if (!force && !getHandle().generation && !hanging.survives()) { // Revert since it doesn't fit diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java index 9d4789ac0d..5ddd594c13 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java @@ -26,6 +26,8 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame { EnumDirection oldDir = hanging.getDirection(); EnumDirection newDir = CraftBlock.blockFaceToNotch(face); + Preconditions.checkArgument(newDir != null, "%s is not a valid facing direction", face); + getHandle().setDirection(newDir); if (!force && !getHandle().generation && !hanging.survives()) { hanging.setDirection(oldDir); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java index 2345f72485..8f59c4a098 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLeash.java @@ -1,6 +1,8 @@ package org.bukkit.craftbukkit.entity; +import com.google.common.base.Preconditions; import net.minecraft.world.entity.decoration.EntityLeash; +import org.bukkit.block.BlockFace; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.entity.EntityType; import org.bukkit.entity.LeashHitch; @@ -10,6 +12,19 @@ public class CraftLeash extends CraftHanging implements LeashHitch { super(server, entity); } + @Override + public boolean setFacingDirection(BlockFace face, boolean force) { + Preconditions.checkArgument(face == BlockFace.SELF, "%s is not a valid facing direction", face); + + return force || getHandle().generation || getHandle().survives(); + } + + @Override + public BlockFace getFacing() { + // Leash hitch has no facing direction, so we return self + return BlockFace.SELF; + } + @Override public EntityLeash getHandle() { return (EntityLeash) entity;