The hell of pasting schematics...

Discussion in 'Spigot Plugin Development' started by coco_gigpn, Mar 13, 2019.

  1. I am trying to paste a schematic (.schem spong schematic with worldedit) in 1.13.2 with this code:

    Code (Java):
    package fr.cocoraid.test;

    import net.minecraft.server.v1_13_R2.*;
    import org.bukkit.Bukkit;
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.block.data.BlockData;
    import org.bukkit.craftbukkit.v1_13_R2.CraftWorld;
    import org.bukkit.scheduler.BukkitRunnable;
    import org.bukkit.util.Vector;

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.Map;
    import java.util.logging.Level;

    public class Schematic {


        private static final String SCHEMATIC_EXTENSION = ".schem";
        private static Reflection.MethodInvoker get =

        private String name;
        private short width,height,length;


        private Map<Integer, BlockData> blocksMap = new HashMap<>();


        private LinkedList<Location> locations = new LinkedList<>();
        private LinkedList<BlockData> datas = new LinkedList<>();


        private Vector size;

        public Schematic(String name, Location loc) {
            this.name = name;

            try {
                InputStream is = Test.getInstance().getInstance().getResource("schematics/" + name + SCHEMATIC_EXTENSION);
                NBTTagCompound nbtData = NBTCompressedStreamTools.a(is);

                width = nbtData.getShort("Width");
                height = nbtData.getShort("Height");
                length = nbtData.getShort("Length");

                byte[] blocksData =  nbtData.getByteArray("BlockData");


                NBTTagCompound palette = nbtData.getCompound("Palette");
                NBTTagList tiles = (NBTTagList) nbtData.get("TileEntities");

                palette.getKeys().forEach(rawState -> {
                    int id = palette.getInt(rawState);
                    BlockData blockData = Bukkit.createBlockData(rawState);
                    blocksMap.put(id, blockData);
                });


                is.close();

                this.size = new Vector(length, height, width);

                //Preload blocks and check if they are already placed or not
                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;
                            Location location = new Location(loc.getWorld(), (x + loc.getX()) - (int) width / 2, y + loc.getY(), (z + loc.getZ()) - (int) length / 2);
                            BlockData data = blocksMap.get((int) blocksData[index]);
                            if(data == null) {
                                //debug
                                locations.add(location);
                                datas.add(Material.SPONGE.createBlockData());
                                continue;
                            }
                            if(location.getBlock().getType() != data.getMaterial()) {
                                locations.add(location);
                                datas.add(data);
                            }

                        }
                    }
                }

            } catch (IOException ex) {
                ex.printStackTrace();
            }
            Bukkit.broadcastMessage("size " + locations.size());

        }


        private static final int SCALE = 60;

        public void paste( ) {

            long start = System.currentTimeMillis();
            new BukkitRunnable() {
                int size = locations.size();
                int part = size / SCALE;
                int left = size % SCALE;

                int i = 0;
                int time = 0;

                public void run() {

                    for (int k = this.i; k < (this.time + 1) * part; ++k) {
                        setBlock(k);
                    }
                    i += part;
                    time++;
                    Bukkit.broadcastMessage("Percent: " + time * 100 / SCALE + "%");
                    if (this.time >= SCALE) {
                        if (left > 0) {
                            for (int k = size - left; k < size; ++k) {
                                setBlock(k);
                            }
                        }
                        this.cancel();
                        //callback.onSchematicGenerated();

                    }

                }
            }.runTaskTimer(Test.getInstance(), 20, 20);


        }

        private void setBlock(int i) {
            Location location = locations.get(i);
            BlockData data = datas.get(i);
            Block nmsBlock = IRegistry.BLOCK.getOrDefault(new MinecraftKey(data.getMaterial().name().toLowerCase()));
            BlockPosition bp = new BlockPosition(location.getX(),location.getY(),location.getZ());
            ((CraftWorld)location.getWorld()).getHandle().setTypeAndData(bp, nmsBlock.getBlockData(),2);
            location.getBlock().setBlockData(data);
            if(location.getBlock().getType() == Material.JUKEBOX) return; //prevent record error
            location.getBlock().getState().update(true, false);
        }

    }
     

    The code is perfectly working for some schematics I made with worldedit and when I tried to paste this one you can see the result:

    [​IMG]

    [​IMG]

    There are sponges because I get some errors with data null:
    Code (Text):
    if(data == null) {
                                //debug
                                locations.add(location);
                                datas.add(Material.SPONGE.createBlockData());
                                continue;
                            }
    If I do not add the code below, that's the result:
    [​IMG]

    While I should get something like that:

    [​IMG]
    [​IMG]

    I am not able to test the schematic with worldedit, this is too large, the server crashs instantly.
    So I have used fastasyncworldedit, and the schematic is pasted like a charm.
    However I can't use the plugin as an api because there is plenty of bugs I can't paste many other schematics. This is really weird...

    I let you the downloading link of 2 glitched schematics:
    this one and this one
    And this simple one is working

    I really do not know what is causing this issue, maybe a block ? why the data is null ?

    Thank you very much !
     
  2. Assuming the schematic contains the whole ship it might be some issue related to the origin as
    This looks odd. In the loop your iteration is based on time * part and yet when you advance i you only ever do it by part meaning you will be processing the same blocks multiple times.
    Edit: Ignore me, as you are not adding (this.time + 1) * part to i what I said was not true, although I find this code hard to parse and really don't know why you wouldn't just do i + part.

    I would also use removeFirst() unless you need to paste the schematic multiple times as walking the linked list to reach the right index each time is not great for performance (or simply use an array or arraylist).

    I'm not sure this is really causing your issue though, I would add some debug and and see what x,y,z and i are as the paste happens and see what their values are when things start to go wonky.
     
    #2 basicmark, Mar 15, 2019
    Last edited: Mar 15, 2019
  3. Thank's for your help :)
    The paste part is not the issue here because I get a null data:
    Code (Text):
    BlockData data = blocksMap.get((int) blocksData[index]);
                            if(data == null) {
                                //debug
                                locations.add(location);
                                datas.add(Material.SPONGE.createBlockData());
                                continue;
                            }
    I have tried to debug, but there is so much data that I can't find anything
     
  4. For whatever reason your byte array "blockData" contains negative numbers that are not present in the Palette list, this is causing your null entries when you try to get these out of your hashmap.
    Since I have no clue at all how schematics are saved nor why there are negative values in the array, I cannot give you a solution.

    But when you find a solution, I'm interested in it too.

    data.png
     
  5. Thank you for help, I think I am using a wrong way for reading schematic,
    https://github.com/EngineHub/WorldE.../clipboard/io/SpongeSchematicReader.java#L171

    I should update my code however I will no longer use schematic.
    I prefere world regeneration, copy paste from a template is 100% faster than pasting a schematic
     

Share This Page