Build a Schematic block by block

Discussion in 'Plugin Development' started by plisov, Dec 26, 2017.

Thread Status:
Not open for further replies.
  1. Offline

    plisov

    Hey,
    I've been trying to get a schematic file to build itself over a period of time. Currently I have the plugin placing the schematic immediately. What I'm shooting for is a method which takes in a schematic file and an int seconds which would be the amount of time it takes for the schematic to build. I do know that Citizens and Builder exist however it does not do exactly what I need it to do.
    Any help is greatly appreciated,
    plisov
     
  2. Offline

    Zombie_Striker

    @plisov
    You may need to create your own reader for this. Either use WorldEdits code to read all the blocks, or look at the links below and figure out how to write your own reader:

    https://bukkit.org/threads/how-the-heck-do-i-read-a-schematic-file.45065/
    https://minecraft.gamepedia.com/Schematic_file_format
    https://www.spigotmc.org/threads/solved-how-to-read-schematics.63401/

    Once you figure it out, create a repeating task. Determine how many blocks should be placed every tick and then place that many blocks each time the task repeats.
     
  3. Offline

    plisov

    Thanks! I'll take a look and let you know.

    EDIT:

    @Zombie_Striker
    Hey, I've found this bit of code while looking through WorldEdit's code.

    Code:
        public Clipboard read(WorldData data) throws IOException {
            // Schematic tag
            NamedTag rootTag = inputStream.readNamedTag();
            if (!rootTag.getName().equals("Schematic")) {
                throw new IOException("Tag 'Schematic' does not exist or is not first");
            }
            CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
    
            // Check
            Map<String, Tag> schematic = schematicTag.getValue();
            if (!schematic.containsKey("Blocks")) {
                throw new IOException("Schematic file is missing a 'Blocks' tag");
            }
    
            // Check type of Schematic
            String materials = requireTag(schematic, "Materials", StringTag.class).getValue();
            if (!materials.equals("Alpha")) {
                throw new IOException("Schematic file is not an Alpha schematic");
            }
    
            // ====================================================================
            // Metadata
            // ====================================================================
    
            Vector origin;
            Region region;
    
            // Get information
            short width = requireTag(schematic, "Width", ShortTag.class).getValue();
            short height = requireTag(schematic, "Height", ShortTag.class).getValue();
            short length = requireTag(schematic, "Length", ShortTag.class).getValue();
    
            try {
                int originX = requireTag(schematic, "WEOriginX", IntTag.class).getValue();
                int originY = requireTag(schematic, "WEOriginY", IntTag.class).getValue();
                int originZ = requireTag(schematic, "WEOriginZ", IntTag.class).getValue();
                Vector min = new Vector(originX, originY, originZ);
    
                int offsetX = requireTag(schematic, "WEOffsetX", IntTag.class).getValue();
                int offsetY = requireTag(schematic, "WEOffsetY", IntTag.class).getValue();
                int offsetZ = requireTag(schematic, "WEOffsetZ", IntTag.class).getValue();
                Vector offset = new Vector(offsetX, offsetY, offsetZ);
    
                origin = min.subtract(offset);
                region = new CuboidRegion(min, min.add(width, height, length).subtract(Vector.ONE));
            } catch (IOException ignored) {
                origin = new Vector(0, 0, 0);
                region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE));
            }
    
            // ====================================================================
            // Blocks
            // ====================================================================
    
            // Get blocks
            byte[] blockId = requireTag(schematic, "Blocks", ByteArrayTag.class).getValue();
            byte[] blockData = requireTag(schematic, "Data", ByteArrayTag.class).getValue();
            byte[] addId = new byte[0];
            short[] blocks = new short[blockId.length]; // Have to later combine IDs
    
            // We support 4096 block IDs using the same method as vanilla Minecraft, where
            // the highest 4 bits are stored in a separate byte array.
            if (schematic.containsKey("AddBlocks")) {
                addId = requireTag(schematic, "AddBlocks", ByteArrayTag.class).getValue();
            }
    
            // Combine the AddBlocks data with the first 8-bit block ID
            for (int index = 0; index < blockId.length; index++) {
                if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index
                    blocks[index] = (short) (blockId[index] & 0xFF);
                } else {
                    if ((index & 1) == 0) {
                        blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF));
                    } else {
                        blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF));
                    }
                }
            }
    
            // Need to pull out tile entities
            List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue();
            Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<BlockVector, Map<String, Tag>>();
    
            for (Tag tag : tileEntities) {
                if (!(tag instanceof CompoundTag))
                    continue;
                CompoundTag t = (CompoundTag) tag;
    
                int x = 0;
                int y = 0;
                int z = 0;
    
                Map<String, Tag> values = new HashMap<String, Tag>();
    
                for (Map.Entry<String, Tag> entry : t.getValue().entrySet()) {
                    if (entry.getKey().equals("x")) {
                        if (entry.getValue() instanceof IntTag) {
                            x = ((IntTag) entry.getValue()).getValue();
                        }
                    } else if (entry.getKey().equals("y")) {
                        if (entry.getValue() instanceof IntTag) {
                            y = ((IntTag) entry.getValue()).getValue();
                        }
                    } else if (entry.getKey().equals("z")) {
                        if (entry.getValue() instanceof IntTag) {
                            z = ((IntTag) entry.getValue()).getValue();
                        }
                    }
    
                    values.put(entry.getKey(), entry.getValue());
                }
    
                BlockVector vec = new BlockVector(x, y, z);
                tileEntitiesMap.put(vec, values);
            }
    
            BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
            clipboard.setOrigin(origin);
    
            // Don't log a torrent of errors
            int failedBlockSets = 0;
    
            for (int x = 0; x < width; ++x) {
                for (int y = 0; y < height; ++y) {
                    for (int z = 0; z < length; ++z) {
                        int index = y * width * length + z * width + x;
                        BlockVector pt = new BlockVector(x, y, z);
                        BaseBlock block = new BaseBlock(blocks[index], blockData[index]);
    
                        if (tileEntitiesMap.containsKey(pt)) {
                            block.setNbtData(new CompoundTag(tileEntitiesMap.get(pt)));
                        }
    
                        try {
                            clipboard.setBlock(region.getMinimumPoint().add(pt), block);
                        } catch (WorldEditException e) {
                            switch (failedBlockSets) {
                            case 0:
                                // log.log(Level.WARNING, "Failed to set block on a Clipboard", e);
                                break;
                            case 1:
                                // log.log(Level.WARNING, "Failed to set block on a Clipboard (again) -- no more
                                // messages will be logged", e);
                                break;
                            default:
                            }
    
                            failedBlockSets++;
                        }
                    }
                }
            }
    
            // ====================================================================
            // Entities
            // ====================================================================
    
            try {
                List<Tag> entityTags = requireTag(schematic, "Entities", ListTag.class).getValue();
    
                for (Tag tag : entityTags) {
                    if (tag instanceof CompoundTag) {
                        CompoundTag compound = (CompoundTag) tag;
                        String id = compound.getString("id");
                        com.sk89q.worldedit.util.Location location = NBTConversions.toLocation(clipboard,
                                compound.getListTag("Pos"), compound.getListTag("Rotation"));
    
                        if (!id.isEmpty()) {
                            BaseEntity state = new BaseEntity(id, compound);
                            clipboard.createEntity(location, state);
                        }
                    }
                }
            } catch (IOException ignored) { // No entities? No problem
            }
    
            return clipboard;
        }
    
        private static <T extends Tag> T requireTag(Map<String, Tag> items, String key, Class<T> expected)
                throws IOException {
            if (!items.containsKey(key)) {
                throw new IOException("Schematic file is missing a \"" + key + "\" tag");
            }
    
            Tag tag = items.get(key);
            if (!expected.isInstance(tag)) {
                throw new IOException(key + " tag is not of tag type " + expected.getName());
            }
    
            return expected.cast(tag);
        }
    How would I determine the number of blocks needed to be placed per tick?
     
    Last edited: Dec 27, 2017
Thread Status:
Not open for further replies.

Share This Page