Can't remove values from arraylist - Urgent

Discussion in 'Plugin Development' started by Jumla, May 28, 2012.

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

    Jumla

    Hey, I made a nice hunger games plugin (I'm aware there are like 30 out there, but, none really fit my needs)

    I have a arraylist<Player> called players which is essentially a list of people that are currently playing.
    Well, the plugin worked perfect on localhost, but when I uploaded to the main server that I own, around 50 people joined the game, and after afew people died, the server crashed with the following error.

    Code:
    17:34:58 [SEVERE] java.lang.IndexOutOfBoundsException: Index: 361, Size: 361
    17:34:58 [SEVERE]      at java.util.ArrayList.RangeCheck(ArrayList.java:547)
    17:34:58 [SEVERE]      at java.util.ArrayList.remove(ArrayList.java:387)
    17:34:58 [SEVERE]      at net.minecraft.server.World.tickEntities(World.java:11                                                                                                                    62)
    17:34:58 [SEVERE]      at net.minecraft.server.MinecraftServer.w(MinecraftServe                                                                                                                    r.java:549)
    17:34:58 [SEVERE]      at net.minecraft.server.MinecraftServer.run(MinecraftSer                                                                                                                    ver.java:450)
    17:34:58 [SEVERE]      at net.minecraft.server.ThreadServerApplication.run(Sour                                                                                                                    ceFile:492)
    17:34:58 [SEVERE] Unexpected exception
    java.lang.IndexOutOfBoundsException: Index: 361, Size: 361
            at java.util.ArrayList.RangeCheck(ArrayList.java:547)
            at java.util.ArrayList.remove(ArrayList.java:387)
            at net.minecraft.server.World.tickEntities(World.java:1162)
            at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:549)
            at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:450)
            at net.minecraft.server.ThreadServerApplication.run(SourceFile:492)
    
    Here's my function to leave the game.

    Code:
    // Leave the game
        public void leaveGame(Player player, boolean broadcast) {
            if (broadcast) LiveBroadcast(player.getName() + " has left The Hunger Games.");
            players.remove(player);
            if (players.size() == 1 && started) {
                broadcast(players.get(0).getName() + " has won the Hunger Games!");
                started = false;
                ScheduleGame();
            }
            player.teleport(deatharea);
        }
    And the definition of arraylist players...

    Code:
        ArrayList<Player> players = new ArrayList<Player>();
    Thank you!
     
  2. Offline

    r0306

    Jumla
    Are you calling up the leaveGame method inside a PlayerQuitEvent and is that event registered in your plugin manager?
     
  3. Offline

    travja

    Your listener is in another class right? If so you need to do plugin.players.remove(player);
     
  4. Offline

    Jumla

    Nope, I am not.

    Yes.
     
  5. Offline

    r0306

    Jumla
    Do you mind posting your whole Listener class? It would tell us more about what might be going wrong.
     
  6. Offline

    travja

    Yeah... We definitely need the whole class to solve this one..
     
  7. Offline

    Jumla

    It's not an issue with the listener class, it's an issue of the remove function, which I have re-written:

    Code:
    public void removePlayer(String player){
                for (int i = 0; i < (players.size() - 1); i++) {
                    final Player p = players.get(i);
                    if(p.getName().equals(player)){
                        players.remove(i);
                        return;
                    }
                }
        }
     
  8. Offline

    r0306

    Jumla
    Instead of that, you can do:
    Code:
    public void removePlayer(String player){
                for (Player p : players) {
                    if(p.getName().equals(player)){
                        players.remove(p);
                        return;
                    }
                }
        }
    It is true that an arraylist starts at an index of 0, causing it to have a size of one more than its maximum index. However, you already solved this by setting i to 0. You went over one when you subtracted 1 from the maximum size of the arraylist, preventing the last player in the list from being removed.
     
  9. Offline

    Njol

    The problem is not your remove function, as your arraylist has no connection to the problem at all.
    Can you see any of your packages/classes in this stacktrace?
    The error is caused in net.minecraft.server.World.tickEntities, which is this method:
    Code:
        public void tickEntities() {
    
            // -- snip --
    
            for (i = 0; i < this.entityList.size(); ++i) {
                entity = (Entity) this.entityList.get(i);
    
                // -- snip --
     
                if (entity.dead) {
                    j = entity.ca;
                    k = entity.cc;
                    if (entity.bZ && this.isChunkLoaded(j, k)) {
                        this.getChunkAt(j, k).b(entity);
                    }
    
                    this.entityList.remove(i--); // line 1162
                    this.d(entity);
                }
    
            }
    
    Do you by chance use an async task somewhere in your plugin? It seems as if some thread is modifying the entityList while the main server thread is executing the above code.
     
  10. Offline

    Jumla

    Code:
        // Countdown from 10.
        public void startCountDown() {
            LiveBroadcast("-----------");
            final int ID;
            ID = Bukkit.getServer().getScheduler().scheduleAsyncRepeatingTask(this, new Runnable() {
                        Integer left = 10;
                        public void run() {
                            if (left == 0) {
                                started = true;
                                teleAll();
                                LiveBroadcast(ChatColor.BOLD + "GO! "
                                        + ChatColor.RESET + ChatColor.RED
                                        + "May the odds be ever in your favor.");
                             
                                LiveBroadcast(ChatColor.RESET + "" + ChatColor.GREEN + "You have 15 seconds of invincibility");
                                return;
                            } else if (left > 0) {
                                LiveBroadcast(left + " seconds...");
                                left--;
                            }
                        }
                    }, 0L, 20L);
            final int seconds = 15;
            Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
                        public void run() {
                            safe = true;
                            for(Player p : players){
                                p.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, seconds * 20, 2));
                                p.setHealth(20);
                                p.setFoodLevel(20);
                            }
                            Bukkit.getServer().getScheduler().cancelTask(ID);
                            log("Success");
                        }
                    }, 200L);
            Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(this, new Runnable() {
                public void run() {
                    safe = false;
                }
            }, (seconds * 20) + 200);
         
        }
     
        // Teleport all players to center
        public void teleAll() {
            for (int i = 0; i < players.size(); i++) {
                final Player player = players.get(i);
                if(!player.isOnline()){
                    removePlayer(player.getName());
                }
                player.teleport(arena);
                player.getInventory().clear();
            }
        }
    This is the only piece of code that uses an async thread... There are loads of SyncDelayed though.

    However, this error occurs mid-game. I'll post the code when a player dies/logs out...

    Events....
    Code:
        @EventHandler
        public void onEntityDeath(PlayerDeathEvent event) {
            final Player p = event.getEntity().getPlayer();
            if (plugin.players.contains(p) && plugin.started) {
                final int left = plugin.players.size() - 1;
                plugin.LiveBroadcast(p.getName() + " has died. "
                        + String.valueOf(left) + " tributes remain.");
                plugin.leaveGame(p, false);
                event.setDeathMessage(null);
                p.getInventory().clear();
                p.getInventory().setArmorContents(null);
                p.getWorld().strikeLightningEffect(p.getLocation());
            }
        }
       
        @EventHandler
        public void onLogout(PlayerQuitEvent event){
            Player p = event.getPlayer();
            if (plugin.players.contains(p) && plugin.started) {
                final int left = plugin.players.size() - 1;
                plugin.LiveBroadcast(p.getName() + " has logged out, and forfeited. "
                        + String.valueOf(left) + " tributes remain.");
                plugin.leaveGame(p, false);
                p.getInventory().clear();
                p.getInventory().setArmorContents(null);
                p.getWorld().strikeLightningEffect(p.getLocation());
            }
        }
    The leaveGame function...


    Code:
    public void leaveGame(Player player, boolean broadcast) {
            if (broadcast) LiveBroadcast(player.getName() + " has left The Hunger Games.");
            removePlayer(player.getName());
            if (players.size() == 1 && started) {
                broadcast(players.get(0).getName() + " has won the Hunger Games!");
                started = false;
                ScheduleGame();
            }
            player.teleport(deatharea);
        }
    Thanks for your help man

    I just tested using a Sync thread instead of an aSync - The error still occurred.

    Code:
    18:15:51 [SEVERE] java.lang.IndexOutOfBoundsException: Index: 311, Size: 311
    18:15:51 [SEVERE]      at java.util.ArrayList.RangeCheck(ArrayList.java:547)
    18:15:51 [SEVERE]      at java.util.ArrayList.remove(ArrayList.java:387)
    18:15:51 [SEVERE]      at net.minecraft.server.World.tickEntities(World.java:1162)
    18:15:51 [SEVERE]      at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:558)
    18:15:51 [SEVERE]      at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:459)
    18:15:51 [SEVERE]      at net.minecraft.server.ThreadServerApplication.run(SourceFile:492)
    18:15:51 [SEVERE] Unexpected exception
    java.lang.IndexOutOfBoundsException: Index: 311, Size: 311
            at java.util.ArrayList.RangeCheck(ArrayList.java:547)
            at java.util.ArrayList.remove(ArrayList.java:387)
            at net.minecraft.server.World.tickEntities(World.java:1162)
            at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:558)
            at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:459)
            at net.minecraft.server.ThreadServerApplication.run(SourceFile:492)
    
    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 26, 2016
  11. Offline

    Njol

    Well the error has to occurr somewhere at the end of this loop:
    Code:
            for (i = 0; i < this.entityList.size(); ++i) {
                entity = (Entity) this.entityList.get(i);
                if (entity.vehicle != null) {
                    if (!entity.vehicle.dead && entity.vehicle.passenger == entity) {
                        continue;
                    }
     
                    entity.vehicle.passenger = null;
                    entity.vehicle = null;
                }
     
                if (!entity.dead) {
                    this.playerJoinedWorld(entity);
                }
     
                // MethodProfiler.a("remove"); // CraftBukkit - not in production code
                if (entity.dead) {
                    j = entity.ca;
                    k = entity.cc;
                    if (entity.bZ && this.isChunkLoaded(j, k)) {
                        this.getChunkAt(j, k).b(entity);
                    }
     
                    this.entityList.remove(i--);
                    this.d(entity);
                }
     
                // MethodProfiler.a(); // CraftBukkit - not in production code
            }
    
    The only method apart from isChunkLoaded and getChunks is can see is the following:
    Code:
        public void playerJoinedWorld(Entity entity) {
            this.entityJoinedWorld(entity, true);
        }
     
        public void entityJoinedWorld(Entity entity, boolean flag) {
            int i = MathHelper.floor(entity.locX);
            int j = MathHelper.floor(entity.locZ);
            byte b0 = 32;
     
            if (!flag || this.a(i - b0, 0, j - b0, i + b0, 0, j + b0)) {
                entity.bL = entity.locX;
                entity.bM = entity.locY;
                entity.bN = entity.locZ;
                entity.lastYaw = entity.yaw;
                entity.lastPitch = entity.pitch;
                if (flag && entity.bZ) {
                    if (entity.vehicle != null) {
                        entity.R();
                    } else {
                        entity.F_();
                    }
                }
     
                // MethodProfiler.a("chunkCheck"); // CraftBukkit - not in production code
                if (Double.isNaN(entity.locX) || Double.isInfinite(entity.locX)) {
                    entity.locX = entity.bL;
                }
     
                if (Double.isNaN(entity.locY) || Double.isInfinite(entity.locY)) {
                    entity.locY = entity.bM;
                }
     
                if (Double.isNaN(entity.locZ) || Double.isInfinite(entity.locZ)) {
                    entity.locZ = entity.bN;
                }
     
                if (Double.isNaN((double) entity.pitch) || Double.isInfinite((double) entity.pitch)) {
                    entity.pitch = entity.lastPitch;
                }
     
                if (Double.isNaN((double) entity.yaw) || Double.isInfinite((double) entity.yaw)) {
                    entity.yaw = entity.lastYaw;
                }
     
                int k = MathHelper.floor(entity.locX / 16.0D);
                int l = MathHelper.floor(entity.locY / 16.0D);
                int i1 = MathHelper.floor(entity.locZ / 16.0D);
     
                if (!entity.bZ || entity.ca != k || entity.cb != l || entity.cc != i1) {
                    if (entity.bZ && this.isChunkLoaded(entity.ca, entity.cc)) {
                        this.getChunkAt(entity.ca, entity.cc).a(entity, entity.cb);
                    }
     
                    if (this.isChunkLoaded(k, i1)) {
                        entity.bZ = true;
                        this.getChunkAt(k, i1).a(entity);
                    } else {
                        entity.bZ = false;
                    }
                }
     
                // MethodProfiler.a(); // CraftBukkit - not in production code
                if (flag && entity.bZ && entity.passenger != null) {
                    if (!entity.passenger.dead && entity.passenger.vehicle == entity) {
                        this.playerJoinedWorld(entity.passenger);
                    } else {
                        entity.passenger.vehicle = null;
                        entity.passenger = null;
                    }
                }
            }
        }
    
    My guess it that is is related to chunks or vehicles, but that would still not make much sense.
    Can you please search through all your files for async tasks (in Eclipse press ctrl+H -> file search (the first tab)). You might have a misplaced 'a' somewhere.
     
Thread Status:
Not open for further replies.

Share This Page