|  | 
| 23 | 23 | 
 | 
| 24 | 24 | import org.bukkit.DyeColor; | 
| 25 | 25 | import org.bukkit.Material; | 
|  | 26 | +import org.bukkit.SkullType; | 
| 26 | 27 | import org.bukkit.TreeSpecies; | 
| 27 | 28 | import org.bukkit.block.Banner; | 
| 28 |  | -import org.bukkit.block.Block; | 
| 29 |  | -import org.bukkit.block.BlockFace; | 
| 30 |  | -import org.bukkit.block.BlockState; | 
|  | 29 | +import org.bukkit.block.Skull; | 
|  | 30 | +import org.bukkit.block.*; | 
| 31 | 31 | import org.bukkit.block.data.BlockData; | 
| 32 | 32 | import org.bukkit.inventory.InventoryHolder; | 
| 33 | 33 | import org.bukkit.material.*; | 
|  | 
| 46 | 46 |  * This class doesn't and shouldn't support materials that are {@link Material#isLegacy()}. | 
| 47 | 47 |  * | 
| 48 | 48 |  * @author Crypto Morin | 
| 49 |  | - * @version 2.2.1 | 
|  | 49 | + * @version 3.0.0 | 
| 50 | 50 |  * @see Block | 
| 51 | 51 |  * @see BlockState | 
| 52 | 52 |  * @see MaterialData | 
| @@ -247,12 +247,19 @@ public static boolean setType(@Nonnull Block block, @Nullable XMaterial material | 
| 247 | 247 |         if (material == null) material = XMaterial.AIR; | 
| 248 | 248 |         XMaterial smartConversion = ITEM_TO_BLOCK.get(material); | 
| 249 | 249 |         if (smartConversion != null) material = smartConversion; | 
| 250 |  | -        if (material.parseMaterial() == null) return false; | 
| 251 | 250 | 
 | 
| 252 |  | -        block.setType(material.parseMaterial(), applyPhysics); | 
|  | 251 | +        Material parsedMat = material.parseMaterial(); | 
|  | 252 | +        if (parsedMat == null) return false; | 
|  | 253 | + | 
|  | 254 | +        String parsedName = parsedMat.name(); | 
|  | 255 | + | 
|  | 256 | +        // SKULL_ITEM is for items and SKULL is for blocks. | 
|  | 257 | +        SkullType skullType = getSkullType(material); | 
|  | 258 | +        if (skullType != null) parsedMat = Material.valueOf("SKULL"); | 
|  | 259 | + | 
|  | 260 | +        block.setType(parsedMat, applyPhysics); | 
| 253 | 261 |         if (ISFLAT) return false; | 
| 254 | 262 | 
 | 
| 255 |  | -        String parsedName = material.parseMaterial().name(); | 
| 256 | 263 |         if (parsedName.endsWith("_ITEM")) { | 
| 257 | 264 |             String blockName = parsedName.substring(0, parsedName.length() - "_ITEM".length()); | 
| 258 | 265 |             Material blockMaterial = Objects.requireNonNull(Material.getMaterial(blockName), | 
| @@ -352,14 +359,53 @@ public static boolean setType(@Nonnull Block block, @Nullable XMaterial material | 
| 352 | 359 |                     throw new AssertionError("Unknown block type " + legacyMaterial + " for tree species: " + species); | 
| 353 | 360 |             } | 
| 354 | 361 |         } else if (material.getData() != 0) { | 
| 355 |  | -            state.setRawData(material.getData()); | 
|  | 362 | +            if (skullType != null) { | 
|  | 363 | +                boolean isWallSkull = material.name().contains("WALL"); | 
|  | 364 | +                state.setRawData((byte) (isWallSkull ? 0 : 1)); | 
|  | 365 | +            } else { | 
|  | 366 | +                state.setRawData(material.getData()); | 
|  | 367 | +            } | 
|  | 368 | +            update = true; | 
|  | 369 | +        } | 
|  | 370 | + | 
|  | 371 | +        if (skullType != null) { | 
|  | 372 | +            Skull skull = (Skull) state; | 
|  | 373 | +            skull.setSkullType(skullType); | 
| 356 | 374 |             update = true; | 
| 357 | 375 |         } | 
| 358 | 376 | 
 | 
| 359 |  | -        if (update) state.update(false, applyPhysics); | 
|  | 377 | +        if (update) state.update(true, applyPhysics); | 
| 360 | 378 |         return update; | 
| 361 | 379 |     } | 
| 362 | 380 | 
 | 
|  | 381 | +    public static SkullType getSkullType(XMaterial material) { | 
|  | 382 | +        switch (material) { | 
|  | 383 | +            case PLAYER_HEAD: | 
|  | 384 | +            case PLAYER_WALL_HEAD: | 
|  | 385 | +                return SkullType.PLAYER; | 
|  | 386 | +            case DRAGON_HEAD: | 
|  | 387 | +            case DRAGON_WALL_HEAD: | 
|  | 388 | +                return SkullType.DRAGON; | 
|  | 389 | +            case ZOMBIE_HEAD: | 
|  | 390 | +            case ZOMBIE_WALL_HEAD: | 
|  | 391 | +                return SkullType.ZOMBIE; | 
|  | 392 | +            case CREEPER_HEAD: | 
|  | 393 | +            case CREEPER_WALL_HEAD: | 
|  | 394 | +                return SkullType.CREEPER; | 
|  | 395 | +            case SKELETON_SKULL: | 
|  | 396 | +            case SKELETON_WALL_SKULL: | 
|  | 397 | +                return SkullType.SKELETON; | 
|  | 398 | +            case WITHER_SKELETON_SKULL: | 
|  | 399 | +            case WITHER_SKELETON_WALL_SKULL: | 
|  | 400 | +                return SkullType.WITHER; | 
|  | 401 | +            case PIGLIN_HEAD: | 
|  | 402 | +            case PIGLIN_WALL_HEAD: | 
|  | 403 | +                return SkullType.PIGLIN; | 
|  | 404 | +            default: | 
|  | 405 | +                return null; | 
|  | 406 | +        } | 
|  | 407 | +    } | 
|  | 408 | + | 
| 363 | 409 |     public static boolean setType(@Nonnull Block block, @Nullable XMaterial material) { | 
| 364 | 410 |         return setType(block, material, true); | 
| 365 | 411 |     } | 
| @@ -726,26 +772,61 @@ public static void setOpened(Block block, boolean opened) { | 
| 726 | 772 |         Openable openable = (Openable) state.getData(); | 
| 727 | 773 |         openable.setOpen(opened); | 
| 728 | 774 |         state.setData((MaterialData) openable); | 
| 729 |  | -        state.update(); | 
|  | 775 | +        state.update(true, true); | 
| 730 | 776 |     } | 
| 731 | 777 | 
 | 
| 732 | 778 |     public static BlockFace getRotation(Block block) { | 
| 733 | 779 |         if (ISFLAT) { | 
| 734 |  | -            if (!(block.getBlockData() instanceof org.bukkit.block.data.Rotatable)) return null; | 
| 735 |  | -            org.bukkit.block.data.Rotatable rotatable = (org.bukkit.block.data.Rotatable) block.getBlockData(); | 
| 736 |  | -            return rotatable.getRotation(); | 
|  | 780 | +            BlockData blockData = block.getBlockData(); | 
|  | 781 | +            if (blockData instanceof org.bukkit.block.data.Rotatable) { | 
|  | 782 | +                return ((org.bukkit.block.data.Rotatable) blockData).getRotation(); | 
|  | 783 | +            } else if (blockData instanceof org.bukkit.block.data.Directional) { | 
|  | 784 | +                return ((org.bukkit.block.data.Directional) blockData).getFacing(); | 
|  | 785 | +            } | 
|  | 786 | +        } else { | 
|  | 787 | +            BlockState state = block.getState(); | 
|  | 788 | +            if (state instanceof Skull) { | 
|  | 789 | +                return ((Skull) state).getRotation(); | 
|  | 790 | +            } else { | 
|  | 791 | +                MaterialData data = state.getData(); | 
|  | 792 | +                if (data instanceof Directional) { | 
|  | 793 | +                    return ((Directional) data).getFacing(); | 
|  | 794 | +                } | 
|  | 795 | +            } | 
| 737 | 796 |         } | 
| 738 | 797 | 
 | 
| 739 | 798 |         return null; | 
| 740 | 799 |     } | 
| 741 | 800 | 
 | 
| 742 | 801 |     public static void setRotation(Block block, BlockFace facing) { | 
| 743 | 802 |         if (ISFLAT) { | 
| 744 |  | -            if (!(block.getBlockData() instanceof org.bukkit.block.data.Rotatable)) return; | 
| 745 |  | -            BlockData data = block.getBlockData(); | 
| 746 |  | -            org.bukkit.block.data.Rotatable rotatable = (org.bukkit.block.data.Rotatable) data; | 
| 747 |  | -            rotatable.setRotation(facing); | 
| 748 |  | -            block.setBlockData(data, false); | 
|  | 803 | +            BlockData blockData = block.getBlockData(); | 
|  | 804 | +            if (blockData instanceof org.bukkit.block.data.Rotatable) { | 
|  | 805 | +                ((org.bukkit.block.data.Rotatable) blockData).setRotation(facing); | 
|  | 806 | +            } else if (blockData instanceof org.bukkit.block.data.Directional) { | 
|  | 807 | +                ((org.bukkit.block.data.Directional) blockData).setFacing(facing); | 
|  | 808 | +            } | 
|  | 809 | +            block.setBlockData(blockData, false); | 
|  | 810 | +        } else { | 
|  | 811 | +            BlockState state = block.getState(); | 
|  | 812 | +            if (state instanceof Skull) { | 
|  | 813 | +                // Special case because the raw data is used for both rotation and | 
|  | 814 | +                // whether the block should be considered a wall skull or a normal skull | 
|  | 815 | +                // on the ground. Some of the raw data values represent wall skulls with | 
|  | 816 | +                // various rotations, but only values that represent normal skull values | 
|  | 817 | +                // on the ground only has one rotation. | 
|  | 818 | +                // https://www.spigotmc.org/threads/getting-player-skull-to-spawn-on-top-of-fence.385083/ | 
|  | 819 | +                // https://www.spigotmc.org/threads/placing-skull-in-world.212900/ | 
|  | 820 | +                // https://www.spigotmc.org/threads/skull-position-above-block-1-13.343247/ | 
|  | 821 | +                // https://www.spigotmc.org/threads/solved-update-skull-rotation.47795/ | 
|  | 822 | +                ((Skull) state).setRotation(facing); | 
|  | 823 | +            } else { | 
|  | 824 | +                MaterialData data = state.getData(); | 
|  | 825 | +                if (!(data instanceof Directional)) return; | 
|  | 826 | +                Directional directional = (Directional) data; | 
|  | 827 | +                directional.setFacingDirection(facing); | 
|  | 828 | +            } | 
|  | 829 | +            state.update(true, true); | 
| 749 | 830 |         } | 
| 750 | 831 |     } | 
| 751 | 832 | 
 | 
|  | 
0 commit comments