|  | 
| 25 | 25 | import org.bukkit.DyeColor; | 
| 26 | 26 | import org.bukkit.Material; | 
| 27 | 27 | import org.bukkit.TreeSpecies; | 
|  | 28 | +import org.bukkit.block.Banner; | 
| 28 | 29 | import org.bukkit.block.Block; | 
| 29 | 30 | import org.bukkit.block.BlockFace; | 
| 30 | 31 | import org.bukkit.block.BlockState; | 
| 31 | 32 | import org.bukkit.inventory.InventoryHolder; | 
| 32 | 33 | import org.bukkit.material.*; | 
| 33 | 34 | 
 | 
|  | 35 | +import javax.annotation.Nonnull; | 
| 34 | 36 | import javax.annotation.Nullable; | 
| 35 | 37 | import java.util.*; | 
| 36 | 38 | 
 | 
| @@ -62,10 +64,26 @@ public final class XBlock { | 
| 62 | 64 |     )); | 
| 63 | 65 |     public static final byte CAKE_SLICES = 6; | 
| 64 | 66 |     private static final boolean ISFLAT = XMaterial.supports(13); | 
|  | 67 | +    private static final Map<XMaterial, XMaterial> ITEM_TO_BLOCK = new EnumMap<>(XMaterial.class); | 
| 65 | 68 | 
 | 
| 66 |  | -    private XBlock() { | 
|  | 69 | +    static { | 
|  | 70 | +        ITEM_TO_BLOCK.put(XMaterial.MELON_SLICE, XMaterial.MELON_STEM); | 
|  | 71 | +        ITEM_TO_BLOCK.put(XMaterial.MELON_SEEDS, XMaterial.MELON_STEM); | 
|  | 72 | + | 
|  | 73 | +        ITEM_TO_BLOCK.put(XMaterial.CARROT_ON_A_STICK, XMaterial.CARROTS); | 
|  | 74 | +        ITEM_TO_BLOCK.put(XMaterial.GOLDEN_CARROT, XMaterial.CARROTS); | 
|  | 75 | +        ITEM_TO_BLOCK.put(XMaterial.CARROT, XMaterial.CARROTS); | 
|  | 76 | + | 
|  | 77 | +        ITEM_TO_BLOCK.put(XMaterial.POTATO, XMaterial.POTATOES); | 
|  | 78 | +        ITEM_TO_BLOCK.put(XMaterial.BAKED_POTATO, XMaterial.POTATOES); | 
|  | 79 | +        ITEM_TO_BLOCK.put(XMaterial.POISONOUS_POTATO, XMaterial.POTATOES); | 
|  | 80 | + | 
|  | 81 | +        ITEM_TO_BLOCK.put(XMaterial.PUMPKIN_SEEDS, XMaterial.PUMPKIN_STEM); | 
|  | 82 | +        ITEM_TO_BLOCK.put(XMaterial.PUMPKIN_PIE, XMaterial.PUMPKIN); | 
| 67 | 83 |     } | 
| 68 | 84 | 
 | 
|  | 85 | +    private XBlock() { } | 
|  | 86 | + | 
| 69 | 87 |     public static boolean isLit(Block block) { | 
| 70 | 88 |         if (ISFLAT) { | 
| 71 | 89 |             if (!(block.getBlockData() instanceof org.bukkit.block.data.Lightable)) return false; | 
| @@ -213,25 +231,114 @@ public static boolean setDirection(Block block, BlockFace facing) { | 
| 213 | 231 |         return false; | 
| 214 | 232 |     } | 
| 215 | 233 | 
 | 
|  | 234 | +    public static boolean setType(@Nonnull Block block, @Nullable XMaterial material) { | 
|  | 235 | +        Objects.requireNonNull(block, "Cannot set type of null block"); | 
|  | 236 | +        if (material == null) material = XMaterial.AIR; | 
|  | 237 | +        XMaterial smartConversion = ITEM_TO_BLOCK.get(material); | 
|  | 238 | +        if (smartConversion != null) material = smartConversion; | 
|  | 239 | +        if (material.parseMaterial() == null) return false; | 
| 216 | 240 | 
 | 
| 217 |  | -    private static String getMetaString(XMaterial material) { | 
| 218 |  | -        return material.name().substring(0, material.name().indexOf('_')); | 
| 219 |  | -    } | 
| 220 |  | - | 
| 221 |  | -    public static boolean setType(Block block, XMaterial material) { | 
| 222 | 241 |         block.setType(material.parseMaterial()); | 
| 223 | 242 |         if (XMaterial.supports(13)) return false; | 
| 224 | 243 | 
 | 
|  | 244 | +        String parsedName = material.parseMaterial().name(); | 
|  | 245 | +        if (parsedName.endsWith("_ITEM")) { | 
|  | 246 | +            String blockName = parsedName.substring(0, parsedName.length() - "_ITEM".length()); | 
|  | 247 | +            Material blockMaterial = Objects.requireNonNull(Material.getMaterial(blockName), | 
|  | 248 | +                    () -> "Could not find block material for item '" + parsedName + "' as '" + blockName + '\''); | 
|  | 249 | +            block.setType(blockMaterial); | 
|  | 250 | +        } else if (parsedName.contains("CAKE")) { | 
|  | 251 | +            Material blockMaterial = Material.getMaterial("CAKE_BLOCK"); | 
|  | 252 | +            block.setType(blockMaterial); | 
|  | 253 | +        } | 
|  | 254 | + | 
|  | 255 | +        LegacyMaterial legacyMaterial = LegacyMaterial.getMaterial(parsedName); | 
|  | 256 | +        if (legacyMaterial == LegacyMaterial.BANNER) block.setType(LegacyMaterial.STANDING_BANNER.material); | 
|  | 257 | +        LegacyMaterial.Handling handling = legacyMaterial == null ? null : legacyMaterial.handling; | 
|  | 258 | + | 
| 225 | 259 |         BlockState state = block.getState(); | 
| 226 |  | -        MaterialData data = state.getData(); | 
| 227 | 260 |         boolean update = false; | 
| 228 | 261 | 
 | 
| 229 |  | -        if (data instanceof Wood) { | 
| 230 |  | -            Wood wood = (Wood) data; | 
| 231 |  | -            wood.setSpecies(TreeSpecies.valueOf(getMetaString(material))); | 
|  | 262 | +        if (handling == LegacyMaterial.Handling.COLORABLE) { | 
|  | 263 | +            if (state instanceof Banner) { | 
|  | 264 | +                Banner banner = (Banner) state; | 
|  | 265 | +                String xName = material.name(); | 
|  | 266 | +                int colorIndex = xName.indexOf('_'); | 
|  | 267 | +                String color = xName.substring(0, colorIndex); | 
|  | 268 | +                if (color.equals("LIGHT")) color = xName.substring(0, "LIGHT_".length() + 4); | 
|  | 269 | + | 
|  | 270 | +                banner.setBaseColor(DyeColor.valueOf(color)); | 
|  | 271 | +            } else state.setRawData(material.getData()); | 
| 232 | 272 |             update = true; | 
| 233 |  | -        } else if (data instanceof Colorable) { | 
| 234 |  | -            ((Colorable) data).setColor(DyeColor.valueOf(getMetaString(material))); | 
|  | 273 | +        } else if (handling == LegacyMaterial.Handling.WOOD_SPECIES) { | 
|  | 274 | +            // Wood doesn't exist in 1.8 | 
|  | 275 | +            // https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/material/Wood.java?until=7d83cba0f2575112577ed7a091ed8a193bfc261a&untilPath=src%2Fmain%2Fjava%2Forg%2Fbukkit%2Fmaterial%2FWood.java | 
|  | 276 | +            // https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/TreeSpecies.java | 
|  | 277 | + | 
|  | 278 | +            String name = material.name(); | 
|  | 279 | +            int firstIndicator = name.indexOf('_'); | 
|  | 280 | +            if (firstIndicator < 0) return false; | 
|  | 281 | +            String woodType = name.substring(0, firstIndicator); | 
|  | 282 | + | 
|  | 283 | +            TreeSpecies species; | 
|  | 284 | +            switch (woodType) { | 
|  | 285 | +                case "OAK": | 
|  | 286 | +                    species = TreeSpecies.GENERIC; | 
|  | 287 | +                    break; | 
|  | 288 | +                case "DARK": | 
|  | 289 | +                    species = TreeSpecies.DARK_OAK; | 
|  | 290 | +                    break; | 
|  | 291 | +                case "SPRUCE": | 
|  | 292 | +                    species = TreeSpecies.REDWOOD; | 
|  | 293 | +                    break; | 
|  | 294 | +                default: { | 
|  | 295 | +                    try { | 
|  | 296 | +                        species = TreeSpecies.valueOf(woodType); | 
|  | 297 | +                    } catch (IllegalArgumentException ex) { | 
|  | 298 | +                        throw new AssertionError("Unknown material " + legacyMaterial + " for wood species"); | 
|  | 299 | +                    } | 
|  | 300 | +                } | 
|  | 301 | +            } | 
|  | 302 | + | 
|  | 303 | +            // Doesn't handle stairs, slabs, fence and fence gates as they had their own separate materials. | 
|  | 304 | +            boolean firstType = false; | 
|  | 305 | +            switch (legacyMaterial) { | 
|  | 306 | +                case WOOD: | 
|  | 307 | +                case WOOD_DOUBLE_STEP: | 
|  | 308 | +                    state.setRawData(species.getData()); | 
|  | 309 | +                    update = true; | 
|  | 310 | +                    break; | 
|  | 311 | +                case LOG: | 
|  | 312 | +                case LEAVES: | 
|  | 313 | +                    firstType = true; | 
|  | 314 | +                    // fall through to next switch statement below | 
|  | 315 | +                case LOG_2: | 
|  | 316 | +                case LEAVES_2: | 
|  | 317 | +                    switch (species) { | 
|  | 318 | +                        case GENERIC: | 
|  | 319 | +                        case REDWOOD: | 
|  | 320 | +                        case BIRCH: | 
|  | 321 | +                        case JUNGLE: | 
|  | 322 | +                            if (!firstType) throw new AssertionError("Invalid tree species " + species + " for block type" + legacyMaterial + ", use block type 2 instead"); | 
|  | 323 | +                            break; | 
|  | 324 | +                        case ACACIA: | 
|  | 325 | +                        case DARK_OAK: | 
|  | 326 | +                            if (firstType) throw new AssertionError("Invalid tree species " + species + " for block type 2 " + legacyMaterial + ", use block type instead"); | 
|  | 327 | +                            break; | 
|  | 328 | +                    } | 
|  | 329 | +                    state.setRawData((byte) ((state.getRawData() & 0xC) | (species.getData() & 0x3))); | 
|  | 330 | +                    update = true; | 
|  | 331 | +                    break; | 
|  | 332 | +                case SAPLING: | 
|  | 333 | +                case WOOD_STEP: | 
|  | 334 | +                    state.setRawData((byte) ((state.getRawData() & 0x8) | species.getData())); | 
|  | 335 | +                    update = true; | 
|  | 336 | +                    break; | 
|  | 337 | +                default: | 
|  | 338 | +                    throw new AssertionError("Unknown block type " + legacyMaterial + " for tree species: " + species); | 
|  | 339 | +            } | 
|  | 340 | +        } else if (material.getData() != 0) { | 
|  | 341 | +            state.setRawData(material.getData()); | 
| 235 | 342 |             update = true; | 
| 236 | 343 |         } | 
| 237 | 344 | 
 | 
| @@ -423,20 +530,6 @@ public static int addCakeSlices(Block block, int slices) { | 
| 423 | 530 |         } | 
| 424 | 531 |     } | 
| 425 | 532 | 
 | 
| 426 |  | -    public static boolean setWooden(Block block, XMaterial species) { | 
| 427 |  | -        block.setType(species.parseMaterial()); | 
| 428 |  | -        if (ISFLAT) return true; | 
| 429 |  | - | 
| 430 |  | -        TreeSpecies type = species == XMaterial.SPRUCE_LOG ? TreeSpecies.REDWOOD : | 
| 431 |  | -                TreeSpecies.valueOf(getMetaString(species)); | 
| 432 |  | - | 
| 433 |  | -        BlockState state = block.getState(); | 
| 434 |  | -        MaterialData data = state.getData(); | 
| 435 |  | -        ((Wood) data).setSpecies(type); | 
| 436 |  | -        state.update(true); | 
| 437 |  | -        return true; | 
| 438 |  | -    } | 
| 439 |  | - | 
| 440 | 533 |     public static void setEnderPearlOnFrame(Block endPortalFrame, boolean eye) { | 
| 441 | 534 |         BlockState state = endPortalFrame.getState(); | 
| 442 | 535 |         if (ISFLAT) { | 
| @@ -638,6 +731,40 @@ private static boolean isMaterial(Block block, BlockMaterial... materials) { | 
| 638 | 731 |         return false; | 
| 639 | 732 |     } | 
| 640 | 733 | 
 | 
|  | 734 | +    private enum LegacyMaterial { | 
|  | 735 | +        // Colorable | 
|  | 736 | +        STANDING_BANNER(Handling.COLORABLE), WALL_BANNER(Handling.COLORABLE), BANNER(Handling.COLORABLE), | 
|  | 737 | +        CARPET(Handling.COLORABLE), WOOL(Handling.COLORABLE), STAINED_CLAY(Handling.COLORABLE), | 
|  | 738 | +        STAINED_GLASS(Handling.COLORABLE), STAINED_GLASS_PANE(Handling.COLORABLE), THIN_GLASS(Handling.COLORABLE), | 
|  | 739 | + | 
|  | 740 | +        // Wood Species | 
|  | 741 | +        WOOD(Handling.WOOD_SPECIES), WOOD_STEP(Handling.WOOD_SPECIES), WOOD_DOUBLE_STEP(Handling.WOOD_SPECIES), | 
|  | 742 | +        LEAVES(Handling.WOOD_SPECIES), LEAVES_2(Handling.WOOD_SPECIES), | 
|  | 743 | +        LOG(Handling.WOOD_SPECIES), LOG_2(Handling.WOOD_SPECIES), | 
|  | 744 | +        SAPLING(Handling.WOOD_SPECIES); | 
|  | 745 | + | 
|  | 746 | +        private static final Map<String, LegacyMaterial> LOOKUP = new HashMap<>(); | 
|  | 747 | + | 
|  | 748 | +        static { | 
|  | 749 | +            for (LegacyMaterial legacyMaterial : values()) { | 
|  | 750 | +                LOOKUP.put(legacyMaterial.name(), legacyMaterial); | 
|  | 751 | +            } | 
|  | 752 | +        } | 
|  | 753 | + | 
|  | 754 | +        private final Material material = Material.getMaterial(name()); | 
|  | 755 | +        private final Handling handling; | 
|  | 756 | + | 
|  | 757 | +        LegacyMaterial(Handling handling) { | 
|  | 758 | +            this.handling = handling; | 
|  | 759 | +        } | 
|  | 760 | + | 
|  | 761 | +        private static LegacyMaterial getMaterial(String name) { | 
|  | 762 | +            return LOOKUP.get(name); | 
|  | 763 | +        } | 
|  | 764 | + | 
|  | 765 | +        private enum Handling {COLORABLE, WOOD_SPECIES;} | 
|  | 766 | +    } | 
|  | 767 | + | 
| 641 | 768 |     /** | 
| 642 | 769 |      * An enum with cached legacy materials which can be used when comparing blocks with blocks and blocks with items. | 
| 643 | 770 |      * | 
|  | 
0 commit comments