Copy blocks from one world to another

Discussion in 'Spigot Plugin Development' started by Mannyyy, Aug 14, 2017.

  1. Okay, so I'm creating a plugin where I need to copy blocks from one world to another. I can do this perfectly fine and it copies blocks perfectly and in the right place and all that is good. The only problem is as their is alot of blocks being copied you can imagine it's kinda laggy. I have scheduled so it pastes a chunk every 0.5 seconds. I'm using
    Code (Text):

    public void generateChunk() {
        final int[] timesrun = {0};
        for(int index1=0; index1<6; index1++){
            for(int index2=0; index2<6; index2++){
                int finalind1 = index1;
                int finalind2 = index2;
                Bukkit.getScheduler().runTaskLater(plugin, new Runnable() {
                    @Override
                    public void run() {
                        copyChunk(finalind1, finalind2);
                        timesrun[0] = timesrun[0] + 1;

                        Bukkit.broadcastMessage(timesrun[0] + "");
                    }
                }, 10L * timesrun[0]);

            }
        }

    }

    private void copyChunk(int chunkId1, int chunkId2){
        Chunk chunk = Bukkit.getWorld("world").getChunkAt(chunkId1, chunkId2);
        for (int x = 0; x < 16; x++) {
            for (int y = 0; y < Bukkit.getWorld("world").getMaxHeight(); y++) {
                for (int z = 0; z < 16; z++) {
                    Bukkit.getWorld("newworld").getChunkAt(chunkId1, chunkId2).getBlock(x, y, z).setType(chunk.getBlock(x, y, z).getType());
                }
            }
        }
    }
     
    To do this.
    * Note: All the copyChunk method is doing getting the block types and setting them in the different world at the same location. So the plugin works but it generates alot of lag, as I cannot type commands or type in chat whilst it is being copied over. Is their anyway to maybe do one y layer every tick or something? Or just make it so lag is minimal, as it would be greatly appreciated. Thanks.
     
  2. Try a bukkit runnable for the y axis you're talking about. As for the rest of it, try like async threading.
     
  3. I tried the y axis method, still no difference. How would I go about async threading?
     
  4. OMG there is so much wrong with your code. Let's start at timesrun. Please don't use a final array there to cheat your way around things. And you don't even need it in this case, just create an int timesRun which you increase not in the task runnable but rather in the loop itself. Following that, please don't use index1 etc. for for-loop counters. They are usually named "i", "j", "k", ... or in your case probably "x" and "y". Next point, creating these final index variables is unneeded, because you didn't even declare them as final and depending on your Java version, it works without this, which it seemingly does.

    Improved code:

    PHP:
    public void generateChunk() {
        int timesRun = 0;
     
        for(int x = 0; x < 6; x++){
            for(int y = 0; y < 6; y++){
                /*int finalX = x; //Only use this with Java 7 or lower I think
                int finalY = y;*/

             
                Bukkit.getScheduler().runTaskLater(plugin, new Runnable() {
                    @Override
                    public void run() {
                        copyChunk(x, y);

                        //Bukkit.broadcastMessage(timesrun[0] + "");
                    }
                }, 10 * timesRun);
             
                timesRun++;
            }
        }
    }

    And to answer your question, you could look into letting WorldEdit do the copying for you, should probably be faster.
     
  5. Also, you may wanna run the whole thing inside of a task that gets called every x time (to avoid creating 36 dummy tasks) using a BukkitRunnable:

    Code (Text):
    new BukkitRunnable() {
           int x = 0;
           int y = 0;
               
           @Override
           public void run() {
               copyChunk(x, y);
                   
               if (++y >= 6) {
                   y = 0;
                       
                   if (++x >= 6) {
                       this.cancel();
                   }
               }
           }
    }.runTaskTimer(plugin, 0L, 10L);
     
  6. Using this works great, although it still does create a little lag, could this be solved by increasing the delay between each chunk paste? If not is there anything I could improve in the copyChunk method, say setting only 100blocks/tick or something similar?
     
  7. As I won't it to create as little lag as possible (atleast under 100ms)
     
  8. As someone said earlier, you can use async block placing, but that's too hard. Instead of placing one chunk per .5s, I would set x blocks (maybe 16) every .5s. You can add a z component into the BukkitRunnable for setting x blocks at the same time.
     
  9. I did this:

    Code (Text):
        private synchronized void copyChunk(int chunkId1, int chunkId2){
            Chunk chunk = Bukkit.getWorld("world").getChunkAt(chunkId1, chunkId2);

            new BukkitRunnable() {
                int x = 0;
                int y = 0;
                int z = 0;

                @Override
                public void run() {

                    Bukkit.getWorld("chunks").getChunkAt(chunkId1, chunkId2).getBlock(x, y, z).setType(chunk.getBlock(x, y, z).getType());

                    if (++x >= 16) {
                        x = 0;

                        if(++y >= Bukkit.getWorld("world").getMaxHeight() + 1) {
                            y = 0;
                            if (++z >= 16) {
                                this.cancel();
                                z = 0;
                            }
                        }
                    }
                }
            }.runTaskTimer(plugin, 0L, 1L);
        }
    But that only made a few blocks out of the thousands I actually need. Also How would I change it to do say 50 blocks per tick?
     
  10. I would create a set of all the blocks you need to change, and then replace x blocks each loop. Btw, that loop will only set one block every tick which will be more intensive than setting 16 every .5 secs.
     
  11. Code (Text):
        public void generateChunk(CommandSender sender) {
            new BukkitRunnable() {
                int x = 0;
                int y = 0;

                @Override
                public void run() {

                    copyChunk(x, y);

                    if (++y >= 4) {
                        y = 0;

                        if (++x >= 4) {
                            this.cancel();
                            x=0;
                            sender.sendMessage("Chunk created. Use /chunk home");
                        }
                    }
                }
            }.runTaskTimer(plugin, 0L, 25L);
        }

        public void removeChunk(){

        }

        private void copyChunk(int chunkId1, int chunkId2){
            Chunk chunk = Bukkit.getWorld("world").getChunkAt(chunkId1, chunkId2);
            for (int x = 0; x < 16; x++) {
                for (int y = 0; y < Bukkit.getWorld("world").getMaxHeight(); y++) {
                    for (int z = 0; z < 16; z++) {
                        Bukkit.getWorld("chunks").getChunkAt(chunkId1, chunkId2).getBlock(x, y, z).setType(chunk.getBlock(x, y, z).getType());
                    }
                }
            }
        }
    New code im using. So instead of using
    Code (Text):
    Bukkit.getWorld("chunks").getChunkAt(chunkId1, chunkId2).getBlock(x, y, z).setType(chunk.getBlock(x, y, z).getType());
    I should create a list, add the blocks then every x seconds paste those blocks?
     
  12. Oh I think I understand what you are doing there.. Using one world ("chunks") as a template right? But that's a wrong approach to doing that. Use WorldEdit to load the template from schematic files instead of having a world for these.
     

Share This Page