Solved Need Help Pasting Schematics One Layer at a Time

Discussion in 'Spigot Plugin Development' started by Daimyo, Jun 26, 2020.

  1. As the title states, I'm creating a build system where I need to get a schematic and paste it one layer at a time.

    I've made a prototype and it works pretty well aside from the fact that it doesn't preserve any block data and just sets the new material.
    This results in all stairs facing one way, beds becoming malformed, etc.

    Here's the code I have now.

    Code (Text):
    /**
         * Begin the slow construction of a schematic at a location
         *
         * @param plugin Your plugin instance
         * @param location The location to use for placement
         * @param file The schematic file to paste
         */
        public static void buildSchematic(JavaPlugin plugin, Location location, File file) throws IOException {
            ClipboardFormat format = ClipboardFormats.findByFile(file);

            if (format == null) {
                Bukkit.getLogger().log(Level.SEVERE, "[" + plugin.getName() + "] Unable to load a ClipboardFormat!");
                return;
            }
            ClipboardReader reader = format.getReader(new FileInputStream(file));
            Clipboard clipboard = reader.read();
            Direction direction = Util.getCardinalDirection(location);
            BlockVector3 minPoint = clipboard.getMinimumPoint();
            BukkitRunnable runnable = new BukkitRunnable() {
                int y = 0;

                @Override
                public void run() {
                    List<BlockVector3> blocks = getBlocksFromClipboardAtY(clipboard, y);

                    if (y > clipboard.getRegion().getHeight()) {
                        cancel();
                        return;
                    }

                    for (BlockVector3 block : blocks) {
                        Location intendedLocation = location.clone();

                        switch (direction) {
                            case NORTH: {
                                intendedLocation.add((clipboard.getRegion().getWidth() / 2) * -1, 0, (clipboard.getRegion().getLength() / 2) * -1);
                                intendedLocation.add(block.getBlockX() - minPoint.getBlockX(), y, (block.getBlockZ() - minPoint.getBlockZ()) * -1);
                                break;
                            }
                            case EAST: {
                                intendedLocation.add(clipboard.getRegion().getWidth() / 2, 0, (clipboard.getRegion().getLength() / 2) * -1);
                                intendedLocation.add(block.getBlockZ() - minPoint.getBlockZ(), y, block.getBlockX() - minPoint.getBlockX());
                                break;
                            }
                            case WEST: {
                                intendedLocation.add((clipboard.getRegion().getWidth() / 2) * -1, 0, (clipboard.getRegion().getLength() / 2) * -1);
                                intendedLocation.add((block.getBlockZ() - minPoint.getBlockZ()) * -1, y, block.getBlockX() - minPoint.getBlockX());
                                break;
                            }
                            case SOUTH: {
                                intendedLocation.add(clipboard.getRegion().getWidth() / 2, 0, clipboard.getRegion().getLength() / 2);
                                intendedLocation.add((block.getBlockX() - minPoint.getBlockX()) * -1, y, block.getBlockZ() - minPoint.getBlockZ());
                                break;
                            }
                        }
                        BlockState blockState = clipboard.getBlock(block);
                        Material blockMaterial = BukkitAdapter.adapt(blockState.getBlockType());

                        intendedLocation.getBlock().setType(blockMaterial);
                    }

                    y += 1;
                }
            };

            runnable.runTaskTimer(plugin, 0L, 20L);
        }

        public static List<BlockVector3> getBlocksFromClipboardAtY(Clipboard clipboard, int y) {
            BlockVector3 minPoint = clipboard.getMinimumPoint();
            BlockVector3 maxPoint = clipboard.getMaximumPoint();
            List<BlockVector3> blockVector3s = new ArrayList<>();

            for (int x = 0; x <= maxPoint.getBlockX() - minPoint.getBlockX(); x++) {
                for (int z = 0; z <= maxPoint.getBlockZ() - minPoint.getBlockZ(); z++) {
                    BlockVector3 relative = minPoint.add(x, y, z);
                    BlockState block = clipboard.getBlock(relative);

                    if (block.getBlockType().getMaterial().isAir()) {
                        continue;
                    }

                    blockVector3s.add(relative);
                }
            }

            return blockVector3s;
        }

    All I need to know at this point is how to properly place a BlockVector3 or if I'm overcomplicating this and if there's an easier way to go about this.
     
  2. Bump, still looking for help on this
     
  3. If you take a look at BlockState, you can actually just get the block at the intended location and set it's BlockState to the captured state in the schematic. This will set the Type, Data, etc. of the block to whatever was saved. Only downside to this operation would be trying to paste in different orientations. Most blocks don't have directional values, but for those that do, it would look funny. For this, you might use the WE API to actually rotate the schematic for you, from there you should be able to run the same process you're using to paste from the ground up.
     
    • Useful Useful x 1
  4. For anyone who stumbles across this post I looked a little more into what @Travja said.

    I got the WE BaseBlock from a clipboard and took got its states in the form of a Map<Property<?>, Object>.

    From there I looped through the key set and got the set value (Being the object).
    Then I implemented checks using property.getName() and was able to obtain the data.

    Here's some code demonstrating it with a few examples of preserving a slab's position and waterlogged state.

    Code (Java):
    public static void buildSchematic(JavaPlugin plugin, Location location, File file) throws IOException {
            ClipboardFormat format = ClipboardFormats.findByFile(file);

            if (format == null) {
                Bukkit.getLogger().log(Level.SEVERE, "[" + plugin.getName() + "] Unable to load a ClipboardFormat!");
                return;
            }
            ClipboardReader reader = format.getReader(new FileInputStream(file));
            Clipboard clipboard = reader.read();
            Direction direction = Util.getCardinalDirection(location);
            BlockVector3 minPoint = clipboard.getMinimumPoint();
            BukkitRunnable runnable = new BukkitRunnable() {
                int y = 0;

                @Override
                public void run() {
                    List<BlockVector3> blocks = getBlocksFromClipboardAtY(clipboard, y);

                    if (y > clipboard.getRegion().getHeight()) {
                        cancel();
                        return;
                    }

                    for (BlockVector3 block : blocks) {
                        Location intendedLocation = location.clone();
                        BaseBlock baseBlock = clipboard.getFullBlock(block);
                        Map<Property<?>, Object> propertyMap = baseBlock.getStates();

                        switch (direction) {
                            case NORTH: {
                                intendedLocation.add((clipboard.getRegion().getWidth() / 2) * -1, 0, (clipboard.getRegion().getLength() / 2) * -1);
                                intendedLocation.add(block.getBlockX() - minPoint.getBlockX(), y, (block.getBlockZ() - minPoint.getBlockZ()) * -1);
                                break;
                            }
                            case EAST: {
                                intendedLocation.add(clipboard.getRegion().getWidth() / 2, 0, (clipboard.getRegion().getLength() / 2) * -1);
                                intendedLocation.add(block.getBlockZ() - minPoint.getBlockZ(), y, block.getBlockX() - minPoint.getBlockX());
                                break;
                            }
                            case WEST: {
                                intendedLocation.add((clipboard.getRegion().getWidth() / 2) * -1, 0, (clipboard.getRegion().getLength() / 2) * -1);
                                intendedLocation.add((block.getBlockZ() - minPoint.getBlockZ()) * -1, y, block.getBlockX() - minPoint.getBlockX());
                                break;
                            }
                            case SOUTH: {
                                intendedLocation.add(clipboard.getRegion().getWidth() / 2, 0, clipboard.getRegion().getLength() / 2);
                                intendedLocation.add((block.getBlockX() - minPoint.getBlockX()) * -1, y, block.getBlockZ() - minPoint.getBlockZ());
                                break;
                            }
                        }
                        BlockState blockState = clipboard.getBlock(block);
                        Material blockMaterial = BukkitAdapter.adapt(blockState.getBlockType());
                        Block bukkitBlock = intendedLocation.getBlock();

                        bukkitBlock.setType(blockMaterial);
                        org.bukkit.block.BlockState state = bukkitBlock.getState();

                        for (Property<?> property : propertyMap.keySet()) {
                            BlockData blockData = state.getBlockData();
                            Object value = propertyMap.get(property);

                            if (property.getName().equalsIgnoreCase("type") && blockData instanceof Slab) {
                                Slab slabData = (Slab) blockData;
                                Slab.Type slabType = Slab.Type.valueOf(value.toString().toUpperCase());

                                slabData.setType(slabType);
                                state.setBlockData(blockData);
                                state.update();
                            }

                            if (property.getName().equalsIgnoreCase("waterlogged") && blockData instanceof Waterlogged) {
                                Waterlogged waterData = (Waterlogged) blockData;
                                boolean waterlogged = Boolean.parseBoolean(value.toString());

                                waterData.setWaterlogged(waterlogged);
                                state.setBlockData(waterData);
                                state.update();
                            }
                        }
                    }

                    y += 1;
                }
            };

            runnable.runTaskTimer(plugin, 0L, 20L);
        }
     
  5. Looking at this bit of code, you'll notice that you're actually setting the BlockData to blockData. I'm not sure if this does indeed work or not; however, there should be a much shorter and graceful solution to the problem.

    This line of code should contain everything you need, but all you're getting out of it currently is the blocks Material. BlockState is inherted by any number of things from chests, enchanting tables, furnaces, you name it. Furthermore, looking at the Javadocs, we can see that there is actually a #getBlockData method that covers just about all the rest of specific information for our various blocks. Thus, we should be able to set the BlockState at the desired location to the BlockState retrieved from the clipboard. This will set the BlockData properties to exactly what they were when the schematic was created. This eliminates the need for complex checks and digging super deep into property values.

    Hopefully that all makes sense. If that doesn't work, let me know. I don't want to be feeding you false information.
     
  6. The BlockState I declare is not Bukkit's BlockState. It's WorldEdit's BlockState, you can see it here
     
    #6 Daimyo, Jul 1, 2020 at 6:44 AM
    Last edited: Jul 2, 2020 at 1:05 AM
    • Like Like x 1
  7. Ah! I didn't realize that. Thanks for correcting me!
     
    • Friendly Friendly x 1