diff --git a/src/main/java/com/comandante/creeper/CreeperEvent.java b/src/main/java/com/comandante/creeper/CreeperEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a50a8fdc46b680d35710ec9e13973dff2cb122d5
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/CreeperEvent.java
@@ -0,0 +1,5 @@
+package com.comandante.creeper;
+
+public interface CreeperEvent {
+    void run();
+}
diff --git a/src/main/java/com/comandante/creeper/SingleThreadedCreeperEventProcessor.java b/src/main/java/com/comandante/creeper/SingleThreadedCreeperEventProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..7aaca477438f28c1c5d0e4ab5e62a2f993473567
--- /dev/null
+++ b/src/main/java/com/comandante/creeper/SingleThreadedCreeperEventProcessor.java
@@ -0,0 +1,47 @@
+package com.comandante.creeper;
+
+import com.google.api.client.util.Lists;
+import com.google.common.util.concurrent.AbstractScheduledService;
+import org.apache.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class SingleThreadedCreeperEventProcessor extends AbstractScheduledService {
+
+    private final ArrayBlockingQueue<CreeperEvent> creeperEventQueue;
+    private static final Logger log = Logger.getLogger(SingleThreadedCreeperEventProcessor.class);
+
+    public SingleThreadedCreeperEventProcessor(ArrayBlockingQueue<CreeperEvent> creeperEventQueue) {
+        this.creeperEventQueue = creeperEventQueue;
+    }
+
+    @Override
+    protected void runOneIteration() throws Exception {
+        ArrayList<CreeperEvent> events = Lists.newArrayList();
+        creeperEventQueue.drainTo(events);
+        events.forEach(this::safeRun);
+    }
+
+    public void addEvent(CreeperEvent event) {
+        try {
+            creeperEventQueue.put(event);
+        } catch (InterruptedException ex) {
+            log.error("Problem adding event.", ex);
+        }
+    }
+
+    private void safeRun(final CreeperEvent e) {
+        try {
+            e.run();
+        } catch (Exception ex) {
+            log.error("Problem executing event.", ex);
+        }
+    }
+
+    @Override
+    protected Scheduler scheduler() {
+        return Scheduler.newFixedDelaySchedule(0, 50, TimeUnit.MILLISECONDS);
+    }
+}
diff --git a/src/main/java/com/comandante/creeper/managers/GameManager.java b/src/main/java/com/comandante/creeper/managers/GameManager.java
index 743f940f2884c3ecf1a70d34232dfca03fad7426..5a26337800b7b59cda85493106f5b4613b958d97 100755
--- a/src/main/java/com/comandante/creeper/managers/GameManager.java
+++ b/src/main/java/com/comandante/creeper/managers/GameManager.java
@@ -5,6 +5,7 @@ import com.comandante.creeper.CreeperConfiguration;
 import com.comandante.creeper.IrcBotService;
 import com.comandante.creeper.Items.*;
 import com.comandante.creeper.Main;
+import com.comandante.creeper.SingleThreadedCreeperEventProcessor;
 import com.comandante.creeper.bot.BotCommandFactory;
 import com.comandante.creeper.bot.BotCommandManager;
 import com.comandante.creeper.entity.CreeperEntity;
@@ -37,6 +38,7 @@ import org.nocrala.tools.texttablefmt.Table;
 
 import java.text.NumberFormat;
 import java.util.*;
+import java.util.concurrent.ArrayBlockingQueue;
 
 import static com.comandante.creeper.server.Color.*;
 
@@ -67,6 +69,7 @@ public class GameManager {
     private final TimeTracker timeTracker;
     private final ItemUseHandler itemUseHandler;
     private final NpcMover npcMover;
+    private final SingleThreadedCreeperEventProcessor eventProcessor = new SingleThreadedCreeperEventProcessor(new ArrayBlockingQueue<>(100000));
 
     public GameManager(CreeperConfiguration creeperConfiguration, RoomManager roomManager, PlayerManager playerManager, EntityManager entityManager, MapsManager mapsManager, ChannelCommunicationUtils channelUtils) {
         this.roomManager = roomManager;
@@ -92,6 +95,7 @@ public class GameManager {
         this.entityManager.addEntity(itemDecayManager);
         this.itemUseHandler = new ItemUseHandler(this);
         this.npcMover = new NpcMover(this);
+        this.eventProcessor.startAsync();
     }
 
     public NpcMover getNpcMover() {
@@ -166,6 +170,10 @@ public class GameManager {
         return timeTracker;
     }
 
+    public SingleThreadedCreeperEventProcessor getEventProcessor() {
+        return eventProcessor;
+    }
+
     public void placePlayerInLobby(Player player) {
         Room room = roomManager.getRoom(LOBBY_ID);
         room.addPresentPlayer(player.getPlayerId());
diff --git a/src/main/java/com/comandante/creeper/npc/Npc.java b/src/main/java/com/comandante/creeper/npc/Npc.java
index c79b4912e183b01a27a12b0acc7c6285f9701b8f..76a630d4fdfb6a3a68e8146d9b93968521f50adf 100644
--- a/src/main/java/com/comandante/creeper/npc/Npc.java
+++ b/src/main/java/com/comandante/creeper/npc/Npc.java
@@ -244,8 +244,8 @@ public class Npc extends CreeperEntity {
 
             long xpEarned = (long) (experienceManager.calculateNpcXp(playerLevel, npcLevel) * playerDamagePercentValue);
             p.addExperience(xpEarned);
-            p.addNpcKillLog(getName());
             gameManager.getChannelUtils().write(p.getPlayerId(), getBattleReport(xpEarned) + "\r\n", true);
+            p.addNpcKillLog(getName());
         }
     }
 
diff --git a/src/main/java/com/comandante/creeper/player/Player.java b/src/main/java/com/comandante/creeper/player/Player.java
index 5a0172536892e379bd8aabc6a993c6bdfb0b7545..24a16bac2ea685c6cc9d6473f92ed1edc6c6f6a5 100755
--- a/src/main/java/com/comandante/creeper/player/Player.java
+++ b/src/main/java/com/comandante/creeper/player/Player.java
@@ -362,11 +362,13 @@ public class Player extends CreeperEntity {
     }
 
     public void addNpcKillLog(String npcName) {
-        synchronized (interner.intern(playerId)) {
-            PlayerMetadata playerMetadata = getPlayerMetadata();
-            playerMetadata.addNpcKill(npcName);
-            savePlayerMetadata(playerMetadata);
-        }
+        gameManager.getEventProcessor().addEvent(() -> {
+            synchronized (interner.intern(playerId)) {
+                PlayerMetadata playerMetadata = getPlayerMetadata();
+                playerMetadata.addNpcKill(npcName);
+                savePlayerMetadata(playerMetadata);
+            }
+        });
     }
 
     public void transferItemFromLocker(String entityId) {
diff --git a/src/main/java/com/comandante/creeper/spawner/NpcSpawner.java b/src/main/java/com/comandante/creeper/spawner/NpcSpawner.java
index 07b9908938cdf80bcfd60ddb8310eb7030e63785..b479ec54ef7b5ef6a8170169f99f658b03c3ccb7 100755
--- a/src/main/java/com/comandante/creeper/spawner/NpcSpawner.java
+++ b/src/main/java/com/comandante/creeper/spawner/NpcSpawner.java
@@ -9,13 +9,12 @@ import com.comandante.creeper.npc.Npc;
 import com.comandante.creeper.npc.NpcBuilder;
 import com.comandante.creeper.world.Area;
 import com.comandante.creeper.world.Room;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
 
-import java.util.ArrayList;
+import java.util.List;
 import java.util.Random;
 import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 public class NpcSpawner extends CreeperEntity {
 
@@ -69,7 +68,7 @@ public class NpcSpawner extends CreeperEntity {
     }
 
     private void createAndAddItem(Area spawnArea) {
-        ArrayList<Room> rooms = Lists.newArrayList(Iterators.filter(gameManager.getRoomManager().getRoomsByArea(spawnArea).iterator(), getRoomsWithRoom()));
+        List<Room> rooms = gameManager.getRoomManager().getRoomsByArea(spawnArea).stream().filter(getRoomsWithRoom()).collect(Collectors.toList());
         Room room = rooms.get(random.nextInt(rooms.size()));
         NpcBuilder npcBuilder = new NpcBuilder(npc);
         Npc newNpc = npcBuilder.createNpc();
@@ -80,23 +79,30 @@ public class NpcSpawner extends CreeperEntity {
         Main.metrics.counter(MetricRegistry.name(NpcSpawner.class, npc.getName() + "-spawn")).inc();
     }
 
+    private Predicate<Room> getRoomsWithRoomNew() {
+        return room -> {
+            int count = room.getNpcIds().stream().filter(npcId -> {
+                Npc npcEntity = gameManager.getEntityManager().getNpcEntity(npcId);
+                return npcEntity.getName().equals(npc.getName());
+            }).collect(Collectors.toList()).size();
+            return count < spawnRule.getMaxPerRoom();
+        };
+    }
+
     private Predicate<Room> getRoomsWithRoom() {
-        return new Predicate<Room>() {
-            @Override
-            public boolean apply(Room room) {
-                int count = 0;
-                Set<String> npcIds = room.getNpcIds();
-                for (String npcId : npcIds) {
-                    Npc npcEntity = gameManager.getEntityManager().getNpcEntity(npcId);
-                    if (npcEntity.getName().equals(npc.getName())) {
-                        count++;
-                    }
-                }
-                if (count < spawnRule.getMaxPerRoom()) {
-                    return true;
+        return room -> {
+            int count = 0;
+            Set<String> npcIds = room.getNpcIds();
+            for (String npcId : npcIds) {
+                Npc npcEntity = gameManager.getEntityManager().getNpcEntity(npcId);
+                if (npcEntity.getName().equals(npc.getName())) {
+                    count++;
                 }
-                return false;
             }
+            if (count < spawnRule.getMaxPerRoom()) {
+                return true;
+            }
+            return false;
         };
     }
 }
\ No newline at end of file