getting all the blocks in a chunk

Discussion in 'Plugin Development' started by LRFLEW, Mar 1, 2011.

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

    LRFLEW

    I am trying to access all the blocks in a chunk to run through a for loop, but there is no
    getBlocks() : Block[]
    command :p. I found Chunk.getX() and Chunk.getZ(), but how would I get it?

    I'm guessing the range is from
    X = [getX(), getX() + C], Z = [getZ(), getZ() +C], Y = [0, 127]

    Is that right? What is C?
     
  2. Offline

    Cheesier

  3. Offline

    Afforess

    No idea what you are planning, but I can already tell you that's a terrible, terrible, terrible idea. There are 16*16*128 (32,768 if you can't do that in your head. ;) ) blocks in a chunk. That loop, if run even semi-often would lag the server. I expect that running it for 10, or 20 chunks would cause a 5-10 second lag.
     
  4. Offline

    LRFLEW

    Not really the answer to my question, but ok.

    It's more of a test/toy anyways. I would never do anything like that for a real plugin :p.
     
  5. Offline

    Cheesier

    Then i misunderstood, so what is it that you want to do?

    You could just loop thru the chunk and put it all in a 3D array, as this would be easier to manage. Getting a single block would be the way that i explained.
     
  6. Offline

    LRFLEW

    If i can loop through the chunk, I wouldn't be asking :p. The point is that I can't figure out what blocks belong to what chunk. For any one chunk, there is getX() and getZ(), but I don't know how to then figure out the bounds of the chunk :p. How big is a chunk anyways?
    --- merged: Mar 3, 2011 6:50 PM ---
    UPDATE: by experimentation, I found the getX() and getZ() will need some sort of multiplication to get the right block. I just dont know :p.
     
  7. Offline

    Cheesier

    A chunk is 16*128*16 (x, y, z), the chunk at a given location can be calculated by the following:
    Code:
    chunkX = Math.floor(blockX / 16);
    chunkZ = Math.floor(blockZ / 16);
    Or simply block.getChunk() will get you the chunk that the block is located in. (Math is more fun :))
     
  8. Offline

    LRFLEW

    Ok, but I was looking for the other way :p.

    To try to make more sense:
    I have a chunk! I want to run a for loop through all blocks in the chunk!
    does that help?
     
  9. Offline

    Cheesier

    reverse my math and get the X and Y, Like this
    Code:
    blockX = 16*chunkX
    blockZ = 16*chunkZ
    This should get you the coordinates of the first block, provide it with Y tho :) 0 is the first block.

    Then just
    Code:
    server.getBlockAt(blockX, 0, blockZ);
     
  10. Offline

    LRFLEW

    Ok, I thought so. I tried it, but... well... just look. The code (and yes, I used an inner class. What are you going to do about it):
    Code:
    package com.LRFLEW.tnt;
    
    import org.bukkit.Material;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandSender;
    import org.bukkit.entity.Player;
    import org.bukkit.event.Event;
    import org.bukkit.event.Event.Priority;
    import org.bukkit.event.world.ChunkLoadEvent;
    import org.bukkit.event.world.WorldListener;
    import org.bukkit.plugin.PluginDescriptionFile;
    import org.bukkit.plugin.PluginManager;
    import org.bukkit.plugin.java.JavaPlugin;
    
    public class Tnt extends JavaPlugin {
    
        private final WorldEvents worldListener = new WorldEvents(this);
    
        @Override
        public void onDisable() {
            PluginDescriptionFile pdfFile = this.getDescription();
            System.out.println( pdfFile.getName() + " says Goodbye!" );
        }
    
        @Override
        public void onEnable() {
            // Register our events
            PluginManager pm = getServer().getPluginManager();
            pm.registerEvent(Event.Type.CHUNK_LOADED, worldListener, Priority.Low, this);
    
            PluginDescriptionFile pdfFile = this.getDescription();
            System.out.println( pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!" );
        }
    
        @Override
        public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
            if (cmd.getName().equals("tnt") && sender instanceof Player) {
                Player player = (Player)sender;
                player.sendMessage(Integer.toString(player.getLocation().getBlock().getChunk().getX()).toString());
                player.sendMessage(Integer.toString(player.getLocation().getBlock().getChunk().getZ()).toString());
            }
            return false;
        }
    
        private class WorldEvents extends WorldListener {
    
            @SuppressWarnings("unused")
            private final Tnt plugin;
    
            public WorldEvents (Tnt instance) {
                this.plugin = instance;
            }
    
            @Override
            public void onChunkLoaded(ChunkLoadEvent event) {
                int X = event.getChunk().getX() * 16;
                int Z = event.getChunk().getZ() * 16;
                for (int x = 0; x < 16; x++) {
                    for (int z = 0; z < 16; z++) {
                        for (int y = 0; y < 128; y++) {
                            if (event.getWorld().getBlockAt(X+x, y, Z+z).getType().equals(Material.GLASS)) {
                                event.getWorld().getBlockAt(X+x, y, Z+z).setType(Material.TNT);
                            }
                        }
                    }
                }
            }
    
        }
    
    }
    And the console:
     
  11. Offline

    Raphfrk

    The easiest way is to use the shift operator.

    Code:
    int bx = chunk.getX()<<4;
    int bz = chunk.getZ()<<4;
    
    World world = event.getWorld();
    
    for(int xx = bx; xx < bx+16; xx++) {
        for(int zz = bz; zz < bz+16; zz++) {
            for(int yy = 0; yy < 128; yy++) {
                int typeId = world.getBlockTypeIdAt(xx, yy, zz);
                if(typeId == 20) {
                    world.getBlockAt(xx,yy,zz).setType(Material.TNT);
                }
            }
        }
    }
    
    Your exception is:
    14:53:53 [SEVERE] #1 read thread" java.lang.OutOfMemoryError: Java heap space

    This means you ran out of memory. Bukkit doesn't handle large scale block updates very well. The reason is that when you read a block, the server caches it. The cache ends up massive if you update a huge number of blocks.

    By using:

    world.getBlockTypeIdAt(x,y,z)

    This method allows you to get the type of a block without updating the cache.

    The cache will still be needed to be used to actually set the glass to TNT, but that should be a lot less than scanning the entire chunk.
     
    cseraphi and Premm like this.
  12. Offline

    Afforess

    Kinda pointless since he still needs to call block.setTypeId(), which will fill the cache just as fast.
     
  13. Offline

    Infernus

    Would be quite funny to put this on a well-used server people would be like "WT* HAPPENED TO MY BUILDING ITS FILLED WITH TNT !!!!". lol
     
  14. Offline

    LRFLEW

    What should I do then?

    Who has grass in their house :p

    A side note, the chunks around the spawn are never "Loaded" and hence never get called. Is there a way to figure out which chunks are part of the spawn area, even if it's just a matter of math?
     
  15. Offline

    4am

    WorldEdit seems to be able to do huge updates without breaking the cache - maybe break up your operation into multiple segments to allow the cache to clear? Or perhaps I've never made an edit big enough to cause problems...
     
  16. Offline

    Afforess

    WorldEdit uses a queue to avoid slamming the server.
     
  17. Offline

    cjc343

    I've made edits big enough to cause problems... on my server at least...

    If it had a few more gigs of RAM, it might handle edits a tad larger.
     
  18. Offline

    LRFLEW

    I don't know what kind of computer you think I'm using, but "a few more gigs" is a lot to put on one little computer :p
    --- merged: Mar 4, 2011 4:58 AM ---
    Is there some way I can force the cache to clear?

    What do you mean by a queue?
     
  19. Offline

    Afforess

    WorldEdit limits block changes per tick (or second, not sure), and only completes part of a change at each interval. You can grab their source.
     
  20. Offline

    LRFLEW

    Once I do that, will I have to reload the chunk to get it to look right to the client?
     
  21. Offline

    Afforess

    Nope. Changing a block id should force the server to send an update to the client.
     
  22. Offline

    Raphfrk

    I think my suggestion would help a lot.

    Your method checks every block in the chunk. This adds them all to the cache.

    My suggestion checks every block using the cache bypassing method. This adds nothing to the cache. Setting the block to TNT will add just that block to the cache.

    If you run this on a chunk where 1% of the blocks are glass, then it only adds 1% as many blocks to the cache.
     
    joehot2000 likes this.
  23. Offline

    Infernus

    Oops, I read it 'glass' instead of 'grass' ;)

    Still quite funny to have TNT grass though, will be hard for miners :D
     
  24. Offline

    LRFLEW

    Thanks. I'm going to try some of this now :).
     
  25. Offline

    Afforess

    Oh yep, you're correct. Need to learn to read, not skim. ;)
     
Thread Status:
Not open for further replies.

Share This Page