Server Concurrent Modification

Discussion in 'Plugin Development' started by mindless728, Jun 15, 2014.

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

    mindless728

    It is not often that I have to ask for help with something, however in making a custom map system to override the normal maps I have run into this fun issue.

    This crash seems to be happening when there is a map in an item frame and reproduces at what seems random times (I am thinking race condition). I have not seem this happen when a player is only holding a map.

    Any thoughts on how to prevent the server thread from trying to iterate over what it is modifying (by changing the renderer) or if this is a bug deeper down?

    Code:
    java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(HashMap.java:926)
        at java.util.HashMap$KeyIterator.next(HashMap.java:960)
        at net.minecraft.server.v1_7_R3.EntityTracker.updatePlayers(EntityTracker.java:152)
        at net.minecraft.server.v1_7_R3.MinecraftServer.v(MinecraftServer.java:658)
        at net.minecraft.server.v1_7_R3.DedicatedServer.v(DedicatedServer.java:260)
        at net.minecraft.server.v1_7_R3.MinecraftServer.u(MinecraftServer.java:558)
        at net.minecraft.server.v1_7_R3.MinecraftServer.run(MinecraftServer.java:469)
        at net.minecraft.server.v1_7_R3.ThreadServerApplication.run(SourceFile:628)
    
    Full report at the bottom of the post.

    This is the code for the map renderer:
    Code:
    package net.mctitan.land.map;
    
    import java.util.EnumSet;
    import java.util.HashMap;
    
    import net.mctitan.land.data.DataManager;
    import net.mctitan.land.data.Nation;
    import net.mctitan.land.data.LandSection;
    import net.mctitan.land.data.Player;
    
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.block.BlockFace;
    import org.bukkit.map.MapRenderer;
    import org.bukkit.map.MapView;
    import org.bukkit.map.MapCanvas;
    
    /**
     *
     * @author colin
     */
    public class NationMapRenderer extends MapRenderer {
        private int maxtimer;
        private int dzoom;
        private final int offset = -63;
        
        private HashMap<String,Integer> timers;
        
        public NationMapRenderer() {
            super(true);
            maxtimer = net.mctitan.land.NationLands.getInstance().getConfig().getInt("MapOptions.updateDelay");
            dzoom = net.mctitan.land.NationLands.getInstance().getConfig().getInt("MapOptions.startZoom");
            timers = new HashMap<>();
        }
        
        @Override
        public void render(MapView map, MapCanvas canvas, org.bukkit.entity.Player p) {
            if(p.getItemInHand().getType() != Material.MAP && p.getItemInHand().getDurability() != map.getId()) {
                return;
            }
            
            Player player = DataManager.getInstance().getPlayer(p);
            
            if(!timers.containsKey(player.UUID)) {
                timers.put(player.UUID, 1);
                return;
            }
            
            int timer = timers.get(player.UUID);
            ++timer;
            timers.put(player.UUID, timer);
            
            int zoom = dzoom + map.getScale().getValue();
            
            if(timer <= zoom * maxtimer) {
                return;
            }
            timer = 0;
            
            timers.put(player.UUID, timer);
            
            Location loc = p.getLocation();
            Location tmp = new Location(loc.getWorld(), 0, 0, 0);
            EnumSet<NationMapColor> colors = EnumSet.noneOf(NationMapColor.class);
            boolean exterior = false;
            boolean bt;
            
            for(int i = 0; i < 128; ++i) {
                for(int j = 0; j < 128; ++j) {
                    colors.clear();
                    exterior = false;
                    
                    for(int a = 0; a < zoom; ++a) {
                        for(int b = 0; b < zoom; ++b) {
                            tmp.setX(loc.getBlockX()+(i+offset)*zoom+a);
                            tmp.setZ(loc.getBlockZ()+(j+offset)*zoom+b);
                            
                            colors.add(getColor(p,tmp));
                            
                            if(!exterior && (bt = isExterior(tmp)))
                                exterior = bt;
                        }
                    }
                    
                    NationMapColor color = colors.iterator().next();
                    canvas.setPixel(i, j, color.color(exterior));
                }
            }
            
            drawPlayerArror(canvas,p);
        }
        
        public void drawPlayerArror(MapCanvas canvas, org.bukkit.entity.Player player) {
            canvas.setPixel(-offset, -offset, NationMapColor.PLAYER_LOCATION.color());
        }
        
        public NationMapColor getColor(org.bukkit.entity.Player p, Location tmp) {
            Player player = DataManager.getInstance().getPlayer(p);
            LandSection section = DataManager.getInstance().getLandSection(tmp);
            Nation sNation = DataManager.getInstance().getNation(section == null?null:section.ownerUUID);
            
            if(section == null)
                return NationMapColor.WILDERNESS_INVALID;
            
            if(player.nation != null && section.ownerUUID != null && section.ownerUUID.equals(player.nation)) {
                if(sNation.getSectionType(section.UUID) == Nation.SectionType.PROTECT)
                    return NationMapColor.SELF_NATION_PROTECT;
                else
                    return NationMapColor.SELF_NATION_CLAIM;
            }
            
            if(section.ownerUUID != null) {
                if(sNation.getSectionType(section.UUID) == Nation.SectionType.PROTECT)
                    return NationMapColor.OTHER_NATION_PROTECT;
                else
                    return NationMapColor.OTHER_NATION_CLAIM;
            }
            
            if(player.nation != null && section.watches.contains(player.nation))
                return NationMapColor.SELF_NATION_WATCH;
            
            return NationMapColor.WILDERNESS_VALID;
        }
        
        public boolean isExterior(Location l) {
            BlockFace faces[] = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST};
            LandSection section = DataManager.getInstance().getLandSection(l);
            
            if(section == null)
                return false;
            
            for(BlockFace bf : faces) {
                LandSection other = DataManager.getInstance().getLandSection(l.getBlock().getRelative(bf).getLocation());
                
                if(other != null && other != section)
                    return true;
            }
            
            return false;
        }
        
        public static void applyToMap(MapView map) {
            if(map != null) {
                for(MapRenderer r : map.getRenderers())
                    map.removeRenderer(r);
                
                map.addRenderer(new NationMapRenderer());
            }
        }
    }
    
    
    Full Crash Report:
    Show Spoiler

    Code:
    ---- Minecraft Crash Report ----
    // This doesn't make any sense!
    
    Time: 6/14/14 8:30 PM
    Description: Exception in server tick loop
    
    java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(HashMap.java:926)
        at java.util.HashMap$KeyIterator.next(HashMap.java:960)
        at net.minecraft.server.v1_7_R3.EntityTracker.updatePlayers(EntityTracker.java:152)
        at net.minecraft.server.v1_7_R3.MinecraftServer.v(MinecraftServer.java:658)
        at net.minecraft.server.v1_7_R3.DedicatedServer.v(DedicatedServer.java:260)
        at net.minecraft.server.v1_7_R3.MinecraftServer.u(MinecraftServer.java:558)
        at net.minecraft.server.v1_7_R3.MinecraftServer.run(MinecraftServer.java:469)
        at net.minecraft.server.v1_7_R3.ThreadServerApplication.run(SourceFile:628)
    
    
    A detailed walkthrough of the error, its code path and all known details is as follows:
    ---------------------------------------------------------------------------------------
    
    -- System Details --
    Details:
        Minecraft Version: 1.7.9
        Operating System: Mac OS X (x86_64) version 10.10
        Java Version: 1.7.0_51, Oracle Corporation
        Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
        Memory: 771541656 bytes (735 MB) / 1067974656 bytes (1018 MB) up to 7635730432 bytes (7282 MB)
        JVM Flags: 2 total; -Xms1G -Xmx8G
        AABB Pool Size: 0 (0 bytes; 0 MB) allocated, 0 (0 bytes; 0 MB) used
        IntCache: cache: 0, tcache: 0, allocated: 13, tallocated: 95
        CraftBukkit Information: 
       Running: CraftBukkit version git-Bukkit-1.7.9-R0.1-b3084jnks (MC: 1.7.9) (Implementing API version 1.7.9-R0.1) true
       Plugins: { OpenInv v2.1.7 com.lishid.openinv.OpenInv [lishid], BlockStorage v1.5 net.mctitan.blockstorage.BlockStorage [], PermissionsEx v1.22 ru.tehkode.permissions.bukkit.PermissionsEx [[t3hk0d3, zml]], NationLands v1.0 net.mctitan.land.NationLands [], VanishNoPacket v3.19.1 org.kitteh.vanish.VanishPlugin [mbaxter],}
       Warnings: DEFAULT
       Threads: { RUNNABLE Server thread: [java.lang.Thread.dumpThreads(Native Method), java.lang.Thread.getAllStackTraces(Thread.java:1639), org.bukkit.craftbukkit.v1_7_R3.CraftCrashReport.call(CraftCrashReport.java:28), net.minecraft.server.v1_7_R3.CrashReportSystemDetails.a(SourceFile:74), net.minecraft.server.v1_7_R3.CrashReport.h(CrashReport.java:45), net.minecraft.server.v1_7_R3.CrashReport.<init>(CrashReport.java:33), net.minecraft.server.v1_7_R3.MinecraftServer.run(MinecraftServer.java:486), net.minecraft.server.v1_7_R3.ThreadServerApplication.run(SourceFile:628)], WAITING Reference Handler: [java.lang.Object.wait(Native Method), java.lang.Object.wait(Object.java:503), java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)], WAITING Finalizer: [java.lang.Object.wait(Native Method), java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135), java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151), java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)], RUNNABLE Thread-5: [java.io.FileOutputStream.writeBytes(Native Method), java.io.FileOutputStream.write(FileOutputStream.java:345), java.io.BufferedOutputStream.write(BufferedOutputStream.java:122), java.io.PrintStream.write(PrintStream.java:480), java.io.FilterOutputStream.write(FilterOutputStream.java:97), org.bukkit.craftbukkit.v1_7_R3.util.TerminalConsoleWriterThread.run(TerminalConsoleWriterThread.java:34), java.lang.Thread.run(Thread.java:744)], RUNNABLE Netty IO #1: [sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method), sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:200), sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103), sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87), sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98), net.minecraft.util.io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:591), net.minecraft.util.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:301), net.minecraft.util.io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101), java.lang.Thread.run(Thread.java:744)], RUNNABLE Netty IO #4: [sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method), sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:200), sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103), sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87), sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98), net.minecraft.util.io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:591), net.minecraft.util.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:301), net.minecraft.util.io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101), java.lang.Thread.run(Thread.java:744)], WAITING Chunk I/O Executor Thread-1: [sun.misc.Unsafe.park(Native Method), java.util.concurrent.locks.LockSupport.park(LockSupport.java:186), java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043), java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442), java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068), java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130), java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615), java.lang.Thread.run(Thread.java:744)], TIMED_WAITING Snooper Timer: [java.lang.Object.wait(Native Method), java.util.TimerThread.mainLoop(Timer.java:552), java.util.TimerThread.run(Timer.java:505)], RUNNABLE Signal Dispatcher: [], RUNNABLE Server console handler: [java.io.FileInputStream.readBytes(Native Method), java.io.FileInputStream.read(FileInputStream.java:272), java.io.BufferedInputStream.fill(BufferedInputStream.java:235), java.io.BufferedInputStream.read(BufferedInputStream.java:254), java.io.FilterInputStream.read(FilterInputStream.java:83), org.bukkit.craftbukkit.libs.jline.console.ConsoleReader$1.read(ConsoleReader.java:167), org.bukkit.craftbukkit.libs.jline.internal.InputStreamReader.read(InputStreamReader.java:267), org.bukkit.craftbukkit.libs.jline.internal.InputStreamReader.read(InputStreamReader.java:204), org.bukkit.craftbukkit.libs.jline.console.ConsoleReader.readCharacter(ConsoleReader.java:995), org.bukkit.craftbukkit.libs.jline.console.ConsoleReader.readLine(ConsoleReader.java:1167), net.minecraft.server.v1_7_R3.ThreadCommandReader.run(ThreadCommandReader.java:32)], TIMED_WAITING File IO Thread: [java.lang.Thread.sleep(Native Method), net.minecraft.server.v1_7_R3.FileIOThread.b(SourceFile:44), net.minecraft.server.v1_7_R3.FileIOThread.run(SourceFile:23), java.lang.Thread.run(Thread.java:744)], WAITING PermissionsEx-Cleaner: [java.lang.Object.wait(Native Method), java.lang.Object.wait(Object.java:503), java.util.TimerThread.mainLoop(Timer.java:526), java.util.TimerThread.run(Timer.java:505)], TIMED_WAITING Server Infinisleeper: [java.lang.Thread.sleep(Native Method), net.minecraft.server.v1_7_R3.ThreadSleepForever.run(SourceFile:65)], RUNNABLE Netty IO #3: [sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method), sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:200), sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103), sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87), sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98), net.minecraft.util.io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:591), net.minecraft.util.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:301), net.minecraft.util.io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101), java.lang.Thread.run(Thread.java:744)], RUNNABLE DestroyJavaVM: [], RUNNABLE Netty IO #0: [sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method), sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:200), sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103), sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87), sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98), net.minecraft.util.io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:591), net.minecraft.util.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:301), net.minecraft.util.io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101), java.lang.Thread.run(Thread.java:744)], RUNNABLE Netty IO #2: [sun.nio.ch.KQueueArrayWrapper.kevent0(Native Method), sun.nio.ch.KQueueArrayWrapper.poll(KQueueArrayWrapper.java:200), sun.nio.ch.KQueueSelectorImpl.doSelect(KQueueSelectorImpl.java:103), sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87), sun.nio.ch.SelectorImpl.select(SelectorImpl.java:98), net.minecraft.util.io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:591), net.minecraft.util.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:301), net.minecraft.util.io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101), java.lang.Thread.run(Thread.java:744)],}
       Recent tasks from 29091-29121{VanishNoPacket:org.kitteh.vanish.metrics.Metrics$1@24031,}
        Profiler Position: N/A (disabled)
        Vec3 Pool Size: 0 (0 bytes; 0 MB) allocated, 0 (0 bytes; 0 MB) used
        Player Count: 2 / 16; [EntityPlayer['mindless728'/104, l='world', x=74.17, y=67.00, z=73.49](mindless728 at 74.16712005100298,67.0,73.49475389822963), EntityPlayer['EvenDefthands'/2258, l='world', x=65.50, y=68.92, z=78.39](EvenDefthands at 65.49769752299777,68.91613285537002,78.39188282521206)]
        Is Modded: Definitely; Server brand changed to 'CraftBukkit'
        Type: Dedicated Server (map_server.txt)
    
     
  2. Offline

    mindless728

    Going to bump this once to see if anyone knows if this is an issue internally in Bukkit or if there is something in the Map API that I am using wrong.
     
  3. Offline

    JaguarJo

    Moved to a more appropriate section.
     
  4. Offline

    AoH_Ruthless

  5. Offline

    mindless728

    I am well aware of what a ConcurrentModificationException is, however if you look at the stack trace from the crash the main server thread is what caused it and not my code (from what I can tell)

    What I am wondering is if I am using the Map API incorrectly and someone knows what that is or if the server itself has an issue with per player context MapCanvas objects.
     
  6. Offline

    LucasEmanuel

    You can't remove from a Map while looping over it, this is what you seem to be doing in your function "applyToMap". If you want to loop over a collection and remove from it inside the loop you have to use an iterator.
     
  7. Offline

    mindless728

    The code in CraftBukkit for CraftMapView.getRenderers() from
    https://github.com/Bukkit/CraftBukk.../org/bukkit/craftbukkit/map/CraftMapView.java

    Code:
    public List<MapRenderer> getRenderers() {
            return new ArrayList<MapRenderer>(renderers);
    }
    
    So that function, so long as it isn't in another thread, should not be an issue
     
Thread Status:
Not open for further replies.

Share This Page