Chunks do not unload

Discussion in 'Spigot Plugin Development' started by MightyOne, Jul 3, 2018.

  1. Hello,
    for a plugin I wanted to remove certain idling Items from spawners whenever their chunk gets unloaded and spawn them again when somebody loads the chunk.

    To check if my code works fine I added debug messages for
    - a chunk with an item is loading / unloading
    - a spawner deletes / respawns it's item

    Ingame I got the confusing messags that whenever a chunk is getting out of view distance it unloads, but right after that it gets loaded again, several times in a row:
    WhatHappens.png

    This would be the reffering listeners:

    Code (Java):
    @EventHandler
        public void onChunkLoad(ChunkLoadEvent e) {
            Chunk c = e.getChunk();
         
            boolean makebroadcast = false;
         
            for(CrystalSpawner spawner : spawners) {
                if(spawner.getLocation().getChunk().equals(c)) {
                    makebroadcast = true;

                    if(!spawner.isReloading())
                        spawner.spawn();
                }
            }
         
            if(makebroadcast)
                Bukkit.broadcastMessage(ChatColor.DARK_RED + "Loading Chunk " + c.getX() + ", " + c.getZ());

        }
     
        @EventHandler
        public void onChunkUnload(ChunkUnloadEvent e) {
            Chunk c = e.getChunk();
         
            boolean makebroadcast = false;
         
            for(CrystalSpawner spawner : spawners) {
                if(spawner.getLocation().getChunk().equals(c)) {
                    makebroadcast = true;
                 
                    if(spawner.isChunkLoaded())
                        spawner.hide();
                }
            }
         
            if(makebroadcast)
                Bukkit.broadcastMessage(ChatColor.GOLD + "Unloading Chunk " + c.getX() + ", " + c.getZ());
        }
    and the used methods from the Spawner class:
    Code (Java):
    public void spawn() {
         
            if(spawn.getChunk().isLoaded()) {
                Bukkit.broadcastMessage(ChatColor.GRAY + "loading spawner " + myNumber);
                isChunkLoaded = true;
             
                drop = world.dropItem(spawn.clone().add(0.5, 0, 0.5), crystal);
                drop.setCustomName(spawnerID);
                drop.setVelocity(new Vector());
             
                sparkling = new BukkitRunnable() {
                    @Override
                    public void run() {
                        drop.getWorld().playEffect(drop.getLocation().add(0, 0.3, 0), Effect.PORTAL, 0, 16);
                    }
                };
             
                sparkling.runTaskTimer(Plugin.getInstance(), 0, 1);

            }else
                isChunkLoaded = false;
        }

    public void hide() {
            if(isChunkLoaded) {
                Bukkit.broadcastMessage(ChatColor.GRAY + "unloading spawner " + myNumber);
                sparkling.cancel();
                drop.remove();
                drop = null;
                isChunkLoaded = false;
            }
        }
    Does anybody know why the chunk loading gets triggered again? Is anything I did responsible for that?

    Thanks for any help
    MighyOne

    P.S. is it even taking less RAM or anything to despawn these items?
     
  2. I can't really explain what you're seeing, but I do think I can answer this question. Nope :)

    Removing the item entities and respawning them on chunk unload/load is exactly what the vanilla behavior is going to be, anyway.
     
  3. Accessing a chunk that isn't loaded loads it.
     
    • Winner Winner x 1
  4. @BillyGalbreath i just have no idea which part of my code is accessing the chunk after the event
     
  5. md_5

    Administrator Developer

    new Exception().printStackTrace() in the load event to see
     
    • Informative Informative x 1
  6. As @BillyGalbreath already said, accessing an unloaded chunk loads it. It's not the most user friendly API part of Spigot in my opinion. Maybe I'm missing some reasoning for this, but I made the same mistake a while ago.

    You can check if a chunk is in use (without loading it) with the following (thanks @electronicboy for the help last time) like so:
    Code (Java):
    if (!spawnLocation.getWorld().isChunkInUse(spawnLocation.getBlockX() >> 4,
            spawnLocation.getBlockZ() >> 4)) {
        continue;
    }
    Frankly I have no clue what the >> 4 does (I think something with moving bytecodes around), but hey it works without a hitch. I'm using this in a custom creature plugin I have which determines whether monsters should be spawning in that area.
     
    #6 MrDienns, Jul 4, 2018
    Last edited: Jul 4, 2018
  7. While not related to the thread, the '>>' is a bitshift operator which shifts the bits (not bytes) to right. The binary system has an interesting property that, each shift to right divides the number by 2, and each shift to left multiplies by 2.

    So, number >> 4 is basically number/2/2/2/2, which can be shortened to number/(2*2*2*2), which can be shortened to number/16. You'll get the same results with 'spawnLocation.getBlockX/Z() / 16', but bitshift is a tiny bit faster, but definitely not worth doing it for the speed here. I'd probably go with /16, because it's more readable if you haven't messed with bits before.

    Same way, you can turn chunk coordinates to block coordinates with 'blockCoord = chunkCoord << 4', because '<< 4' just basically multiplies by 16.
     
    • Winner Winner x 1
  8. To visualize this division by two, let's take the number 1234. If you look at it's binary bits it is 10011010010. 1234 / 16 is 77. The binary bits for 77 is 1001101. Let's see this math executed side by side using bitshift:

    Code (Text):
        >> 4    // move bits to right 4 spaces
    10011010010 // 1234
    01001101001
    00100110100
    00010011010
    00001001101 // 77
    That's why it's called bitshifting ;)
     
    • Winner Winner x 1
  9. Thanks for the explanation. Especially that example greatly helps to visualize it. Guess you learn something new every day here. :D
     
    • Friendly Friendly x 1
  10. @MrDienns so what you are saying is that using
    Block#getChunk().isLoaded()
    will load the vhunk anyway wheras
    World#.isChunkInUse()
    does not?
     
  11. Yes. The World#getChunk will cause the chunk to be loaded as far as I know. If you then use the Chunk#IsLoaded function on it, it will always return true. At least, that's my experience with this. Don't ask me why it works this way, I have no clue. I solved my issue with using World#isChunkInUse, with the example I showed earlier.
     
  12. Block#getChunk is accessing a chunk...