Solved NPE on a for loop, but plugin functions normally

Discussion in 'Spigot Plugin Development' started by DecisionsYT, Aug 9, 2018.

  1. Hey!

    I have an event to make a certain block not break in an EntityExplodeEvent. The functionality of this code is working fine and as expected, however I am getting an NPE in the console which seems rather strange considering it functions normally.

    Code (Text):
     @EventHandler
        public void onSmelterExplodeByEntity(EntityExplodeEvent event) {

            if (event.blockList().size() < 0) {
                return;
            }

            List<Block> explodedBlocks = event.blockList();
            ArrayList<SmelterObject> retrievedSmeltersArrayList;
            retrievedSmeltersArrayList = (ArrayList<SmelterObject>) plugin.getConfig().get("smelters");

            for (Block explodedBlock : explodedBlocks) {
                for (SmelterObject smelter : retrievedSmeltersArrayList) {
                    if (explodedBlock.getLocation().equals(smelter.getLocation())) {
                        event.blockList().remove(explodedBlock);
                    }
                }
            }

        }
    Error:
    Code (Text):
    [19:40:17 ERROR]: Could not pass event EntityExplodeEvent to FurnacesPro v1.0
    org.bukkit.event.EventException: null
            at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:308) ~[spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62) ~[spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:500) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:485) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.Explosion.a(Explosion.java:200) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.World.createExplosion(World.java:1645) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.WorldServer.createExplosion(WorldServer.java:1054) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.World.createExplosion(World.java:1634) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.EntityTNTPrimed.explode(EntityTNTPrimed.java:94) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.EntityTNTPrimed.tick(EntityTNTPrimed.java:74) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.World.entityJoinedWorld(World.java:1360) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.World.g(World.java:1327) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.World.tickEntities(World.java:1161) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.WorldServer.tickEntities(WorldServer.java:601) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.MinecraftServer.w(MinecraftServer.java:956) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.DedicatedServer.w(DedicatedServer.java:411) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.MinecraftServer.v(MinecraftServer.java:819) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at net.minecraft.server.v1_13_R1.MinecraftServer.run(MinecraftServer.java:717) [spigot-1.13.jar:git-Spigot-1503de9-200b239]
            at java.lang.Thread.run(Unknown Source) [?:?]
    Caused by: java.util.ConcurrentModificationException
            at java.util.ArrayList$Itr.checkForComodification(Unknown Source) ~[?:?]
            at java.util.ArrayList$Itr.next(Unknown Source) ~[?:?]
            at com.idleappsinc.furnacespro.listeners.CancelSmelterExplodeListener.onSmelterExplodeByEntity(CancelSmelterExplodeListener.java:59) ~[?:?]
            at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
            at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
            at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:?]
            at java.lang.reflect.Method.invoke(Unknown Source) ~[?:?]
            at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:304) ~[spigot-1.13.jar:git-Spigot-1503de9-200b239]
            ... 18 more
     
    The error is supposedly happening on Line 59, which is:
    Code (Text):
    for (Block explodedBlock : explodedBlocks) {
     
    I have checked to see if explodedBlocks is null, and it returns false (i.e. it's not null)

    I am baffled as to why there is an error on functioning code.

    Any help is appreciated.
     
  2. ConcurrentModificationException
    event.blockList().remove(explodedBlock);

    You can't remove something out of a list while iterating over it. That's why you're getting a
    ConcurrentModificationException error.
     
  3. Ahh. I wasn't aware of that.

    Do you think the best solution would be to save the blocks that I was to remove from the blocklist in an array and then after the for loops to do the remove()?
     
  4. You can simply use an Iterator.

    Code (Java):


        ...  list = yourList....

         Iterator itr = list.iterator();
         
         while(itr.hasNext()) {

            ... element = itr.next(); // This has to be called before removing it from the list.
            list.remove(element);
         }
     
     
  5. Choco

    Moderator

    No need. Use Iterator#remove() which removes the currently indexed element. So, #next(), if some condition, #remove()
     
    • Like Like x 1
  6. Ah alright, cheers for pointing that out.
     
  7. Thanks for your help! I'm just getting slightly confused with these Iterators.

    I have this:
    Code (Text):
    List<Block> explodedBlocks = event.blockList();
            ArrayList<SmelterObject> retrievedSmeltersArrayList;
            retrievedSmeltersArrayList = (ArrayList<SmelterObject>) plugin.getConfig().get("smelters");

            Iterator itr = explodedBlocks.iterator();
            while (itr.hasNext()) {

                Block block = (Block) itr.next();

                for (SmelterObject smelter : retrievedSmeltersArrayList) {
                    if (block.getLocation().equals(smelter.getLocation())) {
                        event.blockList().remove(block);
                    } else {
                        itr.remove();
                    }
                }
            }
    But it's producing errors and I'm not very familiar with Iterators and how they process data. Would you mind helping me out a little with the structure of the checking if a block's location is equal to a location in my retrievedSmeltersArrayList (if that makes sense)
     
  8. An iterator is an alternative way of looping through a collection.

    Not sure if this would work but this is how I'd do it.
    Code (Java):

        @EventHandler
       public void onSmelterExplodeByEntity(EntityExplodeEvent event) {

           if (event.blockList().size() < 0) {
               return;
           }

           List<Block> explodedBlocks = event.blockList();

           List<SmelterObject> retrievedSmelters = (ArrayList<SmelterObject>) getConfig().get("smelters");

           Iterator itr = explodedBlocks.iterator();

           while (itr.hasNext()) {

               Block block = (Block) itr.next();
               if (isLocSimilar(retrievedSmelters, block)) {

                   itr.remove();

               }

           }

       }

       boolean isLocSimilar(List<SmelterObject> smelterList, Block block) {
           Location bLoc = block.getLocation();
           for (SmelterObject smObj : smelterList) {
               if (smObj.getLocation().equals(bLoc)) {
                   return true;
               }
               continue;
           }
       }
     
     
  9. Thanks alot for replying quickly! It works very weel and doesn't produce any errors :)

    I will just put my code down below as I had to make an adjustment.

    Code (Text):
    @EventHandler
        public void onSmelterExplodeByEntity(EntityExplodeEvent event) {

            if (event.blockList().size() < 0) {
                return;
            }

            List<Block> explodedBlocks = event.blockList();
            ArrayList<SmelterObject> retrievedSmeltersArrayList = (ArrayList<SmelterObject>) plugin.getConfig().get("smelters");

            Iterator itr = explodedBlocks.iterator();
            while (itr.hasNext()) {

                Block block = (Block) itr.next();

                if (isLocSimilar(retrievedSmeltersArrayList, block)) {
                    itr.remove();
                }
            }
        }

        public boolean isLocSimilar(List<SmelterObject> smelterList, Block block) {
            Location bLoc = block.getLocation();
            for (SmelterObject smObj : smelterList) {
                if (smObj.getLocation().equals(bLoc)) {
                    return true;
                }
            }
            return false;
        }
     
  10. Reason why it functions is that exceptions are caught from the spigot internal event handler.