diff --git a/pom.xml b/pom.xml
index 32f785faa3efc5ac0ff1b6aeae699a9db6c9a934..7003f73548cc923f87886f650915d3db84b1d545 100644
--- a/pom.xml
+++ b/pom.xml
@@ -187,7 +187,7 @@
         <dependency>
             <groupId>com.syncleus.ferma</groupId>
             <artifactId>ferma</artifactId>
-            <version>3.0.4-SNAPSHOT</version>
+            <version>3.1.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.tinkerpop</groupId>
@@ -204,6 +204,11 @@
             <artifactId>commons-beanutils</artifactId>
             <version>1.9.3</version>
         </dependency>
+        <dependency>
+            <groupId>com.syncleus.ferma</groupId>
+            <artifactId>ferma-orientdb</artifactId>
+            <version>3.0.0-SNAPSHOT</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/src/main/java/com/syncleus/aethermud/Main.java b/src/main/java/com/syncleus/aethermud/Main.java
index 131656f75571b4bf20c3c37586c30b8bbc7a6dc7..b8bc48901265a84f829ba393eba29ebe14f585ca 100644
--- a/src/main/java/com/syncleus/aethermud/Main.java
+++ b/src/main/java/com/syncleus/aethermud/Main.java
@@ -26,13 +26,13 @@ import com.syncleus.aethermud.configuration.AetherMudConfiguration;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.core.SessionManager;
 import com.syncleus.aethermud.entity.EntityManager;
-import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.player.PlayerManagementManager;
 import com.syncleus.aethermud.player.PlayerManager;
 import com.syncleus.aethermud.server.communication.ChannelUtils;
 import com.syncleus.aethermud.server.telnet.AetherMudServer;
 import com.syncleus.aethermud.storage.WorldStorage;
 import com.syncleus.aethermud.storage.graphdb.*;
+import com.syncleus.aethermud.storage.graphdb.model.*;
 import com.syncleus.aethermud.world.MapsManager;
 import com.syncleus.aethermud.world.RoomManager;
 import com.google.common.io.Files;
@@ -58,16 +58,6 @@ public class Main {
     final public static MetricRegistry metrics = new MetricRegistry();
 
     final public static Set<Character> vowels = new HashSet<Character>(Arrays.asList('a', 'e', 'i', 'o', 'u'));
-    public static final Set<Class<?>> FRAMED_TYPES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new Class<?>[]{
-        PlayerData.class,
-        NpcData.class,
-        StatsData.class,
-        ItemData.class,
-        LootData.class,
-        SpawnRuleData.class,
-        CoolDownData.class,
-        EffectData.class
-    })));
 
     public static String getAetherMudVersion() {
 
@@ -99,22 +89,9 @@ public class Main {
 
         Files.isDirectory().apply(new File("world/"));
 
-        Graph graph = TinkerGraph.open();
-        File f = new File(aetherMudConfiguration.databaseFileName);
-        if(f.exists() && !f.isDirectory()) {
-            try {
-                graph.io(IoCore.graphson()).readGraph(aetherMudConfiguration.databaseFileName);
-            } catch (IOException e) {
-                throw new IllegalStateException("Could not read from graph file despite being present.", e);
-            }
-        }
-        WrappedFramedGraph<Graph> framedGraph = new DelegatingFramedGraph(graph, FRAMED_TYPES);
-
-        GraphDbAetherMudStorage graphStorage = new GraphDbAetherMudStorage(framedGraph, aetherMudConfiguration.databaseFileName);
-        graphStorage.startAsync();
-        graphStorage.awaitRunning();
+        GraphStorageFactory graphStorageFactory = new GraphStorageFactory();
 
-        PlayerManager playerManager = new PlayerManager(graphStorage, new SessionManager());
+        PlayerManager playerManager = new PlayerManager(graphStorageFactory, new SessionManager());
         playerManager.createAllGauges();
 
         RoomManager roomManager = new RoomManager(playerManager);
@@ -122,8 +99,8 @@ public class Main {
         startUpMessage("Configuring core systems.");
         MapsManager mapsManager = new MapsManager(aetherMudConfiguration, roomManager);
         ChannelUtils channelUtils = new ChannelUtils(playerManager, roomManager);
-        EntityManager entityManager = new EntityManager(graphStorage, roomManager, playerManager);
-        GameManager gameManager = new GameManager(graphStorage, framedGraph, aetherMudConfiguration, roomManager, playerManager, entityManager, mapsManager, channelUtils, HttpClients.createDefault());
+        EntityManager entityManager = new EntityManager(graphStorageFactory, roomManager, playerManager);
+        GameManager gameManager = new GameManager(graphStorageFactory, aetherMudConfiguration, roomManager, playerManager, entityManager, mapsManager, channelUtils, HttpClients.createDefault());
 
         startUpMessage("Reading world from disk.");
         WorldStorage worldExporter = new WorldStorage(roomManager, mapsManager, gameManager.getFloorManager(), entityManager, gameManager);
diff --git a/src/main/java/com/syncleus/aethermud/bot/command/commands/WhoBotCommand.java b/src/main/java/com/syncleus/aethermud/bot/command/commands/WhoBotCommand.java
index 1bbb899c357214934a46dd91ed431ad2cc15a28c..fb7560f969180a88be5083b6e6c97e443661ee79 100644
--- a/src/main/java/com/syncleus/aethermud/bot/command/commands/WhoBotCommand.java
+++ b/src/main/java/com/syncleus/aethermud/bot/command/commands/WhoBotCommand.java
@@ -17,7 +17,7 @@ package com.syncleus.aethermud.bot.command.commands;
 
 import com.syncleus.aethermud.bot.command.BotCommandManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.stats.Levels;
 import com.google.api.client.util.Lists;
 import com.google.common.collect.Sets;
@@ -42,12 +42,7 @@ public class WhoBotCommand extends BotCommand {
         ArrayList<String> resp = Lists.newArrayList();
         Set<Player> allPlayers = botCommandManager.getGameManager().getAllPlayers();
         for (Player player: allPlayers) {
-            Optional<PlayerData> playerMetadataOptional = botCommandManager.getGameManager().getPlayerManager().getPlayerMetadata(player.getPlayerId());
-            if (!playerMetadataOptional.isPresent()) {
-                continue;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            String line = player.getPlayerName() + " (level " + Levels.getLevel(playerData.getStats().getExperience()) + ") - " + player.getCurrentRoom().getRoomTitle();
+            String line = player.getPlayerName() + " (level " + Levels.getLevel(player.getStats().getExperience()) + ") - " + player.getCurrentRoom().getRoomTitle();
             resp.add(line);
         }
         return resp;
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/GoldCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/GoldCommand.java
index 379ab761625b497c537f4d3334814b03d2f81e59..f8dcbfe3e069b5739e78835ad086d3a2d015f080 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/GoldCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/GoldCommand.java
@@ -17,7 +17,8 @@ package com.syncleus.aethermud.command.commands;
 
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.server.communication.Color;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.channel.MessageEvent;
@@ -41,12 +42,7 @@ public class GoldCommand extends Command {
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         execCommand(ctx, e, () -> {
-            Optional<PlayerData> playerMetadataOptional = playerManager.getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            write("You have " + NumberFormat.getNumberInstance(Locale.US).format(playerData.getGold()) + Color.YELLOW + " gold." + Color.RESET);
+            PlayerUtil.consume(gameManager, playerId, playerData -> write("You have " + NumberFormat.getNumberInstance(Locale.US).format(playerData.getGold()) + Color.YELLOW + " gold." + Color.RESET));
         });
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/MovementCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/MovementCommand.java
index 03e414a9398ac378a7a1128ae8a71992d4476afa..ab2b03a82965d98aa41b02d7da578879452a6919 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/MovementCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/MovementCommand.java
@@ -16,9 +16,10 @@
 package com.syncleus.aethermud.command.commands;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.EffectPojo;
 import com.syncleus.aethermud.player.CoolDownType;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.EffectData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.player.PlayerMovement;
 import com.syncleus.aethermud.world.model.RemoteExit;
 import com.syncleus.aethermud.world.model.Room;
@@ -64,15 +65,17 @@ public class MovementCommand extends Command {
                 MovementCommand.this.write("You are unable to progress, but can return to where you came from by typing \"back\".");
                 return;
             }
-            java.util.Optional<PlayerData> playerMetadataOptional = playerManager.getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            for (EffectPojo effect : playerMetadataOptional.get().getEffects()) {
-                if (effect.isFrozenMovement()) {
-                    MovementCommand.this.write("You are frozen and can not move.");
+            try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                java.util.Optional<PlayerData> playerMetadataOptional = tx.getStorage().getPlayerMetadata(playerId);
+                if (!playerMetadataOptional.isPresent()) {
                     return;
                 }
+                for (EffectData effect : playerMetadataOptional.get().getEffects()) {
+                    if (effect.isFrozenMovement()) {
+                        MovementCommand.this.write("You are frozen and can not move.");
+                        return;
+                    }
+                }
             }
             final String command = rootCommand;
             PlayerMovement playerMovement = null;
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/RecallCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/RecallCommand.java
index 246cd8a29df945e575b47fa10743f8449c60bb3d..acd56d2855dbcb991877254d4aeabbd95049ab43 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/RecallCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/RecallCommand.java
@@ -16,7 +16,6 @@
 package com.syncleus.aethermud.command.commands;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.player.CoolDownPojo;
 import com.syncleus.aethermud.player.CoolDownType;
 import com.syncleus.aethermud.player.PlayerMovement;
 import org.jboss.netty.channel.ChannelHandlerContext;
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/WhoCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/WhoCommand.java
index dfa6169bcb83d471dbe3632bd3f4e97a1c943696..984d8c5be4e090df207077a0d95aed79c44dbd05 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/WhoCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/WhoCommand.java
@@ -17,7 +17,8 @@ package com.syncleus.aethermud.command.commands;
 
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.stats.Levels;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.channel.MessageEvent;
@@ -55,14 +56,16 @@ public class WhoCommand extends Command {
             Set<Player> allPlayers = gameManager.getAllPlayers();
             for (Player allPlayer : allPlayers) {
                 t.addCell(allPlayer.getPlayerName());
-                Optional<PlayerData> playerMetadataOptional = playerManager.getPlayerMetadata(allPlayer.getPlayerId());
-                if (!playerMetadataOptional.isPresent()){
-                    continue;
+                try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                    Optional<PlayerData> playerMetadataOptional = tx.getStorage().getPlayerMetadata(allPlayer.getPlayerId());
+                    if (!playerMetadataOptional.isPresent()){
+                        continue;
+                    }
+                    PlayerData playerData = playerMetadataOptional.get();
+                    t.addCell(Long.toString(Levels.getLevel(playerData.getStats().getExperience())));
+                    t.addCell(NumberFormat.getNumberInstance(Locale.US).format((playerData.getStats().getExperience())));
+                    t.addCell(roomManager.getPlayerCurrentRoom(allPlayer).get().getRoomTitle());
                 }
-                PlayerData playerData = playerMetadataOptional.get();
-                t.addCell(Long.toString(Levels.getLevel(playerData.getStats().getExperience())));
-                t.addCell(NumberFormat.getNumberInstance(Locale.US).format((playerData.getStats().getExperience())));
-                t.addCell(roomManager.getPlayerCurrentRoom(allPlayer).get().getRoomTitle());
             }
             output.append(t.render());
             write(output.toString());
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/XpCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/XpCommand.java
index daf9f74a40440052e767774cbda3cc50cabb1fd5..c99554ada67f331c27a268cd6b595cfcc72735f7 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/XpCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/XpCommand.java
@@ -18,7 +18,8 @@ package com.syncleus.aethermud.command.commands;
 import com.codahale.metrics.Meter;
 import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.stats.Levels;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.channel.MessageEvent;
@@ -48,29 +49,26 @@ public class XpCommand extends Command {
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         execCommand(ctx, e, () -> {
-            Optional<PlayerData> playerMetadataOptional = playerManager.getPlayerMetadata(player.getPlayerId());
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            int nextLevel = Levels.getLevel(playerData.getStats().getExperience()) + 1;
-            int expToNextLevel = Levels.getXp(nextLevel) - playerData.getStats().getExperience();
-            Meter meter = Main.metrics.meter("experience-" + player.getPlayerName());
+            PlayerUtil.consume(gameManager, playerId, playerData -> {
+                int nextLevel = Levels.getLevel(playerData.getStats().getExperience()) + 1;
+                int expToNextLevel = Levels.getXp(nextLevel) - playerData.getStats().getExperience();
+                Meter meter = Main.metrics.meter("experience-" + player.getPlayerName());
 
-            Table table = new Table(2, BorderStyle.CLASSIC_COMPATIBLE, ShownBorders.NONE);
+                Table table = new Table(2, BorderStyle.CLASSIC_COMPATIBLE, ShownBorders.NONE);
 
-            table.setColumnWidth(0, 8, 20);
-            table.setColumnWidth(1, 10, 20);
-            table.addCell("Window");
-            table.addCell("XP/sec");
-            table.addCell(" 1 min");
-            table.addCell(String.valueOf(round(meter.getOneMinuteRate())));
-            table.addCell(" 5 min");
-            table.addCell(String.valueOf(round(meter.getFiveMinuteRate())));
-            table.addCell("15 min");
-            table.addCell(String.valueOf(round(meter.getFifteenMinuteRate())));
+                table.setColumnWidth(0, 8, 20);
+                table.setColumnWidth(1, 10, 20);
+                table.addCell("Window");
+                table.addCell("XP/sec");
+                table.addCell(" 1 min");
+                table.addCell(String.valueOf(round(meter.getOneMinuteRate())));
+                table.addCell(" 5 min");
+                table.addCell(String.valueOf(round(meter.getFiveMinuteRate())));
+                table.addCell("15 min");
+                table.addCell(String.valueOf(round(meter.getFifteenMinuteRate())));
 
-            write(NumberFormat.getNumberInstance(Locale.US).format(expToNextLevel) + " experience to level " + nextLevel + ".\r\n" + table.render());
+                write(NumberFormat.getNumberInstance(Locale.US).format(expToNextLevel) + " experience to level " + nextLevel + ".\r\n" + table.render());
+            });
         });
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadNpcCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadNpcCommand.java
index 42ca05cf4b435fdb073fa26c65cc0fbd1cab847f..c151ddee4a92512471e06cd27dfd44ea1a4e5c45 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadNpcCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadNpcCommand.java
@@ -21,7 +21,8 @@ import com.syncleus.aethermud.npc.Npc;
 import com.syncleus.aethermud.player.PlayerRole;
 import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.storage.AetherMudStorage;
-import com.syncleus.aethermud.storage.graphdb.NpcData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.NpcData;
 import com.google.common.collect.Sets;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.http.HttpEntity;
@@ -94,27 +95,29 @@ public class LoadNpcCommand extends Command {
             }
             httpGet.reset();
 
-            AetherMudStorage storage = gameManager.getGraphStorage();
-            NpcData npcData = storage.newNpcData();
-            try {
-                PropertyUtils.copyProperties(npcData, npc);
-                PropertyUtils.copyProperties(npcData.createStats(), npc.getStats());
-                PropertyUtils.copyProperties(npcData.createLootData(), npc.getLoot());
-            } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException ex) {
-                throw new IllegalStateException("Could not copy properties for stats", ex);
-            }
-            npc.getSpawnRules().forEach(new Consumer<SpawnRule>() {
-                @Override
-                public void accept(SpawnRule spawnRule) {
-                    try {
-                        PropertyUtils.copyProperties(npcData.createSpawnRuleData(), spawnRule);
-                    } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException ex) {
-                        throw new IllegalStateException("Could not copy properties for stats", ex);
-                    }
+            try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                AetherMudStorage storage = tx.getStorage();
+                NpcData npcData = storage.newNpcData();
+                try {
+                    PropertyUtils.copyProperties(npcData, npc);
+                    PropertyUtils.copyProperties(npcData.createStats(), npc.getStats());
+                    PropertyUtils.copyProperties(npcData.createLootData(), npc.getLoot());
+                } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException ex) {
+                    throw new IllegalStateException("Could not copy properties for stats", ex);
                 }
-            });
-            storage.persist();
-            write("NPC Saved. - " + npc.getName() + "\r\n");
+                npc.getSpawnRules().forEach(new Consumer<SpawnRule>() {
+                    @Override
+                    public void accept(SpawnRule spawnRule) {
+                        try {
+                            PropertyUtils.copyProperties(npcData.createSpawnRuleData(), spawnRule);
+                        } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException ex) {
+                            throw new IllegalStateException("Could not copy properties for stats", ex);
+                        }
+                    }
+                });
+                tx.success();
+                write("NPC Saved. - " + npc.getName() + "\r\n");
+            }
 
         });
     }
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/admin/RestartCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/admin/RestartCommand.java
index c71438827c73bec6c159c726bcd68f964533f6c8..c8a551bc2e8b00c4b31efa819606e66c6aecf73b 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/admin/RestartCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/admin/RestartCommand.java
@@ -42,7 +42,6 @@ public class RestartCommand extends Command {
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         execCommandThreadSafe(ctx, e, BounceIrcBotCommand.class, () -> {
-            gameManager.getGraphStorage().persist();
             playerManager.getAllPlayersMap().values().stream()
                     .filter(player -> player.getChannel().isConnected())
                     .forEach(player -> gameManager.getChannelUtils().write(player.getPlayerId(),
@@ -55,8 +54,6 @@ public class RestartCommand extends Command {
                     "88         \"8b,   ,aa 88b,   ,a8\" \"8a,   ,a8\" \"8a,   ,a8\"  88,    \n" +
                     "88          `\"Ybbd8\"' 8Y\"Ybbd8\"'   `\"YbbdP\"'   `\"YbbdP\"'   \"Y888  \n" +
                     "                                                                  "));
-            gameManager.getGraphStorage().stopAsync();
-            gameManager.getGraphStorage().awaitTerminated();
             System.exit(0);
         });
     }
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/admin/SpawnCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/admin/SpawnCommand.java
index ec11185b02ab432ab943f0bedb839352103f9b66..9bb53b8a16c179a6656a83e2b5f4b5cc35a11b38 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/admin/SpawnCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/admin/SpawnCommand.java
@@ -25,6 +25,7 @@ import com.syncleus.aethermud.player.PlayerRole;
 import com.syncleus.aethermud.server.communication.Color;
 import com.google.common.base.Joiner;
 import com.google.common.collect.Sets;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.channel.MessageEvent;
 
@@ -46,28 +47,30 @@ public class SpawnCommand  extends Command {
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         execCommand(ctx, e, () -> {
-            List<? extends NpcSpawn> npcsFromFile = gameManager.getGraphStorage().getAllNpcs(gameManager);
-            if (originalMessageParts.size() == 1) {
-                write(getHeader());
-                for (NpcSpawn npcSpawn : npcsFromFile) {
-                    write(npcSpawn.getName() + "\r\n");
-                }
-            } else {
-                originalMessageParts.remove(0);
-                String targetNpc = Joiner.on(" ").join(originalMessageParts);
-                for (NpcSpawn npcSpawn : npcsFromFile) {
-                    if (targetNpc.equals(npcSpawn.getName())) {
-                        Loot loot = new Loot(0,0, Lists.newArrayList());
-                        NpcSpawn modifiedNpcSpawn = new NpcBuilder(npcSpawn).setSpawnRules(null).setLoot(loot).createNpc();
-                        modifiedNpcSpawn.getStats().setExperience(0);
-                        modifiedNpcSpawn.setCurrentRoom(currentRoom);
-                        gameManager.getEntityManager().addEntity(modifiedNpcSpawn);
-                        currentRoom.addPresentNpc(modifiedNpcSpawn.getEntityId());
-                        writeToRoom("A " + modifiedNpcSpawn.getColorName() + " appears." + "\r\n");
-                        return;
+            try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                List<? extends NpcSpawn> npcsFromFile = tx.getStorage().getAllNpcs(gameManager);
+                if (originalMessageParts.size() == 1) {
+                    write(getHeader());
+                    for (NpcSpawn npcSpawn : npcsFromFile) {
+                        write(npcSpawn.getName() + "\r\n");
+                    }
+                } else {
+                    originalMessageParts.remove(0);
+                    String targetNpc = Joiner.on(" ").join(originalMessageParts);
+                    for (NpcSpawn npcSpawn : npcsFromFile) {
+                        if (targetNpc.equals(npcSpawn.getName())) {
+                            Loot loot = new Loot(0, 0, Lists.newArrayList());
+                            NpcSpawn modifiedNpcSpawn = new NpcBuilder(npcSpawn).setSpawnRules(null).setLoot(loot).createNpc();
+                            modifiedNpcSpawn.getStats().setExperience(0);
+                            modifiedNpcSpawn.setCurrentRoom(currentRoom);
+                            gameManager.getEntityManager().addEntity(modifiedNpcSpawn);
+                            currentRoom.addPresentNpc(modifiedNpcSpawn.getEntityId());
+                            writeToRoom("A " + modifiedNpcSpawn.getColorName() + " appears." + "\r\n");
+                            return;
+                        }
                     }
+                    write("No npc found with name: " + targetNpc + "\r\n");
                 }
-                write("No npc found with name: " + targetNpc + "\r\n");
             }
         });
     }
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/admin/TeleportCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/admin/TeleportCommand.java
index 029a7a25f4cedc1a139aa9abae6262ac3cbeb0df..2a4bfdaf7918806e717fdd992b56bb6919b461c7 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/admin/TeleportCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/admin/TeleportCommand.java
@@ -18,10 +18,11 @@ package com.syncleus.aethermud.command.commands.admin;
 
 import com.syncleus.aethermud.command.commands.Command;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.EffectPojo;
 import com.syncleus.aethermud.player.*;
 import com.syncleus.aethermud.server.communication.Color;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.EffectData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.world.model.Room;
 import com.google.common.collect.Sets;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -56,16 +57,18 @@ public class TeleportCommand extends Command {
                 write("You are dead and can not move.");
                 return;
             }
-            Optional<PlayerData> playerMetadataOptional = playerManager.getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            for (EffectPojo effect : playerData.getEffects()) {
-                if (effect.isFrozenMovement()) {
-                    write("You are frozen and can not move.");
+            try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                Optional<PlayerData> playerMetadataOptional = tx.getStorage().getPlayerMetadata(playerId);
+                if (!playerMetadataOptional.isPresent()) {
                     return;
                 }
+                PlayerData playerData = playerMetadataOptional.get();
+                for (EffectData effect : playerData.getEffects()) {
+                    if (effect.isFrozenMovement()) {
+                        write("You are frozen and can not move.");
+                        return;
+                    }
+                }
             }
             String desiredId = originalMessageParts.get(1);
             Iterator<Map.Entry<String, Player>> players = playerManager.getPlayers();
diff --git a/src/main/java/com/syncleus/aethermud/configuration/ConfigureNpc.java b/src/main/java/com/syncleus/aethermud/configuration/ConfigureNpc.java
index f3e94f99e6f14b6dfbfda6d2812dba3f63f9c7d3..9aae9c6cb020b16e55698f5a64e2a285d9195b6b 100644
--- a/src/main/java/com/syncleus/aethermud/configuration/ConfigureNpc.java
+++ b/src/main/java/com/syncleus/aethermud/configuration/ConfigureNpc.java
@@ -26,6 +26,7 @@ import com.syncleus.aethermud.npc.NpcSpawn;
 import com.syncleus.aethermud.spawner.ItemSpawner;
 import com.syncleus.aethermud.spawner.NpcSpawner;
 import com.syncleus.aethermud.spawner.SpawnRule;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
 
 import java.io.IOException;
 import java.util.List;
@@ -35,13 +36,15 @@ public class ConfigureNpc {
 
     public static void configureAllNpcs(GameManager gameManager) throws IOException {
         EntityManager entityManager = gameManager.getEntityManager();
-        List<? extends NpcSpawn> npcsFromFile = gameManager.getGraphStorage().getAllNpcs(gameManager);
-        for (NpcSpawn npcSpawn : npcsFromFile) {
-            Main.startUpMessage("Adding npc spawn: " + npcSpawn.getName());
-            entityManager.addEntity(npcSpawn);
-            Set<SpawnRule> spawnRules = npcSpawn.getSpawnRules();
-            for (SpawnRule spawnRule : spawnRules) {
-                entityManager.addEntity(new NpcSpawner(npcSpawn, gameManager, spawnRule));
+        try( GraphStorageFactory.AetherMudTx tx = gameManager.getGraphStorageFactory().beginTransaction() ) {
+            List<? extends NpcSpawn> npcsFromFile = tx.getStorage().getAllNpcs(gameManager);
+            for (NpcSpawn npcSpawn : npcsFromFile) {
+                Main.startUpMessage("Adding npc spawn: " + npcSpawn.getName());
+                entityManager.addEntity(npcSpawn);
+                Set<SpawnRule> spawnRules = npcSpawn.getSpawnRules();
+                for (SpawnRule spawnRule : spawnRules) {
+                    entityManager.addEntity(new NpcSpawner(npcSpawn, gameManager, spawnRule));
+                }
             }
         }
     }
diff --git a/src/main/java/com/syncleus/aethermud/core/GameManager.java b/src/main/java/com/syncleus/aethermud/core/GameManager.java
index 35ed0e46e7d7cf11713cb95a14f284d72a83f857..c4856db6deb9fad8ffb362fb8116519e3975788a 100644
--- a/src/main/java/com/syncleus/aethermud/core/GameManager.java
+++ b/src/main/java/com/syncleus/aethermud/core/GameManager.java
@@ -42,6 +42,7 @@ import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.aethermud.stats.StatsBuilder;
 import com.syncleus.aethermud.stats.modifier.StatsModifierFactory;
 import com.syncleus.aethermud.storage.*;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
 import com.syncleus.aethermud.world.FloorManager;
 import com.syncleus.aethermud.world.MapsManager;
 import com.syncleus.aethermud.world.RoomManager;
@@ -56,11 +57,9 @@ import com.google.common.collect.Interners;
 import com.google.common.collect.Maps;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
-import com.syncleus.ferma.WrappedFramedGraph;
 import org.apache.commons.lang3.text.WordUtils;
 import org.apache.http.client.HttpClient;
 import org.apache.log4j.Logger;
-import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.nocrala.tools.texttablefmt.BorderStyle;
 import org.nocrala.tools.texttablefmt.ShownBorders;
 import org.nocrala.tools.texttablefmt.Table;
@@ -77,7 +76,7 @@ public class GameManager {
 
     public static final Integer LOBBY_ID = 1;
     private static final Logger log = Logger.getLogger(GameManager.class);
-    public static String LOGO = "AetherMUD.";
+    public static String LOGO = "AetherMUD";
     private final RoomManager roomManager;
     private final PlayerManager playerManager;
     private final ChannelCommunicationUtils channelUtils;
@@ -107,7 +106,7 @@ public class GameManager {
     private final HttpClient httpclient;
     private final Gson gson;
     private final FilebasedJsonStorage filebasedJsonStorage;
-    private final AetherMudStorage graphStorage;
+    private final GraphStorageFactory graphStorageFactory;
 
     public MerchantStorage getMerchantStorage() {
         return merchantStorage;
@@ -116,8 +115,8 @@ public class GameManager {
     private final MerchantStorage merchantStorage;
 
 
-    public GameManager(AetherMudStorage graphStorage, WrappedFramedGraph<Graph> framedGraph, AetherMudConfiguration aetherMudConfiguration, RoomManager roomManager, PlayerManager playerManager, EntityManager entityManager, MapsManager mapsManager, ChannelCommunicationUtils channelUtils, HttpClient httpClient) {
-        this.graphStorage = graphStorage;
+    public GameManager(GraphStorageFactory graphStorageFactory, AetherMudConfiguration aetherMudConfiguration, RoomManager roomManager, PlayerManager playerManager, EntityManager entityManager, MapsManager mapsManager, ChannelCommunicationUtils channelUtils, HttpClient httpClient) {
+        this.graphStorageFactory = graphStorageFactory;
         this.roomManager = roomManager;
         this.playerManager = playerManager;
         this.entityManager = entityManager;
@@ -151,8 +150,8 @@ public class GameManager {
         this.httpclient = httpClient;
     }
 
-    public AetherMudStorage getGraphStorage() {
-        return graphStorage;
+    public GraphStorageFactory getGraphStorageFactory() {
+        return graphStorageFactory;
     }
 
     public Gson getGson() {
@@ -612,9 +611,9 @@ public class GameManager {
 
         sb = new StringBuilder();
         t.addCell("Mele");
-        t.addCell(getFormattedNumber(stats.getMeleSkill()));
-        if (diff.getMeleSkill() > 0)
-            sb.append("(").append(Color.GREEN).append("+").append(getFormattedNumber(diff.getMeleSkill())).append(RESET).append(")");
+        t.addCell(getFormattedNumber(stats.getMeleeSkill()));
+        if (diff.getMeleeSkill() > 0)
+            sb.append("(").append(Color.GREEN).append("+").append(getFormattedNumber(diff.getMeleeSkill())).append(RESET).append(")");
         t.addCell(sb.toString());
 
         sb = new StringBuilder();
@@ -663,7 +662,7 @@ public class GameManager {
         return NumberFormat.getNumberInstance(Locale.US).format(longval);
     }
 
-    public String renderEffectsString(List<EffectPojo> effects) {
+    public String renderEffectsString(List<Effect> effects) {
         Table t = new Table(2, BorderStyle.CLASSIC_COMPATIBLE,
                 ShownBorders.NONE);
 
@@ -671,7 +670,7 @@ public class GameManager {
         // t.setColumnWidth(1, 10, 13);
 
         int i = 1;
-        for (EffectPojo effect : effects) {
+        for (Effect effect : effects) {
             int percent = 100 - (int) ((effect.getEffectApplications() * 100.0f) / effect.getMaxEffectApplications());
             t.addCell(drawProgressBar(percent));
             t.addCell(effect.getEffectName());
diff --git a/src/main/java/com/syncleus/aethermud/core/NewUserRegistrationManager.java b/src/main/java/com/syncleus/aethermud/core/NewUserRegistrationManager.java
index 4bcd2ceb5b0ffecdbbf434c6172732e451794227..ab63162c1be5319221607b7fdbef79ab854696fa 100644
--- a/src/main/java/com/syncleus/aethermud/core/NewUserRegistrationManager.java
+++ b/src/main/java/com/syncleus/aethermud/core/NewUserRegistrationManager.java
@@ -22,7 +22,8 @@ import com.syncleus.aethermud.server.model.AetherMudSession;
 import com.syncleus.aethermud.stats.DefaultStats;
 import com.google.common.base.Optional;
 import com.google.common.collect.Sets;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.log4j.Logger;
 import org.jboss.netty.channel.MessageEvent;
@@ -32,6 +33,7 @@ import java.nio.charset.Charset;
 import java.nio.charset.CharsetEncoder;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 
 public class NewUserRegistrationManager {
 
@@ -69,18 +71,21 @@ public class NewUserRegistrationManager {
     private boolean setDesiredUsername(AetherMudSession session, MessageEvent e) {
         String name = (String) e.getMessage();
         String username = name.replaceAll("[^a-zA-Z0-9]", "");
-        java.util.Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(Main.createPlayerId(username));
-        if (!isValidUsername(username)) {
-            e.getChannel().write("Username is in invalid.\r\n");
-            return false;
-        }
-        if (playerMetadataOptional.isPresent()) {
-            e.getChannel().write("Username is in use.\r\n");
-            newUserRegistrationFlow(session, e);
-            return false;
+        try( GraphStorageFactory.AetherMudTx tx = gameManager.getGraphStorageFactory().beginTransaction() ) {
+            java.util.Optional<PlayerData> playerMetadataOptional = tx.getStorage().getPlayerMetadata(Main.createPlayerId(username));
+
+            if (!isValidUsername(username)) {
+                e.getChannel().write("Username is in invalid.\r\n");
+                return false;
+            }
+            if (playerMetadataOptional.isPresent()) {
+                e.getChannel().write("Username is in use.\r\n");
+                newUserRegistrationFlow(session, e);
+                return false;
+            }
+            session.setUsername(java.util.Optional.of(username));
+            return true;
         }
-        session.setUsername(java.util.Optional.of(username));
-        return true;
     }
 
     private void promptForDesirePassword(AetherMudSession session, MessageEvent e) {
@@ -97,36 +102,40 @@ public class NewUserRegistrationManager {
         }
         session.setPassword(Optional.of(password));
 
-        PlayerData playerData = gameManager.getPlayerManager().newPlayerData();
-        playerData.setNpcKillLog(new HashMap<>());
-        playerData.setEffects(new ArrayList<>());
-        playerData.setGold(0);
-        playerData.setGoldInBank(0);
-        playerData.setInventory(new ArrayList<>());
-        playerData.setLearnedSpells(new ArrayList<>());
-        playerData.setLockerInventory(new ArrayList<>());
-        playerData.setIsMarkedForDelete(false);
-        playerData.setPlayerName(session.getUsername().get());
-        playerData.setPassword(session.getPassword().get());
-        playerData.setPlayerClass(PlayerClass.BASIC);
-        playerData.setPlayerEquipment(new ArrayList<>());
-        playerData.setPlayerId(Main.createPlayerId(session.getUsername().get()));
-        // TODO : remove this, not all players should be admins
-        playerData.setPlayerRoles(Sets.newHashSet(PlayerRole.MORTAL, PlayerRole.ADMIN, PlayerRole.GOD, PlayerRole.TELEPORTER));
-        playerData.setPlayerSettings(new HashMap<>());
-        try {
-            PropertyUtils.copyProperties(playerData.createStats(), DefaultStats.DEFAULT_PLAYER.createStats());
-        } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
-            throw new IllegalStateException("Could not copy properties for stats", e);
+        String playerId = Main.createPlayerId(session.getUsername().get());
+        String playerName = session.getUsername().get();
+        try( GraphStorageFactory.AetherMudTx tx = gameManager.getGraphStorageFactory().beginTransaction() ) {
+            PlayerData playerData = tx.getStorage().newPlayerData();
+            playerData.setNpcKillLog(new HashMap<>());
+            playerData.setEffects(new HashSet<>());
+            playerData.setGold(0);
+            playerData.setGoldInBank(0);
+            playerData.setInventory(new ArrayList<>());
+            playerData.setLearnedSpells(new ArrayList<>());
+            playerData.setLockerInventory(new ArrayList<>());
+            playerData.setIsMarkedForDelete(false);
+            playerData.setPlayerName(playerName);
+            playerData.setPassword(session.getPassword().get());
+            playerData.setPlayerClass(PlayerClass.BASIC);
+            playerData.setPlayerEquipment(new ArrayList<>());
+            playerData.setPlayerId(playerId);
+            // TODO : remove this, not all players should be admins
+            playerData.setPlayerRoles(Sets.newHashSet(PlayerRole.MORTAL, PlayerRole.ADMIN, PlayerRole.GOD, PlayerRole.TELEPORTER));
+            playerData.setPlayerSettings(new HashMap<>());
+            try {
+                PropertyUtils.copyProperties(playerData.createStats(), DefaultStats.DEFAULT_PLAYER.createStats());
+            } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
+                throw new IllegalStateException("Could not copy properties for stats", e);
+            }
+            playerData.createCoolDown(CoolDownType.NEWBIE);
+            tx.success();
         }
-        playerData.createCoolDown(CoolDownType.NEWBIE);
-        gameManager.getPlayerManager().persist();
 
         messageEvent.getChannel().write("User created.\r\n");
-        log.info("User " + playerData.getPlayerName() + " created.");
+        log.info("User " + playerName + " created.");
         session.setState(AetherMudSession.State.newUserRegCompleted);
         try {
-            PlayerManagementManager.registerPlayer(playerData.getPlayerName(), playerData.getPlayerId(), gameManager);
+            PlayerManagementManager.registerPlayer(playerName, playerId, gameManager);
         } catch (Exception e) {
             log.error("Problem registering new player in the MBean server!");
         }
diff --git a/src/main/java/com/syncleus/aethermud/entity/EntityManager.java b/src/main/java/com/syncleus/aethermud/entity/EntityManager.java
index fbe4d987bd3374cc5a4f3d7af0c231f6ea050e5d..74f18c31b3f9a666f2f7e32d844f5ce2994f173e 100644
--- a/src/main/java/com/syncleus/aethermud/entity/EntityManager.java
+++ b/src/main/java/com/syncleus/aethermud/entity/EntityManager.java
@@ -15,7 +15,9 @@
  */
 package com.syncleus.aethermud.entity;
 
+import com.google.common.base.Function;
 import com.syncleus.aethermud.Main;
+import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.core.SentryManager;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.ItemBuilder;
@@ -23,7 +25,10 @@ import com.syncleus.aethermud.npc.NpcSpawn;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.player.PlayerManager;
 import com.syncleus.aethermud.storage.AetherMudStorage;
-import com.syncleus.aethermud.storage.graphdb.ItemData;
+import com.syncleus.aethermud.storage.graphdb.GraphDbAetherMudStorage;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.world.RoomManager;
 import com.syncleus.aethermud.world.model.Room;
 import org.apache.log4j.Logger;
@@ -34,6 +39,7 @@ import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.function.Consumer;
 
 import static com.codahale.metrics.MetricRegistry.name;
 
@@ -41,15 +47,15 @@ public class EntityManager {
 
     private static final Logger log = Logger.getLogger(EntityManager.class);
 
-    private final AetherMudStorage aetherMudStorage;
+    private final GraphStorageFactory graphStorageFactory;
     private final RoomManager roomManager;
     private final PlayerManager playerManager;
     private final Map<String, NpcSpawn> npcs = new ConcurrentHashMap<>();
     private final Map<String, AetherMudEntity> entities = new ConcurrentHashMap<>();
     private final ExecutorService mainTickExecutorService = Executors.newFixedThreadPool(50);
 
-    public EntityManager(AetherMudStorage aetherMudStorage, RoomManager roomManager, PlayerManager playerManager) {
-        this.aetherMudStorage = aetherMudStorage;
+    public EntityManager(GraphStorageFactory graphStorageFactory, RoomManager roomManager, PlayerManager playerManager) {
+        this.graphStorageFactory = graphStorageFactory;
         this.roomManager = roomManager;
         this.playerManager = playerManager;
         ExecutorService tickOrchestratorService = Executors.newFixedThreadPool(5);
@@ -79,20 +85,22 @@ public class EntityManager {
     }
 
     public ItemData saveItem(ItemPojo item) {
-        return aetherMudStorage.saveItem(item);
+        return this.transact(storage -> storage.saveItem(item));
     }
 
     public void removeItem(ItemPojo item) {
-        aetherMudStorage.removeItem(item.getItemId());
+        this.consume(storage -> storage.removeItem(item.getItemId()));
     }
 
     public void removeItem(String itemId) {
-        aetherMudStorage.removeItem(itemId);
+        this.consume(storage -> storage.removeItem(itemId));
     }
 
     public Optional<ItemPojo> getItemEntity(String itemId) {
-        Optional<ItemData> item = aetherMudStorage.getItemEntity(itemId);
-        return item.map(itemName -> new ItemBuilder().from(itemName).create());
+        return this.transactRead(storage -> {
+            Optional<ItemData> item = storage.getItemEntity(itemId);
+            return item.map(itemName -> new ItemBuilder().from(itemName).create());
+        });
     }
 
     public void deleteNpcEntity(String npcId) {
@@ -105,6 +113,27 @@ public class EntityManager {
 
     public static final int SLEEP_MILLIS = 500;
 
+    private <T> T transact(Function<GraphDbAetherMudStorage, T> func) {
+        try( GraphStorageFactory.AetherMudTx tx = this.graphStorageFactory.beginTransaction() ) {
+            T retVal = func.apply(tx.getStorage());
+            tx.success();
+            return retVal;
+        }
+    }
+
+    private void consume(Consumer<GraphDbAetherMudStorage> func) {
+        try( GraphStorageFactory.AetherMudTx tx = this.graphStorageFactory.beginTransaction() ) {
+            func.accept(tx.getStorage());
+            tx.success();
+        }
+    }
+
+    private <T> T transactRead(Function<GraphDbAetherMudStorage, T> func) {
+        try( GraphStorageFactory.AetherMudTx tx = this.graphStorageFactory.beginTransaction() ) {
+            return func.apply(tx.getStorage());
+        }
+    }
+
     class PlayerTicker implements Runnable {
         private final com.codahale.metrics.Timer ticktime = Main.metrics.timer(name(EntityManager.class, "player_tick_time"));
 
diff --git a/src/main/java/com/syncleus/aethermud/items/Effect.java b/src/main/java/com/syncleus/aethermud/items/Effect.java
index 9cc59c5456b2fac293e9c79240c3bd64fe9d9bc2..6ab456a7a261e05216dace903671f31e019eed50 100644
--- a/src/main/java/com/syncleus/aethermud/items/Effect.java
+++ b/src/main/java/com/syncleus/aethermud/items/Effect.java
@@ -15,44 +15,119 @@
  */
 package com.syncleus.aethermud.items;
 
+
 import com.syncleus.aethermud.stats.Stats;
 
 import java.util.List;
 
-public interface Effect {
-    String getEffectName();
-
-    String getEffectDescription();
-
-    List<String> getEffectApplyMessages();
-
-    Stats getApplyStatsOnTick();
-
-    int getMaxEffectApplications();
-
-    boolean isFrozenMovement();
-
-    int getEffectApplications();
-
-    void setEffectApplications(int effectApplications);
-
-    Stats getDurationStats();
-
-    String getPlayerId();
-
-    void setPlayerId(String playerId);
-
-    void setEffectName(String effectName);
-
-    void setEffectDescription(String effectDescription);
-
-    void setEffectApplyMessages(List<String> effectApplyMessages);
-
-    void setApplyStatsOnTick(Stats applyStatsOnTick);
-
-    void setDurationStats(Stats durationStats);
-
-    void setMaxEffectApplications(int maxEffectApplications);
-
-    void setFrozenMovement(boolean frozenMovement);
+public class Effect {
+
+    private String effectName;
+    private String effectDescription;
+    private List<String> effectApplyMessages;
+    private Stats applyStatsOnTick;
+    private Stats durationStats;
+    private int maxEffectApplications;
+    private boolean frozenMovement;
+    private int effectApplications;
+    private String playerId;
+
+    public Effect() {
+
+    }
+
+    public Effect(String effectName, String effectDescription, List<String> effectApplyMessages, Stats applyStatsOnTick, Stats durationStats, int maxEffectApplications, boolean frozenMovement) {
+        this.effectName = effectName;
+        this.effectDescription = effectDescription;
+        this.effectApplyMessages = effectApplyMessages;
+        this.applyStatsOnTick = applyStatsOnTick;
+        this.durationStats = durationStats;
+        this.maxEffectApplications = maxEffectApplications;
+        this.frozenMovement = frozenMovement;
+        this.effectApplications = 0;
+    }
+
+    public Effect(Effect effect) {
+        this.effectName = effect.effectName;
+        this.effectDescription = effect.effectDescription;
+        this.effectApplyMessages = effect.effectApplyMessages;
+        this.applyStatsOnTick = effect.applyStatsOnTick;
+        this.durationStats = effect.durationStats;
+        this.maxEffectApplications = effect.maxEffectApplications;
+        this.frozenMovement = effect.frozenMovement;
+        this.effectApplications = effect.effectApplications;
+    }
+
+
+    public String getEffectName() {
+        return effectName;
+    }
+
+    public String getEffectDescription() {
+        return effectDescription;
+    }
+
+    public List<String> getEffectApplyMessages() {
+        return effectApplyMessages;
+    }
+
+    public Stats getApplyStatsOnTick() {
+        return applyStatsOnTick;
+    }
+
+    public int getMaxEffectApplications() {
+        return maxEffectApplications;
+    }
+
+    public boolean isFrozenMovement() {
+        return frozenMovement;
+    }
+
+    public int getEffectApplications() {
+        return effectApplications;
+    }
+
+    public void setEffectApplications(int effectApplications) {
+        this.effectApplications = effectApplications;
+    }
+
+    public Stats getDurationStats() {
+        return durationStats;
+    }
+
+    public String getPlayerId() {
+        return playerId;
+    }
+
+    public void setPlayerId(String playerId) {
+        this.playerId = playerId;
+    }
+
+    public void setEffectName(String effectName) {
+        this.effectName = effectName;
+    }
+
+    public void setEffectDescription(String effectDescription) {
+        this.effectDescription = effectDescription;
+    }
+
+    public void setEffectApplyMessages(List<String> effectApplyMessages) {
+        this.effectApplyMessages = effectApplyMessages;
+    }
+
+    public void setApplyStatsOnTick(Stats applyStatsOnTick) {
+        this.applyStatsOnTick = applyStatsOnTick;
+    }
+
+    public void setDurationStats(Stats durationStats) {
+        this.durationStats = durationStats;
+    }
+
+    public void setMaxEffectApplications(int maxEffectApplications) {
+        this.maxEffectApplications = maxEffectApplications;
+    }
+
+    public void setFrozenMovement(boolean frozenMovement) {
+        this.frozenMovement = frozenMovement;
+    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/EffectBuilder.java b/src/main/java/com/syncleus/aethermud/items/EffectBuilder.java
index ab0ed3e378d796be33db73951049e996fb3b5576..561e9fa259331d786ae9341cff0684cc0d6288a5 100644
--- a/src/main/java/com/syncleus/aethermud/items/EffectBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/items/EffectBuilder.java
@@ -63,7 +63,7 @@ public class EffectBuilder {
         return this;
     }
 
-    public EffectPojo createEffect() {
-        return new EffectPojo(effectName, effectDescription, effectApplyMessages, applyStatsOnTick, durationStats, lifeSpanTicks, frozenMovement);
+    public Effect createEffect() {
+        return new Effect(effectName, effectDescription, effectApplyMessages, applyStatsOnTick, durationStats, lifeSpanTicks, frozenMovement);
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/EffectPojo.java b/src/main/java/com/syncleus/aethermud/items/EffectPojo.java
deleted file mode 100644
index 4c4b3fb772e489e6670e97d2b1f8d237bd8d89c7..0000000000000000000000000000000000000000
--- a/src/main/java/com/syncleus/aethermud/items/EffectPojo.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * Copyright 2017 Syncleus, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.syncleus.aethermud.items;
-
-
-import com.syncleus.aethermud.stats.Stats;
-
-import java.util.List;
-
-public class EffectPojo implements Effect {
-
-    private String effectName;
-    private String effectDescription;
-    private List<String> effectApplyMessages;
-    private Stats applyStatsOnTick;
-    private Stats durationStats;
-    private int maxEffectApplications;
-    private boolean frozenMovement;
-    private int effectApplications;
-    private String playerId;
-
-    public EffectPojo(String effectName, String effectDescription, List<String> effectApplyMessages, Stats applyStatsOnTick, Stats durationStats, int maxEffectApplications, boolean frozenMovement) {
-        this.effectName = effectName;
-        this.effectDescription = effectDescription;
-        this.effectApplyMessages = effectApplyMessages;
-        this.applyStatsOnTick = applyStatsOnTick;
-        this.durationStats = durationStats;
-        this.maxEffectApplications = maxEffectApplications;
-        this.frozenMovement = frozenMovement;
-        this.effectApplications = 0;
-    }
-
-    public EffectPojo(EffectPojo effect) {
-        this.effectName = effect.effectName;
-        this.effectDescription = effect.effectDescription;
-        this.effectApplyMessages = effect.effectApplyMessages;
-        this.applyStatsOnTick = effect.applyStatsOnTick;
-        this.durationStats = effect.durationStats;
-        this.maxEffectApplications = effect.maxEffectApplications;
-        this.frozenMovement = effect.frozenMovement;
-        this.effectApplications = effect.effectApplications;
-    }
-
-
-    @Override
-    public String getEffectName() {
-        return effectName;
-    }
-
-    @Override
-    public String getEffectDescription() {
-        return effectDescription;
-    }
-
-    @Override
-    public List<String> getEffectApplyMessages() {
-        return effectApplyMessages;
-    }
-
-    @Override
-    public Stats getApplyStatsOnTick() {
-        return applyStatsOnTick;
-    }
-
-    @Override
-    public int getMaxEffectApplications() {
-        return maxEffectApplications;
-    }
-
-    @Override
-    public boolean isFrozenMovement() {
-        return frozenMovement;
-    }
-
-    @Override
-    public int getEffectApplications() {
-        return effectApplications;
-    }
-
-    @Override
-    public void setEffectApplications(int effectApplications) {
-        this.effectApplications = effectApplications;
-    }
-
-    @Override
-    public Stats getDurationStats() {
-        return durationStats;
-    }
-
-    @Override
-    public String getPlayerId() {
-        return playerId;
-    }
-
-    @Override
-    public void setPlayerId(String playerId) {
-        this.playerId = playerId;
-    }
-
-    @Override
-    public void setEffectName(String effectName) {
-        this.effectName = effectName;
-    }
-
-    @Override
-    public void setEffectDescription(String effectDescription) {
-        this.effectDescription = effectDescription;
-    }
-
-    @Override
-    public void setEffectApplyMessages(List<String> effectApplyMessages) {
-        this.effectApplyMessages = effectApplyMessages;
-    }
-
-    @Override
-    public void setApplyStatsOnTick(Stats applyStatsOnTick) {
-        this.applyStatsOnTick = applyStatsOnTick;
-    }
-
-    @Override
-    public void setDurationStats(Stats durationStats) {
-        this.durationStats = durationStats;
-    }
-
-    @Override
-    public void setMaxEffectApplications(int maxEffectApplications) {
-        this.maxEffectApplications = maxEffectApplications;
-    }
-
-    @Override
-    public void setFrozenMovement(boolean frozenMovement) {
-        this.frozenMovement = frozenMovement;
-    }
-}
diff --git a/src/main/java/com/syncleus/aethermud/items/EffectsManager.java b/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
index 799b3f863f6c027f5962ab1958a5961c7e180357..0dae32f788e02af9b3e09088de7530158eb1e989 100644
--- a/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
+++ b/src/main/java/com/syncleus/aethermud/items/EffectsManager.java
@@ -22,7 +22,7 @@ import com.syncleus.aethermud.npc.NpcStatsChangeBuilder;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.aethermud.stats.StatsPojo;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.server.communication.Color;
 import com.syncleus.aethermud.stats.StatsBuilder;
 import com.syncleus.aethermud.stats.StatsHelper;
@@ -43,10 +43,10 @@ public class EffectsManager {
         this.gameManager = gameManager;
     }
 
-    public void applyEffectsToNpcs(Player player, Set<NpcSpawn> npcSpawns, Set<EffectPojo> effects) {
+    public void applyEffectsToNpcs(Player player, Set<NpcSpawn> npcSpawns, Set<Effect> effects) {
         effects.forEach(effect ->
                 npcSpawns.forEach(npc -> {
-                    EffectPojo nEffect = new EffectPojo(effect);
+                    Effect nEffect = new Effect(effect);
                     nEffect.setPlayerId(player.getPlayerId());
                     if (effect.getDurationStats().getCurrentHealth() < 0) {
                         log.error("ERROR! Someone added an effect with a health modifier which won't work for various reasons.");
@@ -57,9 +57,9 @@ public class EffectsManager {
                 }));
     }
 
-    public void applyEffectsToPlayer(Player destinationPlayer, Player player, Set<EffectPojo> effects) {
-        for (EffectPojo effect : effects) {
-            EffectPojo nEffect = new EffectPojo(effect);
+    public void applyEffectsToPlayer(Player destinationPlayer, Player player, Set<Effect> effects) {
+        for (Effect effect : effects) {
+            Effect nEffect = new Effect(effect);
             nEffect.setPlayerId(player.getPlayerId());
             if (effect.getDurationStats().getCurrentHealth() < 0) {
                 log.error("ERROR! Someone added an effect with a health modifier which won't work for various reasons.");
@@ -76,17 +76,17 @@ public class EffectsManager {
         }
     }
 
-    public void application(EffectPojo effect, Player player) {
+    public void application(Effect effect, Player player) {
         // if there are effecst that modify player health, deal with it here, you can't rely on combine stats.
         Stats applyStatsOnTick = effect.getApplyStatsOnTick();
-        if (effect.getApplyStatsOnTick() != null) {
-            if (effect.getApplyStatsOnTick().getCurrentHealth() != 0) {
-                gameManager.getPlayerManager().getPlayer(player.getPlayerId()).updatePlayerHealth(effect.getApplyStatsOnTick().getCurrentHealth(), null);
+        if (applyStatsOnTick != null) {
+            if (applyStatsOnTick.getCurrentHealth() != 0) {
+                gameManager.getPlayerManager().getPlayer(player.getPlayerId()).updatePlayerHealth(applyStatsOnTick.getCurrentHealth(), null);
                 for (String message : effect.getEffectApplyMessages()) {
-                    if (effect.getApplyStatsOnTick().getCurrentHealth() > 0) {
-                        gameManager.getChannelUtils().write(player.getPlayerId(), Color.BOLD_ON + Color.GREEN + "[effect] " + Color.RESET + message + " +" + Color.GREEN + NumberFormat.getNumberInstance(Locale.US).format(effect.getApplyStatsOnTick().getCurrentHealth()) + Color.RESET + "\r\n", true);
+                    if (applyStatsOnTick.getCurrentHealth() > 0) {
+                        gameManager.getChannelUtils().write(player.getPlayerId(), Color.BOLD_ON + Color.GREEN + "[effect] " + Color.RESET + message + " +" + Color.GREEN + NumberFormat.getNumberInstance(Locale.US).format(applyStatsOnTick.getCurrentHealth()) + Color.RESET + "\r\n", true);
                     } else {
-                        gameManager.getChannelUtils().write(player.getPlayerId(), Color.BOLD_ON + Color.GREEN + "[effect] " + Color.RESET + message + " -" + Color.RED + NumberFormat.getNumberInstance(Locale.US).format(effect.getApplyStatsOnTick().getCurrentHealth()) + Color.RESET + "\r\n", true);
+                        gameManager.getChannelUtils().write(player.getPlayerId(), Color.BOLD_ON + Color.GREEN + "[effect] " + Color.RESET + message + " -" + Color.RED + NumberFormat.getNumberInstance(Locale.US).format(applyStatsOnTick.getCurrentHealth()) + Color.RESET + "\r\n", true);
                     }
                 }
                 //applyStatsOnTick = new StatsBuilder(applyStatsOnTick).setCurrentHealth(0).createStats();
@@ -95,7 +95,7 @@ public class EffectsManager {
         }
     }
 
-    public void application(EffectPojo effect, NpcSpawn npcSpawn) {
+    public void application(Effect effect, NpcSpawn npcSpawn) {
         Player player = gameManager.getPlayerManager().getPlayer(effect.getPlayerId());
         Stats applyStats = new StatsPojo(effect.getApplyStatsOnTick());
         // if there are effecst that modify npc health, deal with it here, you can't rely on combine stats.
@@ -113,13 +113,13 @@ public class EffectsManager {
         StatsHelper.combineStats(npcSpawn.getStats(), finalCombineWorthyStats);
     }
 
-    public void removeDurationStats(EffectPojo effect, NpcSpawn npcSpawn) {
+    public void removeDurationStats(Effect effect, NpcSpawn npcSpawn) {
         Stats newStats = new StatsPojo(effect.getDurationStats());
         StatsHelper.inverseStats(newStats);
         StatsHelper.combineStats(npcSpawn.getStats(), newStats);
     }
 
-    public void removeDurationStats(EffectPojo effect, PlayerData playerData) {
+    public void removeDurationStats(Effect effect, PlayerData playerData) {
         Stats newStats = new StatsPojo(effect.getDurationStats());
         StatsHelper.inverseStats(newStats);
         StatsHelper.combineStats(playerData.getStats(), newStats);
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java b/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
index 0184646b3275b0800e5f4cb72deb97114556120a..25d723672bf0833167633139a50f39ba0bf44ad8 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
@@ -19,7 +19,7 @@ package com.syncleus.aethermud.items;
 import com.google.common.collect.Sets;
 import com.syncleus.aethermud.core.service.TimeTracker;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
+import com.syncleus.aethermud.storage.graphdb.model.StatsData;
 
 import java.util.List;
 import java.util.Set;
@@ -65,7 +65,7 @@ public class ItemBuilder {
         this.isDisposable = itemMetadata.isDisposable();
         this.equipment = itemMetadata.getEquipment();
         this.validTimeOfDays = itemMetadata.getValidTimeOfDays();
-        Set<EffectPojo> effects = itemMetadata.getEffects();
+        Set<Effect> effects = itemMetadata.getEffects();
         this.effects = (effects != null ? Sets.newHashSet(itemMetadata.getEffects()) : null );
         this.itemApplyStats = itemMetadata.getItemApplyStats();
         return this;
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java b/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
index 03fa5bcf1d7e22524b0e1b7593282811708c6b8d..a151770694d9165b36e3d224bc9788cd79a1e78d 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
@@ -39,7 +39,7 @@ public class ItemMetadata {
     private int itemHalfLifeTicks;
     private Rarity rarity;
     private Equipment equipment;
-    private Set<EffectPojo> effects;
+    private Set<Effect> effects;
     private List<String> itemTriggers;
     private Set<TimeTracker.TimeOfDay> validTimeOfDays;
     private boolean isDisposable;
@@ -123,11 +123,11 @@ public class ItemMetadata {
         this.equipment = equipment;
     }
 
-    public Set<EffectPojo> getEffects() {
+    public Set<Effect> getEffects() {
         return effects;
     }
 
-    public void setEffects(Set<EffectPojo> effects) {
+    public void setEffects(Set<Effect> effects) {
         this.effects = effects;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java b/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
index b412e882264be551c1244c1b7815dd9c286e2ff3..0877b9d6365d1c7019b12ef2210d1db61d961c4c 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
@@ -28,6 +28,6 @@ public interface ItemUseAction {
 
     void postExecuteAction(GameManager gameManager, Player player, ItemPojo item);
 
-    Set<EffectPojo> getEffects();
+    Set<Effect> getEffects();
 
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java b/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java
index 81326e6c2d6f4a99268ca6b73ca5f9bfd45f5e8a..5062ed726b6a02d33072c6ca43543bdda18779bf 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java
@@ -27,7 +27,7 @@ import java.util.Set;
 public class DefaultApplyEffectsStats implements ItemUseAction {
 
     private final String internalItemName;
-    private final Set<EffectPojo> effectSet;
+    private final Set<Effect> effectSet;
     private final Stats itemApplyStats;
     private static final Logger log = Logger.getLogger(DefaultApplyEffectsStats.class);
 
@@ -73,16 +73,16 @@ public class DefaultApplyEffectsStats implements ItemUseAction {
     }
 
     @Override
-    public Set<EffectPojo> getEffects() {
+    public Set<Effect> getEffects() {
         return effectSet;
     }
 
-    public static void processEffects(GameManager gameManager, Player player, Set<EffectPojo> effects) {
+    public static void processEffects(GameManager gameManager, Player player, Set<Effect> effects) {
         if (effects == null) {
             return;
         }
-        for (EffectPojo effect : effects) {
-            EffectPojo nEffect = new EffectPojo(effect);
+        for (Effect effect : effects) {
+            Effect nEffect = new Effect(effect);
             nEffect.setPlayerId(player.getPlayerId());
             boolean effectResult = player.addEffect(nEffect);
             if (effect.getDurationStats() != null) {
diff --git a/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java b/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java
index f47cd9118aa46167cc0471fada9e73cbf4e5a9d2..f70f2e0c506b72be221291a5ad0b3c075abc1566 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java
@@ -87,7 +87,7 @@ public class DirtyBombUseAction implements ItemUseAction {
     }
 
     @Override
-    public Set<EffectPojo> getEffects() {
+    public Set<Effect> getEffects() {
         return null;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java b/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java
index 13362f639ec09076bf6ddc4cb8b7cab06ee7f399..430a6f65ece16d60ce1abd88c17a4dc2684b18e9 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java
@@ -17,7 +17,7 @@ package com.syncleus.aethermud.items.use;
 
 import com.syncleus.aethermud.command.commands.UseCommand;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.EffectPojo;
+import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.ItemMetadata;
 import com.syncleus.aethermud.items.ItemUseAction;
@@ -61,7 +61,7 @@ public class LightningSpellBookUseAction implements ItemUseAction {
     }
 
     @Override
-    public Set<EffectPojo> getEffects() {
+    public Set<Effect> getEffects() {
         return null;
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java b/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java
index e703dd3c444311787d321294d3d85f88446dbeba..3530b2db1c3c1cf813aaf498f8f0b8d7e1e1384d 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java
@@ -55,7 +55,7 @@ public class ResetAllEffectsUseAction implements ItemUseAction {
     }
 
     @Override
-    public Set<EffectPojo> getEffects() {
+    public Set<Effect> getEffects() {
         return null;
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java b/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java
index 2824479b05797369a521119cf933eb23b84cc4bf..45efbe1b72832c41d8cb78af9048ced3c2d4bde6 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java
@@ -17,7 +17,7 @@ package com.syncleus.aethermud.items.use;
 
 import com.syncleus.aethermud.command.commands.UseCommand;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.EffectPojo;
+import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.ItemMetadata;
 import com.syncleus.aethermud.items.ItemUseAction;
@@ -64,7 +64,7 @@ public class StickOfJusticeUseAction implements ItemUseAction {
     }
 
     @Override
-    public Set<EffectPojo> getEffects() {
+    public Set<Effect> getEffects() {
         return null;
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/merchant/MerchantManager.java b/src/main/java/com/syncleus/aethermud/merchant/MerchantManager.java
index ffba3520a825385dd27701164bb58b080883ca09..902dbae52c5045fb1a80416f9260c2da56b35482 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/MerchantManager.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/MerchantManager.java
@@ -23,7 +23,8 @@ import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.ItemBuilder;
 import com.syncleus.aethermud.items.ItemMetadata;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 
 import java.util.Optional;
 
@@ -58,21 +59,18 @@ public class MerchantManager {
                         return;
                     }
                     int price = merchantItemForSale.getCost();
-                    Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(player.getPlayerId());
-                    if (!playerMetadataOptional.isPresent()) {
-                        continue;
-                    }
-                    PlayerData playerData = playerMetadataOptional.get();
-                    long availableGold = playerData.getGold();
-                    if (availableGold >= price) {
-                        ItemPojo item = new ItemBuilder().from(itemMetadata).create();
-                        gameManager.getEntityManager().saveItem(item);
-                        gameManager.acquireItem(player, item.getItemId());
-                        player.incrementGold(-price);
-                        gameManager.getChannelUtils().write(player.getPlayerId(), "You have purchased: " + item.getItemName() + "\r\n");
-                    } else {
-                        gameManager.getChannelUtils().write(player.getPlayerId(), "You can't afford: " + itemMetadata.getItemName() + "\r\n");
-                    }
+                    PlayerUtil.consume(gameManager, player.getPlayerId(), playerData -> {
+                        long availableGold = playerData.getGold();
+                        if (availableGold >= price) {
+                            ItemPojo item = new ItemBuilder().from(itemMetadata).create();
+                            gameManager.getEntityManager().saveItem(item);
+                            gameManager.acquireItem(player, item.getItemId());
+                            player.incrementGold(-price);
+                            gameManager.getChannelUtils().write(player.getPlayerId(), "You have purchased: " + item.getItemName() + "\r\n");
+                        } else {
+                            gameManager.getChannelUtils().write(player.getPlayerId(), "You can't afford: " + itemMetadata.getItemName() + "\r\n");
+                        }
+                    });
                 }
             }
         } finally {
diff --git a/src/main/java/com/syncleus/aethermud/merchant/bank/commands/AccountQueryCommand.java b/src/main/java/com/syncleus/aethermud/merchant/bank/commands/AccountQueryCommand.java
index dd7bada90e60881fb5fc14db42a09a0fb1caaba1..32e36c05a105311daf25ada096b63174abdb147d 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/bank/commands/AccountQueryCommand.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/bank/commands/AccountQueryCommand.java
@@ -17,7 +17,8 @@ package com.syncleus.aethermud.merchant.bank.commands;
 
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.server.communication.Color;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.channel.MessageEvent;
@@ -41,15 +42,13 @@ public class AccountQueryCommand extends BankCommand {
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         configure(e);
         try {
-            Optional<PlayerData> playerMetadataOptional = playerManager.getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            long goldInBank = playerData.getGoldInBank();
-            long gold = playerData.getGold();
-            write("You have " + NumberFormat.getNumberInstance(Locale.US).format(goldInBank) + Color.YELLOW + " gold" + Color.RESET + " in your bank account."+ "\r\n");
-            write("You have " + NumberFormat.getNumberInstance(Locale.US).format(gold) + Color.YELLOW + " gold" + Color.RESET + " in your inventory."+ "\r\n");
+            PlayerUtil.consume(gameManager, player.getPlayerId(), playerData -> {
+                long goldInBank = playerData.getGoldInBank();
+                long gold = playerData.getGold();
+                write("You have " + NumberFormat.getNumberInstance(Locale.US).format(goldInBank) + Color.YELLOW + " gold" + Color.RESET + " in your bank account."+ "\r\n");
+                write("You have " + NumberFormat.getNumberInstance(Locale.US).format(gold) + Color.YELLOW + " gold" + Color.RESET + " in your inventory."+ "\r\n");
+            });
+
         } finally {
             super.messageReceived(ctx, e);
         }
diff --git a/src/main/java/com/syncleus/aethermud/merchant/bank/commands/DepositCommand.java b/src/main/java/com/syncleus/aethermud/merchant/bank/commands/DepositCommand.java
index b3b4c59bdb4535c42d347ea42bd2d1a89b428552..8476c01a33e15df3fd59e9400123bb034467718f 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/bank/commands/DepositCommand.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/bank/commands/DepositCommand.java
@@ -17,7 +17,8 @@ package com.syncleus.aethermud.merchant.bank.commands;
 
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.server.communication.Color;
 import org.apache.commons.lang.math.NumberUtils;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -57,13 +58,7 @@ public class DepositCommand extends BankCommand {
     }
 
     private boolean areFundsAvailable(long amt) {
-        Optional<PlayerData> playerMetadataOptional = playerManager.getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()) {
-            return false;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        long inventoryGold = playerData.getGold();
-        return (inventoryGold >= amt);
+        return PlayerUtil.transactRead(gameManager, player.getPlayerId(), playerData -> playerData.getGold() >= amt);
     }
 
 }
diff --git a/src/main/java/com/syncleus/aethermud/merchant/bank/commands/WithdrawalCommand.java b/src/main/java/com/syncleus/aethermud/merchant/bank/commands/WithdrawalCommand.java
index 1c6b5e244e76fe3af4bc43e16049b753797453ed..8c64acd8083c5d2a9fde7ba67cfec4ca26017a33 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/bank/commands/WithdrawalCommand.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/bank/commands/WithdrawalCommand.java
@@ -16,7 +16,8 @@
 package com.syncleus.aethermud.merchant.bank.commands;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.server.communication.Color;
 import org.apache.commons.lang.math.NumberUtils;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -56,13 +57,7 @@ public class WithdrawalCommand extends BankCommand {
     }
 
     private boolean areBankFundsAvailable(int amt) {
-        Optional<PlayerData> playerMetadataOptional = playerManager.getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()) {
-            return false;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        long bankGold = playerData.getGoldInBank();
-        return (bankGold >= amt);
+        return PlayerUtil.transactRead(gameManager, playerId, playerData -> (playerData.getGoldInBank() >= amt));
     }
 
 }
diff --git a/src/main/java/com/syncleus/aethermud/merchant/lockers/GetCommand.java b/src/main/java/com/syncleus/aethermud/merchant/lockers/GetCommand.java
index fa0343769f077c753ef39edfc47615c01eea04f7..ac53808471d3b03a54155a33eccd16e2d9aec348 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/lockers/GetCommand.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/lockers/GetCommand.java
@@ -17,7 +17,8 @@ package com.syncleus.aethermud.merchant.lockers;
 
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.google.common.base.Joiner;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.channel.MessageEvent;
@@ -42,24 +43,26 @@ public class GetCommand extends LockerCommand {
             configure(e);
             originalMessageParts.remove(0);
             String desiredRetrieveOption = Joiner.on(" ").join(originalMessageParts);
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            for (String entityId: playerData.getLockerInventory()) {
-                Optional<ItemPojo> itemEntityOptional = gameManager.getEntityManager().getItemEntity(entityId);
-                if (!itemEntityOptional.isPresent()) {
-                    continue;
-                }
-                ItemPojo itemEntity = itemEntityOptional.get();
-                if (itemEntity.getItemTriggers().contains(desiredRetrieveOption)) {
-                    player.transferItemFromLocker(entityId);
-                    write(itemEntity.getItemName() + " retrieved from locker.\r\n");
+            try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                Optional<PlayerData> playerMetadataOptional = tx.getStorage().getPlayerMetadata(playerId);
+                if (!playerMetadataOptional.isPresent()) {
                     return;
                 }
+                PlayerData playerData = playerMetadataOptional.get();
+                for (String entityId : playerData.getLockerInventory()) {
+                    Optional<ItemPojo> itemEntityOptional = gameManager.getEntityManager().getItemEntity(entityId);
+                    if (!itemEntityOptional.isPresent()) {
+                        continue;
+                    }
+                    ItemPojo itemEntity = itemEntityOptional.get();
+                    if (itemEntity.getItemTriggers().contains(desiredRetrieveOption)) {
+                        player.transferItemFromLocker(entityId);
+                        write(itemEntity.getItemName() + " retrieved from locker.\r\n");
+                        return;
+                    }
+                }
+                write("Item not found in locker.\r\n");
             }
-            write("Item not found in locker.\r\n");
         } finally {
             super.messageReceived(ctx, e);
         }
diff --git a/src/main/java/com/syncleus/aethermud/merchant/lockers/QueryCommand.java b/src/main/java/com/syncleus/aethermud/merchant/lockers/QueryCommand.java
index 461eceab34d6e02e7ec6f1cacd2473ff95c2cf68..adb80831352da46639c4cb058ea05af17121f9d9 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/lockers/QueryCommand.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/lockers/QueryCommand.java
@@ -16,7 +16,8 @@
 package com.syncleus.aethermud.merchant.lockers;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.channel.MessageEvent;
 
@@ -37,19 +38,16 @@ public class QueryCommand extends LockerCommand {
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         try {
             configure(e);
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            write("----LOCKER ITEMS\r\n");
-            for (String rolledUpInvLine: player.getRolledUpLockerInventory()) {
-                write(rolledUpInvLine);;
-            }
-            write("\r\n\r\n----PERSONAL INVENTORY\r\n");
-            for (String rolledUpInvLine: player.getRolledUpIntentory()) {
-                write(rolledUpInvLine);
-            }
+            PlayerUtil.consume(gameManager, playerId, playerData -> {
+                write("----LOCKER ITEMS\r\n");
+                for (String rolledUpInvLine: player.getRolledUpLockerInventory()) {
+                    write(rolledUpInvLine);;
+                }
+                write("\r\n\r\n----PERSONAL INVENTORY\r\n");
+                for (String rolledUpInvLine: player.getRolledUpIntentory()) {
+                    write(rolledUpInvLine);
+                }
+            });
         } finally {
             super.messageReceived(ctx, e);
         }
diff --git a/src/main/java/com/syncleus/aethermud/npc/BasicNpcPlayerDamageProcessor.java b/src/main/java/com/syncleus/aethermud/npc/BasicNpcPlayerDamageProcessor.java
index 3557f9b05806c3d3a0f2daffa2223e06a08d6333..dabc11101dc7609cd376827097ee2ac847498baa 100644
--- a/src/main/java/com/syncleus/aethermud/npc/BasicNpcPlayerDamageProcessor.java
+++ b/src/main/java/com/syncleus/aethermud/npc/BasicNpcPlayerDamageProcessor.java
@@ -48,7 +48,7 @@ public class BasicNpcPlayerDamageProcessor implements DamageProcessor {
     public int getChanceToHit(Player player, NpcSpawn npcSpawn) {
         Stats playerStats = player.getPlayerStatsWithEquipmentAndLevel();
         Stats npcStats = npcSpawn.getStats();
-        return (int) ((npcStats.getStrength() + npcStats.getMeleSkill()) * 5 - playerStats.getAgile() * 5);
+        return (int) ((npcStats.getStrength() + npcStats.getMeleeSkill()) * 5 - playerStats.getAgile() * 5);
     }
 
     @Override
diff --git a/src/main/java/com/syncleus/aethermud/npc/NpcBuilder.java b/src/main/java/com/syncleus/aethermud/npc/NpcBuilder.java
index f6039508cda70316fb783bb6cd1092065138e7ba..25a8b90b486e39ac7f38fa67878e8ad251f35a7a 100644
--- a/src/main/java/com/syncleus/aethermud/npc/NpcBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/npc/NpcBuilder.java
@@ -22,8 +22,8 @@ import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.aethermud.stats.StatsPojo;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
-import com.syncleus.aethermud.storage.graphdb.NpcData;
+import com.syncleus.aethermud.storage.graphdb.model.StatsData;
+import com.syncleus.aethermud.storage.graphdb.model.NpcData;
 import com.syncleus.aethermud.world.model.Area;
 import org.apache.commons.beanutils.PropertyUtils;
 
diff --git a/src/main/java/com/syncleus/aethermud/npc/NpcMover.java b/src/main/java/com/syncleus/aethermud/npc/NpcMover.java
index efe6fd68983b3345e9ea26728a5a28743fe91330..24dcc25e5ce0451a9b4630887bf754cf26c33309 100644
--- a/src/main/java/com/syncleus/aethermud/npc/NpcMover.java
+++ b/src/main/java/com/syncleus/aethermud/npc/NpcMover.java
@@ -16,7 +16,7 @@
 package com.syncleus.aethermud.npc;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.player.CoolDownPojo;
+import com.syncleus.aethermud.player.CoolDown;
 import com.syncleus.aethermud.player.CoolDownType;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.spawner.SpawnRule;
@@ -75,7 +75,7 @@ public class NpcMover {
         Room destinationRoom = gameManager.getRoomManager().getRoom(destinationRoomId);
         npcSpawnEntity.setCurrentRoom(destinationRoom);
         destinationRoom.getNpcIds().add(npcId);
-        npcSpawnEntity.addCoolDown(new CoolDownPojo(CoolDownType.NPC_ROAM));
+        npcSpawnEntity.addCoolDown(new CoolDown(CoolDownType.NPC_ROAM));
         gameManager.roomSay(destinationRoomId, npcSpawnEntity.getColorName() + " has arrived.", "");
         destinationRoom.getPresentPlayers().forEach(Player::processNpcAggro);
     }
diff --git a/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java b/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
index 2f9d5bbf1654bda97ac7fbae4438694dcb41ed3c..5a508369452eae6329011de4bb643af0d1392ddb 100644
--- a/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
+++ b/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
@@ -21,10 +21,10 @@ import com.syncleus.aethermud.common.AetherMudMessage;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.core.SentryManager;
 import com.syncleus.aethermud.entity.AetherMudEntity;
-import com.syncleus.aethermud.items.EffectPojo;
+import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.Loot;
-import com.syncleus.aethermud.player.CoolDownPojo;
+import com.syncleus.aethermud.player.CoolDown;
 import com.syncleus.aethermud.player.CoolDownType;
 import com.syncleus.aethermud.player.DamageProcessor;
 import com.syncleus.aethermud.player.Player;
@@ -77,12 +77,12 @@ public class NpcSpawn extends AetherMudEntity {
     private final Random random = new Random();
     private long lastPhraseTimestamp;
     private Loot loot;
-    private List<EffectPojo> effects = Lists.newArrayList();
+    private List<Effect> effects = Lists.newArrayList();
     private int maxEffects = 4;
     private Map<String, Long> playerDamageMap = Maps.newHashMap();
     private Room currentRoom;
     private int effectsTickBucket = 0;
-    private Set<CoolDownPojo> coolDowns = Sets.newHashSet();
+    private Set<CoolDown> coolDowns = Sets.newHashSet();
     private final Experience experience = new Experience();
     // The messages used when dealing damage
     private final Set<AetherMudMessage> attackMessages;
@@ -136,9 +136,9 @@ public class NpcSpawn extends AetherMudEntity {
                     if (effectsTickBucket == 5) {
 
                         // START Process NPC Effects
-                        Iterator<EffectPojo> iterator = effects.iterator();
+                        Iterator<Effect> iterator = effects.iterator();
                         while (iterator.hasNext()) {
-                            EffectPojo effect = iterator.next();
+                            Effect effect = iterator.next();
                             if (effect.getEffectApplications() >= effect.getMaxEffectApplications()) {
                                 Optional<Room> npcCurrentRoom = gameManager.getRoomManager().getNpcCurrentRoom(this);
                                 if (npcCurrentRoom.isPresent()) {
@@ -230,7 +230,7 @@ public class NpcSpawn extends AetherMudEntity {
     }
 
     public boolean isActiveCooldown(CoolDownType coolDownType) {
-        for (CoolDownPojo c : coolDowns) {
+        for (CoolDown c : coolDowns) {
             if (c.getCoolDownType().equals(coolDownType)) {
                 if (c.isActive()) {
                     return true;
@@ -248,9 +248,9 @@ public class NpcSpawn extends AetherMudEntity {
     }
 
     private void tickAllActiveCoolDowns() {
-        Iterator<CoolDownPojo> iterator = coolDowns.iterator();
+        Iterator<CoolDown> iterator = coolDowns.iterator();
         while (iterator.hasNext()) {
-            CoolDownPojo coolDown = iterator.next();
+            CoolDown coolDown = iterator.next();
             if (coolDown.isActive()) {
                 coolDown.decrementTick();
             } else {
@@ -367,7 +367,7 @@ public class NpcSpawn extends AetherMudEntity {
         this.loot = loot;
     }
 
-    public Set<CoolDownPojo> getCoolDowns() {
+    public Set<CoolDown> getCoolDowns() {
         return coolDowns;
     }
 
@@ -412,7 +412,7 @@ public class NpcSpawn extends AetherMudEntity {
         return spawnRules;
     }
 
-    public void addEffect(EffectPojo effect) {
+    public void addEffect(Effect effect) {
         synchronized (interner.intern(getEntityId())) {
             if (effects.size() >= maxEffects) {
             } else {
@@ -421,7 +421,7 @@ public class NpcSpawn extends AetherMudEntity {
         }
     }
 
-    public List<EffectPojo> getEffects() {
+    public List<Effect> getEffects() {
         return effects;
     }
 
@@ -445,9 +445,9 @@ public class NpcSpawn extends AetherMudEntity {
 
     public void addNpcDamage(NpcStatsChange npcStatsChange) {
         if (!isActiveCooldown(CoolDownType.NPC_FIGHT)) {
-            addCoolDown(new CoolDownPojo(CoolDownType.NPC_FIGHT));
+            addCoolDown(new CoolDown(CoolDownType.NPC_FIGHT));
         } else {
-            for (CoolDownPojo coolDown : coolDowns) {
+            for (CoolDown coolDown : coolDowns) {
                 if (coolDown.getCoolDownType().equals(CoolDownType.NPC_FIGHT)) {
                     coolDown.setNumberOfTicks(coolDown.getOriginalNumberOfTicks());
                 }
@@ -456,7 +456,7 @@ public class NpcSpawn extends AetherMudEntity {
         this.npcStatsChanges.add(npcStatsChange);
     }
 
-    public void addCoolDown(CoolDownPojo coolDown) {
+    public void addCoolDown(CoolDown coolDown) {
         this.coolDowns.add(coolDown);
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/npc/NpcStatsChange.java b/src/main/java/com/syncleus/aethermud/npc/NpcStatsChange.java
index d85cd765d70fb8bec367dc23aa3597ca95982746..3c31892d59d3574377faebd229c8f694cca4d4a2 100644
--- a/src/main/java/com/syncleus/aethermud/npc/NpcStatsChange.java
+++ b/src/main/java/com/syncleus/aethermud/npc/NpcStatsChange.java
@@ -17,7 +17,6 @@ package com.syncleus.aethermud.npc;
 
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
 
 import java.util.List;
 
diff --git a/src/main/java/com/syncleus/aethermud/npc/NpcStatsChangeBuilder.java b/src/main/java/com/syncleus/aethermud/npc/NpcStatsChangeBuilder.java
index 6c9d20e4ebdcdc379cb09c86ffea608e45096f1c..44dd4c7bb938c8da8d3d559e670c07db3d13713b 100644
--- a/src/main/java/com/syncleus/aethermud/npc/NpcStatsChangeBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/npc/NpcStatsChangeBuilder.java
@@ -17,7 +17,6 @@ package com.syncleus.aethermud.npc;
 
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
 
 import java.util.List;
 
diff --git a/src/main/java/com/syncleus/aethermud/player/BasicPlayerDamageProcessor.java b/src/main/java/com/syncleus/aethermud/player/BasicPlayerDamageProcessor.java
index 887a6ed1a750e7bbc20d23b26ca63b0cbce61c20..817a74bce0fdbe776145c81ea1a7098e04ddafdb 100644
--- a/src/main/java/com/syncleus/aethermud/player/BasicPlayerDamageProcessor.java
+++ b/src/main/java/com/syncleus/aethermud/player/BasicPlayerDamageProcessor.java
@@ -46,7 +46,7 @@ public class BasicPlayerDamageProcessor implements DamageProcessor {
     public int getChanceToHit(Player player, NpcSpawn npcSpawn) {
         Stats playerStats = player.getPlayerStatsWithEquipmentAndLevel();
         Stats npcStats = npcSpawn.getStats();
-        return (int) ((playerStats.getStrength() + playerStats.getMeleSkill()) * 5 - npcStats.getAgile() * 5);
+        return (int) ((playerStats.getStrength() + playerStats.getMeleeSkill()) * 5 - npcStats.getAgile() * 5);
     }
 
     @Override
diff --git a/src/main/java/com/syncleus/aethermud/player/CoolDown.java b/src/main/java/com/syncleus/aethermud/player/CoolDown.java
index 25f7a33f4b41600acd9c140e33b6f7873cb1de26..0d94a015c3480ea72304df2bc9e2fb30c4683506 100644
--- a/src/main/java/com/syncleus/aethermud/player/CoolDown.java
+++ b/src/main/java/com/syncleus/aethermud/player/CoolDown.java
@@ -16,30 +16,82 @@
 package com.syncleus.aethermud.player;
 
 
-public interface CoolDown {
-    CoolDownType getCoolDownType();
+public class CoolDown {
 
-    void setCoolDownType(CoolDownType coolDownType);
+    private int numberOfTicks;
+    private String name;
+    private CoolDownType coolDownType;
+    private int originalNumberOfTicks;
 
-    String getName();
+    public CoolDown(CoolDownType coolDownType) {
+        this.name = coolDownType.getName();
+        this.numberOfTicks = coolDownType.getTicks();
+        this.coolDownType = coolDownType;
+        this.originalNumberOfTicks = coolDownType.getTicks();
+    }
+
+    public CoolDown(String name, int numberOfTicks, CoolDownType coolDownType){
+        this.name = name;
+        this.numberOfTicks = numberOfTicks;
+        this.coolDownType = coolDownType;
+        this.originalNumberOfTicks = numberOfTicks;
+    }
+
+    public CoolDownType getCoolDownType() {
+        return coolDownType;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getNumberOfTicks() {
+        return numberOfTicks;
+    }
 
-    void setName(String name);
+    public int getOriginalNumberOfTicks() {
+        return originalNumberOfTicks;
+    }
 
-    int getNumberOfTicks();
+    public void setNumberOfTicks(int numberOfTicks) {
+        this.numberOfTicks = numberOfTicks;
+    }
 
-    void setNumberOfTicks(int numberOfTicks);
+    public void setName(String name) {
+        this.name = name;
+    }
 
-    int getOriginalNumberOfTicks();
+    public void setCoolDownType(CoolDownType coolDownType) {
+        this.coolDownType = coolDownType;
+    }
 
-    void setOriginalNumberOfTicks(int ticks);
+    public void setOriginalNumberOfTicks(int originalNumberOfTicks) {
+        this.originalNumberOfTicks = originalNumberOfTicks;
+    }
 
-    default void decrementTick() {
+    public int hashCode() {
+        return this.getCoolDownType().hashCode();
+    }
+
+    public void decrementTick() {
         if (getNumberOfTicks() > 0) {
             this.setNumberOfTicks(getNumberOfTicks() - 1);
         }
     }
 
-    default boolean isActive() {
+    public boolean isActive() {
         return getNumberOfTicks() > 0;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if(o == null)
+            return false;
+        if( o instanceof CoolDown)
+            return this.getCoolDownType().equals(((CoolDown)o).getCoolDownType());
+        else if( o instanceof CoolDownType )
+            return this.getCoolDownType().equals((CoolDownType)o);
+        else
+            return false;
+    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/player/CoolDownPojo.java b/src/main/java/com/syncleus/aethermud/player/CoolDownPojo.java
deleted file mode 100644
index 6a881d507a89323551f3bb730852ded19937e26a..0000000000000000000000000000000000000000
--- a/src/main/java/com/syncleus/aethermud/player/CoolDownPojo.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * Copyright 2017 Syncleus, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.syncleus.aethermud.player;
-
-
-public class CoolDownPojo implements CoolDown {
-
-    private int numberOfTicks;
-    private String name;
-    private CoolDownType coolDownType;
-    private int originalNumberOfTicks;
-
-    public CoolDownPojo(CoolDownType coolDownType) {
-        this.name = coolDownType.getName();
-        this.numberOfTicks = coolDownType.getTicks();
-        this.coolDownType = coolDownType;
-        this.originalNumberOfTicks = coolDownType.getTicks();
-    }
-
-    public CoolDownPojo(String name, int numberOfTicks, CoolDownType coolDownType){
-        this.name = name;
-        this.numberOfTicks = numberOfTicks;
-        this.coolDownType = coolDownType;
-        this.originalNumberOfTicks = numberOfTicks;
-    }
-
-    public CoolDownType getCoolDownType() {
-        return coolDownType;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public int getNumberOfTicks() {
-        return numberOfTicks;
-    }
-
-    public int getOriginalNumberOfTicks() {
-        return originalNumberOfTicks;
-    }
-
-    public void setNumberOfTicks(int numberOfTicks) {
-        this.numberOfTicks = numberOfTicks;
-    }
-
-    @Override
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    @Override
-    public void setCoolDownType(CoolDownType coolDownType) {
-        this.coolDownType = coolDownType;
-    }
-
-    @Override
-    public void setOriginalNumberOfTicks(int originalNumberOfTicks) {
-        this.originalNumberOfTicks = originalNumberOfTicks;
-    }
-
-    @Override
-    public int hashCode() {
-        return this.getCoolDownType().hashCode();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if(o == null)
-            return false;
-        if( o instanceof CoolDown )
-            return this.getCoolDownType().equals(((CoolDown)o).getCoolDownType());
-        else if( o instanceof CoolDownType )
-            return this.getCoolDownType().equals((CoolDownType)o);
-        else
-            return false;
-    }
-}
diff --git a/src/main/java/com/syncleus/aethermud/player/Player.java b/src/main/java/com/syncleus/aethermud/player/Player.java
index 0c45fd112f9185873845b3ae65bfe7f82d30029e..213cba313ae8d1225e722281c731033b79e0fda3 100644
--- a/src/main/java/com/syncleus/aethermud/player/Player.java
+++ b/src/main/java/com/syncleus/aethermud/player/Player.java
@@ -17,6 +17,7 @@ package com.syncleus.aethermud.player;
 
 
 import com.codahale.metrics.Meter;
+import com.google.common.base.*;
 import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.common.AetherMudUtils;
 import com.syncleus.aethermud.core.GameManager;
@@ -26,13 +27,11 @@ import com.syncleus.aethermud.npc.NpcSpawn;
 import com.syncleus.aethermud.npc.NpcStatsChangeBuilder;
 import com.syncleus.aethermud.npc.Temperament;
 import com.syncleus.aethermud.server.communication.Color;
-import com.syncleus.aethermud.stats.Levels;
-import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.CoolDownData;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
-import com.syncleus.aethermud.stats.StatsBuilder;
-import com.syncleus.aethermud.stats.StatsHelper;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.stats.*;
+import com.syncleus.aethermud.storage.graphdb.model.CoolDownData;
+import com.syncleus.aethermud.storage.graphdb.model.EffectData;
+import com.syncleus.aethermud.storage.graphdb.model.StatsData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.world.model.Room;
 import com.google.common.collect.*;
 import com.syncleus.aethermud.items.*;
@@ -45,9 +44,11 @@ import org.nocrala.tools.texttablefmt.Table;
 
 import java.text.NumberFormat;
 import java.util.*;
+import java.util.Optional;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 public class Player extends AetherMudEntity {
@@ -142,47 +143,35 @@ public class Player extends AetherMudEntity {
 
     private void processRegens() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
             Stats stats = getPlayerStatsWithEquipmentAndLevel();
             if (isActive(CoolDownType.NPC_FIGHT) || isActive(CoolDownType.DEATH)) {
                 return;
             }
-            if (playerData.getStats().getCurrentHealth() < stats.getMaxHealth()) {
-                updatePlayerHealth((int) (stats.getMaxHealth() * .05), null);
-            }
-            if (playerData.getStats().getCurrentMana() < stats.getMaxMana()) {
-                addMana((int) (stats.getMaxMana() * .03));
-            }
+            int maxHealth = stats.getMaxHealth();
+            this.consumeRead((p) -> {
+                if (p.getStats().getCurrentHealth() < maxHealth) {
+                    updatePlayerHealth((int) (maxHealth * .05), null);
+                }
+                if (p.getStats().getCurrentMana() < maxHealth) {
+                    addMana((int) (maxHealth * .03));
+                }
+            });
         }
     }
 
     private void processEffects() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            List<EffectPojo> effectsToRemove = Lists.newArrayList();
-            for (EffectPojo effect : playerData.getEffects()) {
-                if (effect.getEffectApplications() >= effect.getMaxEffectApplications()) {
-                    gameManager.getChannelUtils().write(playerId, Color.BOLD_ON + Color.GREEN + "[effect] " + Color.RESET + effect.getEffectName() + " has worn off.\r\n", true);
-                    effectsToRemove.add(effect);
-                    continue;
-                } else {
-                    effect.setEffectApplications(effect.getEffectApplications() + 1);
-                    gameManager.getEffectsManager().application(effect, this);
+            this.consume((playerData) -> {
+                for (EffectData effect : playerData.getEffects()) {
+                    if (effect.getEffectApplications() >= effect.getMaxEffectApplications()) {
+                        gameManager.getChannelUtils().write(playerId, Color.BOLD_ON + Color.GREEN + "[effect] " + Color.RESET + effect.getEffectName() + " has worn off.\r\n", true);
+                        playerData.removeEffect(effect);
+                    } else {
+                        effect.setEffectApplications(effect.getEffectApplications() + 1);
+                        gameManager.getEffectsManager().application(EffectData.copyEffect(effect), this);
+                    }
                 }
-
-            }
-            for (EffectPojo effect : effectsToRemove) {
-                playerData.removeEffect(effect);
-            }
-            savePlayerMetadata();
+            });
         }
     }
 
@@ -193,18 +182,14 @@ public class Player extends AetherMudEntity {
                 removeAllActiveFights();
             }
             if (!isActive(CoolDownType.DEATH)) {
-                Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-                if (!playerMetadataOptional.isPresent()) {
-                    return;
-                }
-                PlayerData playerData = playerMetadataOptional.get();
-                int newGold = playerData.getGold() / 2;
-                playerData.setGold(newGold);
-                if (newGold > 0) {
-                    gameManager.getChannelUtils().write(getPlayerId(), "You just " + Color.BOLD_ON + Color.RED + "lost " + Color.RESET + newGold + Color.YELLOW + " gold" + Color.RESET + "!\r\n");
-                }
-                removeActiveAlertStatus();
-                savePlayerMetadata();
+                this.consume((playerData) -> {
+                    int newGold = playerData.getGold() / 2;
+                    playerData.setGold(newGold);
+                    if (newGold > 0) {
+                        gameManager.getChannelUtils().write(getPlayerId(), "You just " + Color.BOLD_ON + Color.RED + "lost " + Color.RESET + newGold + Color.YELLOW + " gold" + Color.RESET + "!\r\n");
+                    }
+                    removeActiveAlertStatus();
+                });
                 addCoolDown(CoolDownType.DEATH);
                 gameManager.writeToPlayerCurrentRoom(getPlayerId(), getPlayerName() + " is now dead." + "\r\n");
                 PlayerMovement playerMovement = new PlayerMovement(this, gameManager.getRoomManager().getPlayerCurrentRoom(this).get().getRoomId(), GameManager.LOBBY_ID, "vanished into the ether.", "");
@@ -218,24 +203,30 @@ public class Player extends AetherMudEntity {
 
     public boolean updatePlayerHealth(int amount, NpcSpawn npcSpawn) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return false;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
             if (amount > 0) {
-                addHealth(amount, playerData);
-                savePlayerMetadata();
+                int currentHealth = this.transactRead(playerData -> playerData.getStats().getCurrentHealth());
+                Stats statsModifier = getPlayerStatsWithEquipmentAndLevel();
+                int maxHealth = statsModifier.getMaxHealth();
+                int proposedNewAmt = currentHealth + amount;
+                final int finalNewAmount;
+                if (proposedNewAmt > maxHealth) {
+                    if (currentHealth < maxHealth) {
+                        finalNewAmount = proposedNewAmt - (proposedNewAmt - maxHealth);
+                    } else {
+                        finalNewAmount = proposedNewAmt - amount;
+                    }
+                }
+                else
+                    finalNewAmount = proposedNewAmt;
+                this.consume(playerData -> playerData.getStats().setCurrentHealth(finalNewAmount));
                 return false;
             } else {
-                StatsData stats = playerData.getStats();
-                if ((stats.getCurrentHealth() + amount) < 0) {
-                    stats.setCurrentHealth(0);
+                if ((this.transactRead(playerData -> playerData.getStats().getCurrentHealth()) + amount) < 0) {
+                    this.consume(playerData -> playerData.getStats().setCurrentHealth(0));
                 } else {
-                    stats.setCurrentHealth(stats.getCurrentHealth() + amount);
+                    this.consume(playerData -> playerData.getStats().setCurrentHealth(playerData.getStats().getCurrentHealth() + amount));
                 }
-                savePlayerMetadata();
-                if (playerData.getStats().getCurrentHealth() == 0) {
+                if (this.transactRead(playerData -> playerData.getStats().getCurrentHealth()) == 0) {
                     killPlayer(npcSpawn);
                     return true;
                 }
@@ -264,199 +255,119 @@ public class Player extends AetherMudEntity {
         return getPlayerStatsWithEquipmentAndLevel().getCurrentMana();
     }
 
-
-    private void addHealth(int addAmt, PlayerData playerData) {
-        int currentHealth = playerData.getStats().getCurrentHealth();
-        Stats statsModifier = getPlayerStatsWithEquipmentAndLevel();
-        int maxHealth = statsModifier.getMaxHealth();
-        int proposedNewAmt = currentHealth + addAmt;
-        if (proposedNewAmt > maxHealth) {
-            if (currentHealth < maxHealth) {
-                int adjust = proposedNewAmt - maxHealth;
-                proposedNewAmt = proposedNewAmt - adjust;
-            } else {
-                proposedNewAmt = proposedNewAmt - addAmt;
-            }
-        }
-        playerData.getStats().setCurrentHealth(proposedNewAmt);
-    }
-
     public void addMana(int addAmt) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            PlayerData playerData = playerMetadataOptional.get();
-            int currentMana = playerData.getStats().getCurrentMana();
+            int currentMana = this.transactRead(playerData -> playerData.getStats().getCurrentMana());
             Stats statsModifier = getPlayerStatsWithEquipmentAndLevel();
             int maxMana = statsModifier.getMaxMana();
             int proposedNewAmt = currentMana + addAmt;
+            final int finalNewAmount;
             if (proposedNewAmt > maxMana) {
                 if (currentMana < maxMana) {
-                    int adjust = proposedNewAmt - maxMana;
-                    proposedNewAmt = proposedNewAmt - adjust;
+                    finalNewAmount = proposedNewAmt - (proposedNewAmt - maxMana);
                 } else {
-                    proposedNewAmt = proposedNewAmt - addAmt;
+                    finalNewAmount = proposedNewAmt - addAmt;
                 }
             }
-            playerData.getStats().setCurrentMana(proposedNewAmt);
-            savePlayerMetadata();
+            else
+                finalNewAmount = proposedNewAmt;
+            this.consume(playerData -> playerData.getStats().setCurrentMana(finalNewAmount));
         }
     }
 
     public void addExperience(int exp) {
         synchronized (interner.intern(playerId)) {
             final Meter requests = Main.metrics.meter("experience-" + playerName);
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            int currentExperience = playerData.getStats().getExperience();
-            int currentLevel = Levels.getLevel(currentExperience);
-            playerData.getStats().setExperience(currentExperience + exp);
-            requests.mark(exp);
-            int newLevel = Levels.getLevel(playerData.getStats().getExperience());
-            if (newLevel > currentLevel) {
-                gameManager.announceLevelUp(playerName, currentLevel, newLevel);
-            }
-            savePlayerMetadata();
+            this.consume(playerData -> {
+                int currentExperience = playerData.getStats().getExperience();
+                int currentLevel = Levels.getLevel(currentExperience);
+                playerData.getStats().setExperience(currentExperience + exp);
+                requests.mark(exp);
+                int newLevel = Levels.getLevel(playerData.getStats().getExperience());
+                if (newLevel > currentLevel) {
+                    gameManager.announceLevelUp(playerName, currentLevel, newLevel);
+                }
+            });
         }
     }
 
     public int getLevel() {
-        Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-        return playerMetadataOptional.map(playerMetadata -> Levels.getLevel(playerMetadata.getStats().getExperience())).orElse(0);
-    }
-
-    private Optional<PlayerData> getPlayerMetadata() {
-        return gameManager.getPlayerManager().getPlayerMetadata(playerId);
-    }
-
-    private void savePlayerMetadata() {
-        gameManager.getPlayerManager().persist();
+        return this.transactRead(playerData -> Levels.getLevel(playerData.getStats().getExperience()));
     }
 
     public int getCurrentHealth() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            return playerMetadataOptional.map(playerMetadata -> playerMetadata.getStats().getCurrentHealth()).orElse(0);
+            return this.transactRead(playerData -> playerData.getStats().getCurrentHealth());
         }
     }
 
     public void setCurrentHealth(int health) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            playerMetadataOptional.ifPresent((p) -> p.getStats().setCurrentHealth(health));
+            this.consume(playerData -> playerData.getStats().setCurrentHealth(health));
         }
     }
 
     public void transferGoldToBank(int amt) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.transferGoldToBank(amt);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.transferGoldToBank(amt));
         }
     }
 
     public void transferBankGoldToPlayer(int amt) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.transferBankGoldToPlayer(amt);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.transferBankGoldToPlayer(amt));
         }
     }
 
     public void incrementGold(int amt) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.incrementGold(amt);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.incrementGold(amt));
         }
     }
 
-    public boolean addEffect(EffectPojo effect) {
+    public boolean addEffect(Effect effect) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return false;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            if (playerData.getEffects() != null && (playerData.getEffects().size() >= playerData.getStats().getMaxEffects())) {
-                return false;
-            }
-            playerData.addEffect(effect);
-            savePlayerMetadata();
-            return true;
+            return this.transact(playerData -> {
+                if (playerData.getEffects() != null && (playerData.getEffects().size() >= playerData.getStats().getMaxEffects())) {
+                    return false;
+                }
+
+                EffectData.copyEffect(playerData.createEffect(), effect);
+                return true;
+            });
         }
     }
 
     public void resetEffects() {
         synchronized (interner) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.resetEffects();
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.resetEffects());
         }
     }
 
     public void addLearnedSpellByName(String spellName) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.addLearnedSpellByName(spellName);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.addLearnedSpellByName(spellName));
         }
     }
 
     public boolean doesHaveSpellLearned(String spellName) {
-        Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-        if (!playerMetadataOptional.isPresent()) {
-            return false;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        List<String> learnedSpells = playerData.getLearnedSpells();
-        if (learnedSpells == null || learnedSpells.isEmpty()) {
-            return false;
-        }
-        return learnedSpells.contains(spellName);
+        return this.transactRead(playerData -> {
+            List<String> learnedSpells = playerData.getLearnedSpells();
+            if (learnedSpells == null || learnedSpells.isEmpty()) {
+                return false;
+            }
+            return learnedSpells.contains(spellName);
+        });
     }
 
     public void removeLearnedSpellByName(String spellName) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.removeLearnedSpellByName(spellName);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.removeLearnedSpellByName(spellName));
         }
     }
 
     public List<String> getLearnedSpells() {
-        Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-        if (!playerMetadataOptional.isPresent()) {
-            return Lists.newArrayList();
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        return Lists.newArrayList(playerData.getLearnedSpells());
+        return this.transactRead(playerData -> Lists.newArrayList(playerData.getLearnedSpells()));
     }
 
     public boolean isActiveAlertNpcStatus(NpcSpawn npcSpawn) {
@@ -503,13 +414,7 @@ public class Player extends AetherMudEntity {
 
     public void addInventoryId(String inventoryId) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.addInventoryEntityId(inventoryId);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.addInventoryEntityId(inventoryId));
         }
     }
 
@@ -522,42 +427,25 @@ public class Player extends AetherMudEntity {
 
     public void removeInventoryId(String inventoryId) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.removeInventoryEntityId(inventoryId);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.removeInventoryEntityId(inventoryId));
         }
     }
 
     public void addLockerInventoryId(String entityId) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.addLockerEntityId(entityId);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.addLockerEntityId(entityId));
         }
     }
 
     public void addNpcKillLog(String npcName) {
         gameManager.getEventProcessor().addEvent(() -> {
             synchronized (interner.intern(playerId)) {
-                Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-                if (!playerMetadataOptional.isPresent()) {
-                    return;
-                }
-                PlayerData playerData = playerMetadataOptional.get();
-                playerData.addNpcKill(npcName);
-                savePlayerMetadata();
+                this.consume(playerData -> playerData.addNpcKill(npcName));
             }
         });
     }
 
+
     public void transferItemFromLocker(String entityId) {
         synchronized (interner.intern(playerId)) {
             if (gameManager.acquireItem(this, entityId)) {
@@ -568,85 +456,47 @@ public class Player extends AetherMudEntity {
 
     public void removeLockerInventoryId(String lockerInventoryId) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.removeLockerEntityId(lockerInventoryId);
-            savePlayerMetadata();
+            this.consume((p) -> p.removeLockerEntityId(lockerInventoryId));
         }
     }
 
     public void updatePlayerMana(int amount) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            StatsData stats = playerData.getStats();
-            stats.setCurrentMana(stats.getCurrentMana() + amount);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.getStats().setCurrentMana(playerData.getStats().getCurrentMana() + amount));
         }
     }
 
     public void updatePlayerForageExperience(int amount) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            StatsData stats = playerData.getStats();
-            stats.setForaging(stats.getForaging() + amount);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.getStats().setForaging(playerData.getStats().getForaging() + amount));
         }
     }
 
     public void addCoolDown(CoolDownType coolDownType) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.createCoolDown(coolDownType);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.createCoolDown(coolDownType));
         }
     }
 
-    public void addCoolDown(CoolDownPojo coolDown) {
+    public void addCoolDown(CoolDown coolDown) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.createCoolDown(coolDown);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.createCoolDown(coolDown));
         }
     }
 
     public Set<? extends CoolDown> getCoolDowns() {
-        synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return Sets.newHashSet();
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            return playerData.getCoolDowns();
-        }
+        return this.transactRead(playerDate -> {
+            Set<CoolDownData> coolDowns = playerDate.getCoolDowns();
+            Set<CoolDown> pojos = new HashSet<CoolDown>();
+            for(CoolDownData coolDown : coolDowns)
+                pojos.add(CoolDownData.copyCoolDown(coolDown));
+            return Collections.unmodifiableSet(pojos);
+        });
     }
 
     public boolean isActiveCoolDown() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return false;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            return playerData.getCoolDowns().size() > 0;
+            return this.transactRead(playerData -> playerData.getCoolDowns().size() > 0);
         }
     }
 
@@ -662,60 +512,50 @@ public class Player extends AetherMudEntity {
 
     public boolean isActive(CoolDownType coolDownType) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return false;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            Set<CoolDownData> coolDowns = playerData.getCoolDowns();
-            for (CoolDownData c : coolDowns) {
-                if (c.getCoolDownType() != null && c.getCoolDownType().equals(coolDownType)) {
-                    if (c.isActive()) {
-                        return true;
+            return this.transactRead( playerData -> {
+                Set<CoolDownData> coolDowns = playerData.getCoolDowns();
+                for (CoolDownData c : coolDowns) {
+                    if (c.getCoolDownType() != null && c.getCoolDownType().equals(coolDownType)) {
+                        if (c.isActive()) {
+                            return true;
+                        }
                     }
                 }
-            }
+                return false;
+            });
         }
-        return false;
     }
 
     public boolean isActiveSpellCoolDown(String spellName) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return false;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            Set<CoolDownData> coolDowns = playerData.getCoolDowns();
-            for (CoolDownData coolDown : coolDowns) {
-                if (coolDown.getName().equalsIgnoreCase(spellName)) {
-                    return true;
+            return this.transactRead(playerData -> {
+                Set<CoolDownData> coolDowns = playerData.getCoolDowns();
+                for (CoolDownData coolDown : coolDowns) {
+                    if (coolDown.getName().equalsIgnoreCase(spellName)) {
+                        return true;
+                    }
                 }
-            }
-            return false;
+                return false;
+            });
         }
     }
 
     private void tickAllActiveCoolDowns() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            Set<CoolDownData> coolDowns = playerData.getCoolDowns();
-            for(final CoolDownData coolDown : coolDowns) {
-                if (coolDown.isActive()) {
-                    coolDown.decrementTick();
-                } else {
-                    if (coolDown.equals(CoolDownType.DEATH)) {
-                        gameManager.getChannelUtils().write(playerId, "You have risen from the dead.\r\n");
-                    }
+            this.consume(playerData -> {
+                Set<CoolDownData> coolDowns = playerData.getCoolDowns();
+                for(final CoolDownData coolDown : coolDowns) {
+                    if (coolDown.isActive()) {
+                        coolDown.decrementTick();
+                    } else {
+                        if (coolDown.equals(CoolDownType.DEATH)) {
+                            gameManager.getChannelUtils().write(playerId, "You have risen from the dead.\r\n");
+                        }
 
-                    playerData.removeCoolDown(coolDown);
+                        playerData.removeCoolDown(coolDown);
+                    }
                 }
-            }
-            savePlayerMetadata();
+            });
         }
     }
 
@@ -745,15 +585,16 @@ public class Player extends AetherMudEntity {
     }
 
     public Room getCurrentRoom() {
-        Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-        if (currentRoom == null && playerMetadataOptional.isPresent()) {
-            PlayerData playerData = playerMetadataOptional.get();
-            Integer currentRoomId = playerData.getCurrentRoomId();
-            if (currentRoomId != null) {
-                this.currentRoom = gameManager.getRoomManager().getRoom(currentRoomId);
+        return this.transactRead(playerData -> {
+            if (currentRoom == null) {
+                Integer currentRoomId = playerData.getCurrentRoomId();
+                if (currentRoomId != null) {
+                    this.currentRoom = gameManager.getRoomManager().getRoom(currentRoomId);
+                }
             }
-        }
-        return currentRoom;
+            return currentRoom;
+        });
+
     }
 
     public void setCurrentRoomAndPersist(Room currentRoom) {
@@ -761,13 +602,7 @@ public class Player extends AetherMudEntity {
         setCurrentRoom(currentRoom);
         gameManager.getEventProcessor().addEvent(() -> {
             synchronized (interner.intern(playerId)) {
-                Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-                if (!playerMetadataOptional.isPresent()) {
-                    return;
-                }
-                PlayerData playerData = playerMetadataOptional.get();
-                playerData.setCurrentRoomId(currentRoom.getRoomId());
-                savePlayerMetadata();
+                this.consume(playerData -> playerData.setCurrentRoomId(currentRoom.getRoomId()));
             }
         });
     }
@@ -777,14 +612,11 @@ public class Player extends AetherMudEntity {
     }
 
     public Map<String, Long> getNpcKillLog() {
-        ImmutableMap.Builder<String, Long> builder = ImmutableMap.builder();
-        Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-        if (!playerMetadataOptional.isPresent()) {
-            return Maps.newHashMap();
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        playerData.getNpcKillLog().forEach(builder::put);
-        return builder.build();
+        return this.transactRead(playerData -> {
+            ImmutableMap.Builder<String, Long> builder = ImmutableMap.builder();
+            playerData.getNpcKillLog().forEach(builder::put);
+            return builder.build();
+        });
     }
 
     public void removePlayerFromRoom(Room room) {
@@ -866,12 +698,9 @@ public class Player extends AetherMudEntity {
 
     public Optional<ItemPojo> getInventoryItem(String itemKeyword) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return Optional.empty();
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            for (String itemId : playerData.getInventory()) {
+            final List<String> inventory = new ArrayList<>();
+            this.consumeRead(playerData -> inventory.addAll(playerData.getInventory()));
+            for (String itemId : inventory) {
                 Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
                 if (!itemOptional.isPresent()) {
                     continue;
@@ -932,25 +761,22 @@ public class Player extends AetherMudEntity {
 
     public List<ItemPojo> getLockerInventory() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptonal = getPlayerMetadata();
-            if (!playerMetadataOptonal.isPresent()) {
-                return Lists.newArrayList();
-            }
-            PlayerData playerData = playerMetadataOptonal.get();
-            List<ItemPojo> inventoryItems = Lists.newArrayList();
-            List<String> inventory = playerData.getLockerInventory();
-            if (inventory != null) {
-                for (String itemId : inventory) {
-                    Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
-                    if (!itemOptional.isPresent()) {
-                        log.info("Orphaned inventoryId:" + itemId + " player: " + getPlayerName());
-                        continue;
+            return this.transactRead(playerData -> {
+                List<ItemPojo> inventoryItems = Lists.newArrayList();
+                List<String> inventory = playerData.getLockerInventory();
+                if (inventory != null) {
+                    for (String itemId : inventory) {
+                        Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                        if (!itemOptional.isPresent()) {
+                            log.info("Orphaned inventoryId:" + itemId + " player: " + getPlayerName());
+                            continue;
+                        }
+                        inventoryItems.add(itemOptional.get());
                     }
-                    inventoryItems.add(itemOptional.get());
                 }
-            }
-            inventoryItems.sort(Comparator.comparing(ItemPojo::getItemName));
-            return inventoryItems;
+                inventoryItems.sort(Comparator.comparing(ItemPojo::getItemName));
+                return inventoryItems;
+            });
         }
     }
 
@@ -997,48 +823,42 @@ public class Player extends AetherMudEntity {
 
     public List<ItemPojo> getInventory() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return Lists.newArrayList();
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            List<ItemPojo> inventoryItems = Lists.newArrayList();
-            List<String> inventory = playerData.getInventory();
-            if (inventory != null) {
-                for (String itemId : inventory) {
-                    Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
-                    if (!itemOptional.isPresent()) {
-                        log.info("Orphaned inventoryId:" + itemId + " player: " + getPlayerName());
-                        continue;
+            return this.transactRead(playerData -> {
+                List<ItemPojo> inventoryItems = Lists.newArrayList();
+                List<String> inventory = playerData.getInventory();
+                if (inventory != null) {
+                    for (String itemId : inventory) {
+                        Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                        if (!itemOptional.isPresent()) {
+                            log.info("Orphaned inventoryId:" + itemId + " player: " + getPlayerName());
+                            continue;
+                        }
+                        inventoryItems.add(itemOptional.get());
                     }
-                    inventoryItems.add(itemOptional.get());
                 }
-            }
-            inventoryItems.sort(Comparator.comparing(ItemPojo::getItemName));
-            return inventoryItems;
+                inventoryItems.sort(Comparator.comparing(ItemPojo::getItemName));
+                return inventoryItems;
+            });
         }
     }
 
     public Set<ItemPojo> getEquipment() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return Sets.newHashSet();
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            Set<ItemPojo> equipmentItems = Sets.newHashSet();
-            List<String> equipment = playerData.getPlayerEquipment();
-            if (equipment != null) {
-                for (String itemId : equipment) {
-                    Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
-                    if (!itemOptional.isPresent()) {
-                        log.info("Orphaned equipmentId:" + itemId + " player: " + getPlayerName());
-                        continue;
+            return this.transactRead(playerData -> {
+                Set<ItemPojo> equipmentItems = Sets.newHashSet();
+                List<String> equipment = playerData.getPlayerEquipment();
+                if (equipment != null) {
+                    for (String itemId : equipment) {
+                        Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                        if (!itemOptional.isPresent()) {
+                            log.info("Orphaned equipmentId:" + itemId + " player: " + getPlayerName());
+                            continue;
+                        }
+                        equipmentItems.add(itemOptional.get());
                     }
-                    equipmentItems.add(itemOptional.get());
                 }
-            }
-            return equipmentItems;
+                return equipmentItems;
+            });
         }
     }
 
@@ -1062,26 +882,23 @@ public class Player extends AetherMudEntity {
     }
 
     public Optional<ItemPojo> getSlotItem(EquipmentSlotType slot) {
-        Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-        if (!playerMetadataOptional.isPresent()) {
-            return Optional.empty();
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        if (playerData.getPlayerEquipment() == null) {
-            return Optional.empty();
-        }
-        for (String item : playerData.getPlayerEquipment()) {
-            Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(item);
-            if (!itemOptional.isPresent()) {
-                continue;
+        return this.transactRead(playerData -> {
+            if (playerData.getPlayerEquipment() == null) {
+                return Optional.empty();
             }
-            ItemPojo itemEntity = itemOptional.get();
-            EquipmentSlotType equipmentSlotType = itemEntity.getEquipment().getEquipmentSlotType();
-            if (equipmentSlotType.equals(slot)) {
-                return Optional.of(itemEntity);
+            for (String item : playerData.getPlayerEquipment()) {
+                Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(item);
+                if (!itemOptional.isPresent()) {
+                    continue;
+                }
+                ItemPojo itemEntity = itemOptional.get();
+                EquipmentSlotType equipmentSlotType = itemEntity.getEquipment().getEquipmentSlotType();
+                if (equipmentSlotType.equals(slot)) {
+                    return Optional.of(itemEntity);
+                }
             }
-        }
-        return Optional.empty();
+            return Optional.empty();
+        });
     }
 
     public boolean unEquip(ItemPojo item) {
@@ -1097,25 +914,13 @@ public class Player extends AetherMudEntity {
 
     public void addEquipmentId(String equipmentId) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.addEquipmentEntityId(equipmentId);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.addEquipmentEntityId(equipmentId));
         }
     }
 
     public void removeEquipmentId(String equipmentId) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.removeEquipmentEntityId(equipmentId);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.removeEquipmentEntityId(equipmentId));
         }
     }
 
@@ -1135,14 +940,12 @@ public class Player extends AetherMudEntity {
         sb.append(buildEquipmentString()).append("\r\n");
         sb.append(Color.MAGENTA + "Stats--------------------------------" + Color.RESET).append("\r\n");
         sb.append(gameManager.buildLookString(playerName, modifiedStats, diffStats)).append("\r\n");
-        Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-        if (playerMetadataOptional.isPresent()) {
-            PlayerData playerData = playerMetadataOptional.get();
+        this.consumeRead(playerData -> {
             if (playerData.getEffects() != null && playerData.getEffects().size() > 0) {
                 sb.append(Color.MAGENTA + "Effects--------------------------------" + Color.RESET).append("\r\n");
                 sb.append(buldEffectsString()).append("\r\n");
             }
-        }
+        });
         StringBuilder finalString = new StringBuilder();
         Lists.newArrayList(sb.toString().split("[\\r\\n]+")).forEach(s -> finalString.append(AetherMudUtils.trimTrailingBlanks(s)).append("\r\n"));
         return finalString.toString();
@@ -1153,60 +956,48 @@ public class Player extends AetherMudEntity {
             StatsBuilder statsBuilder = new StatsBuilder();
             Stats newStats = statsBuilder.createStats();
 
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return newStats;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
             Stats playerStats = gameManager.getStatsModifierFactory().getStatsModifier(this);
             StatsHelper.combineStats(newStats, playerStats);
-            List<String> playerEquipment = playerData.getPlayerEquipment();
-            if (playerEquipment == null) {
-                return playerStats;
-            }
-            for (String equipId : playerEquipment) {
-                Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(equipId);
-                if (!itemOptional.isPresent()) {
-                    continue;
+            return this.transactRead(playerData -> {
+                List<String> playerEquipment = playerData.getPlayerEquipment();
+                if (playerEquipment == null) {
+                    return playerStats;
                 }
-                ItemPojo itemEntity = itemOptional.get();
-                Equipment equipment = itemEntity.getEquipment();
-                Stats stats = equipment.getStats();
-                StatsHelper.combineStats(newStats, stats);
-            }
-            if (playerData.getEffects() != null) {
-                for (EffectPojo effect : playerData.getEffects()) {
-                    StatsHelper.combineStats(newStats, effect.getDurationStats());
+                for (String equipId : playerEquipment) {
+                    Optional<ItemPojo> itemOptional = gameManager.getEntityManager().getItemEntity(equipId);
+                    if (!itemOptional.isPresent()) {
+                        continue;
+                    }
+                    ItemPojo itemEntity = itemOptional.get();
+                    Equipment equipment = itemEntity.getEquipment();
+                    Stats stats = equipment.getStats();
+                    StatsHelper.combineStats(newStats, stats);
                 }
-            }
-            return newStats;
+                if (playerData.getEffects() != null) {
+                    for (EffectData effect : playerData.getEffects()) {
+                        StatsHelper.combineStats(newStats, StatsData.copyStats(effect.getDurationStats()));
+                    }
+                }
+                return newStats;
+            });
         }
     }
 
     public PlayerClass getPlayerClass() {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return PlayerClass.BASIC;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            PlayerClass playerClass = playerData.getPlayerClass();
-            if (playerClass == null) {
-                return PlayerClass.BASIC;
-            }
-            return playerClass;
+            return this.transactRead(playerData -> {
+                PlayerClass playerClass = playerData.getPlayerClass();
+                if (playerClass == null) {
+                    return PlayerClass.BASIC;
+                }
+                return playerClass;
+            });
         }
     }
 
     public void setPlayerClass(PlayerClass playerClass) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.setPlayerClass(playerClass);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.setPlayerClass(playerClass));
         }
     }
 
@@ -1228,16 +1019,13 @@ public class Player extends AetherMudEntity {
         return t.render();
     }
 
-    /* FIGHT FIGHT FIGHT FIGHT */
-
     public String buldEffectsString() {
-        Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-        if (!playerMetadataOptional.isPresent()) {
-            return "";
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        List<EffectPojo> effects = playerData.getEffects();
-        return gameManager.renderEffectsString(effects);
+        return this.transactRead(playerData -> {
+            List<Effect> effects = new ArrayList<Effect>();
+            for(EffectData effect : playerData.getEffects())
+                effects.add(EffectData.copyEffect(effect));
+            return gameManager.renderEffectsString(effects);
+        });
     }
 
     private String capitalize(final String line) {
@@ -1255,37 +1043,23 @@ public class Player extends AetherMudEntity {
     }
 
     public boolean setPlayerSetting(String key, String value) {
-        boolean success;
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return false;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            success = playerData.setSetting(key, value);
-            savePlayerMetadata();
+            return this.transact(playerData -> playerData.setSetting(key, value));
         }
-        return success;
     }
 
     public Optional<String> getPlayerSetting(String key) {
-        return getPlayerMetadata().flatMap(playerMetadata -> Optional.ofNullable(playerMetadata.getSetting(key)));
+        return this.transactRead(playerData -> Optional.ofNullable(playerData.getSetting(key)));
     }
 
     public void removePlayerSetting(String key) {
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = getPlayerMetadata();
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.deleteSetting(key);
-            savePlayerMetadata();
+            this.consume(playerData -> playerData.deleteSetting(key));
         }
     }
 
     public Map<String, String> getPlayerSettings() {
-        return getPlayerMetadata().map(PlayerData::getPlayerSettings).orElseGet(Maps::newHashMap);
+        return this.transactRead(playerData -> new HashMap<>(playerData.getPlayerSettings()));
     }
 
     public boolean addActiveFight(NpcSpawn npcSpawn) {
@@ -1480,4 +1254,28 @@ public class Player extends AetherMudEntity {
     public boolean isChatModeOn() {
         return isChatMode.get();
     }
+
+    public Set<PlayerRole> getRoles() {
+        return this.transactRead(playerData -> new HashSet<PlayerRole>(playerData.getPlayerRoles()));
+    }
+
+    public StatsPojo getStats() {
+        return this.transactRead(playerData -> StatsData.copyStats(playerData.getStats()));
+    }
+
+    private <T> T transact(Function<PlayerData, T> func) {
+        return PlayerUtil.transact(this.gameManager, this.playerId, func);
+    }
+
+    private void consume(Consumer<PlayerData> func) {
+        PlayerUtil.consume(this.gameManager, this.playerId, func);
+    }
+
+    private <T> T transactRead(Function<PlayerData, T> func) {
+        return PlayerUtil.transactRead(this.gameManager, this.playerId, func);
+    }
+
+    private void consumeRead(Consumer<PlayerData> func) {
+        PlayerUtil.consumeRead(this.gameManager, this.playerId, func);
+    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/player/PlayerManagement.java b/src/main/java/com/syncleus/aethermud/player/PlayerManagement.java
index f66259183275e8f4abe74eb9adde9b9443995571..f402fd13da3c0bc91f4c3e6755b9946e3654d2bb 100644
--- a/src/main/java/com/syncleus/aethermud/player/PlayerManagement.java
+++ b/src/main/java/com/syncleus/aethermud/player/PlayerManagement.java
@@ -15,6 +15,7 @@
  */
 package com.syncleus.aethermud.player;
 
+import com.google.common.base.Function;
 import com.google.common.collect.Sets;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.ItemPojo;
@@ -26,9 +27,10 @@ import com.google.api.client.util.Maps;
 import com.google.common.base.Joiner;
 import com.google.common.collect.Interner;
 import com.google.common.collect.Interners;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 
 import java.util.*;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 public class PlayerManagement implements PlayerManagementMBean {
@@ -45,134 +47,75 @@ public class PlayerManagement implements PlayerManagementMBean {
     public void setMarkForDelete(boolean isMark) {
         Interner<String> interner = findInterner();
         synchronized (interner.intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.setIsMarkedForDelete(isMark);
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.setIsMarkedForDelete(isMark));
         }
     }
 
     @Override
     public boolean getMarkForDelete() {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return false;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        return playerData.isMarkedForDelete();
+        return this.transactRead(playerData -> playerData.isMarkedForDelete());
     }
 
     @Override
     public int getGold() {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return 0;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        return playerData.getGold();
+        return this.transactRead(playerData -> playerData.getGold());
     }
 
     @Override
     public int getGoldInBankAmount() {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return 0;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        return playerData.getGoldInBank();
+        return this.transactRead(playerData -> playerData.getGoldInBank());
     }
 
     @Override
     public void setGoldInBankAmount(int amt) {
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()){
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.setGoldInBank(amt);
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.setGoldInBank(amt));
         }
     }
 
     @Override
     public void setGold(int amt) {
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()){
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.setGold(amt);
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.setGold(amt));
         }
     }
 
     @Override
     public void setHealth(int amt) {
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()){
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.getStats().setCurrentHealth(amt);
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.getStats().setCurrentHealth(amt));
         }
     }
 
     @Override
     public String getPassword() {
-        StringBuilder shadowPwd = new StringBuilder();
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return "";
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        String password = playerData.getPassword();
-        for (int i = 0; i < password.length(); i++) {
-            shadowPwd.append("*");
-        }
-        return shadowPwd.toString();
+        return this.transactRead(playerData -> {
+            StringBuilder shadowPwd = new StringBuilder();
+            String password = playerData.getPassword();
+            for (int i = 0; i < password.length(); i++) {
+                shadowPwd.append("*");
+            }
+            return shadowPwd.toString();
+        });
     }
 
     @Override
     public void setPassword(String password) {
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()){
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.setPassword(password);
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.setPassword(password));
         }
     }
 
     @Override
     public void setMana(int amt) {
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()){
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.getStats().setCurrentMana(amt);
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.getStats().setCurrentMana(amt));
         }
     }
 
     @Override
     public int getHealth() {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return 0;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        return playerData.getStats().getCurrentHealth();
+        return this.transactRead(playerData -> playerData.getStats().getCurrentHealth());
     }
 
     @Override
@@ -182,117 +125,88 @@ public class PlayerManagement implements PlayerManagementMBean {
 
     @Override
     public int getMana() {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return 0;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        return playerData.getStats().getCurrentMana();
+        return this.transactRead(playerData -> playerData.getStats().getCurrentMana());
     }
 
     @Override
     public void setExperience(int amt) {
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()){
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.getStats().setExperience(amt);
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.getStats().setExperience(amt));
         }
     }
 
     @Override
     public int getExperience() {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return 0;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        return playerData.getStats().getExperience();
+        return this.transactRead(playerData -> playerData.getStats().getExperience());
     }
 
     @Override
     public void setRoles(String roles) {
         String[] split = roles.split(",");
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()){
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.resetPlayerRoles();
-            for (String roleType : split) {
-                PlayerRole byType = PlayerRole.getByType(roleType);
-                if (byType == null) {
-                    continue;
+            this.consume(playerData -> {
+                playerData.resetPlayerRoles();
+                for (String roleType : split) {
+                    PlayerRole byType = PlayerRole.getByType(roleType);
+                    if (byType == null) {
+                        continue;
+                    }
+                    playerData.addPlayerRole(byType);
                 }
-                playerData.addPlayerRole(byType);
-            }
-            gameManager.getPlayerManager().persist();
+            });
         }
     }
 
     @Override
     public String getRoles() {
-        List<String> rolesList = Lists.newArrayList();
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return "";
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        Set<PlayerRole> playerRoleSet = Sets.newHashSet(playerData.getPlayerRoles());
-        for (PlayerRole next : playerRoleSet) {
-            rolesList.add(next.getRoleType());
-        }
-        return Joiner.on(",").join(rolesList);
+        return this.transactRead(playerData -> {
+            List<String> rolesList = Lists.newArrayList();
+            Set<PlayerRole> playerRoleSet = Sets.newHashSet(playerData.getPlayerRoles());
+            for (PlayerRole next : playerRoleSet) {
+                rolesList.add(next.getRoleType());
+            }
+            return Joiner.on(",").join(rolesList);
+        });
     }
 
     @Override
     public Map<String, String> getInventory() {
-        Map<String, String> inventoryContents = Maps.newHashMap();
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return inventoryContents;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        List<String> inventory = playerData.getInventory();
-        for (String itemId : inventory) {
-            Optional<ItemPojo> itemEntityOptional = gameManager.getEntityManager().getItemEntity(itemId);
-            if (!itemEntityOptional.isPresent()) {
-                continue;
-            }
-            ItemPojo itemEntity = itemEntityOptional.get();
-            String itemName = itemEntity.getItemName();
-            final String msgWithoutColorCodes =
+        return this.transactRead(playerData -> {
+            Map<String, String> inventoryContents = Maps.newHashMap();
+            List<String> inventory = playerData.getInventory();
+            for (String itemId : inventory) {
+                Optional<ItemPojo> itemEntityOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                if (!itemEntityOptional.isPresent()) {
+                    continue;
+                }
+                ItemPojo itemEntity = itemEntityOptional.get();
+                String itemName = itemEntity.getItemName();
+                final String msgWithoutColorCodes =
                     itemName.replaceAll("\u001B\\[[;\\d]*m", "");
-            inventoryContents.put(itemEntity.getItemId(), msgWithoutColorCodes);
-        }
-        return inventoryContents;
+                inventoryContents.put(itemEntity.getItemId(), msgWithoutColorCodes);
+            }
+            return inventoryContents;
+        });
     }
 
     @Override
     public Map<String, String> getLockerInventory() {
-        Map<String, String> inventoryContents = Maps.newHashMap();
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()){
-            return inventoryContents;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        List<String> inventory = playerData.getLockerInventory();
-        for (String itemId : inventory) {
-            Optional<ItemPojo> itemEntityOptional = gameManager.getEntityManager().getItemEntity(itemId);
-            if (!itemEntityOptional.isPresent()) {
-                continue;
-            }
-            ItemPojo itemEntity = itemEntityOptional.get();
-            String itemName = itemEntity.getItemName();
-            final String msgWithoutColorCodes =
+        return this.transactRead(playerData -> {
+            Map<String, String> inventoryContents = Maps.newHashMap();
+            List<String> inventory = playerData.getLockerInventory();
+            for (String itemId : inventory) {
+                Optional<ItemPojo> itemEntityOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                if (!itemEntityOptional.isPresent()) {
+                    continue;
+                }
+                ItemPojo itemEntity = itemEntityOptional.get();
+                String itemName = itemEntity.getItemName();
+                final String msgWithoutColorCodes =
                     itemName.replaceAll("\u001B\\[[;\\d]*m", "");
-            inventoryContents.put(itemEntity.getItemId(), msgWithoutColorCodes);
-        }
-        return inventoryContents;
+                inventoryContents.put(itemEntity.getItemId(), msgWithoutColorCodes);
+            }
+            return inventoryContents;
+        });
     }
 
     @Override
@@ -304,13 +218,7 @@ public class PlayerManagement implements PlayerManagementMBean {
         ItemPojo item = new ItemBuilder().from(itemMetadata.get()).create();
         gameManager.getEntityManager().saveItem(item);
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()){
-                return "";
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.addInventoryEntityId(item.getItemId());
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.addInventoryEntityId(item.getItemId()));
         }
         final String msgWithoutColorCodes = item.getItemName().replaceAll("\u001B\\[[;\\d]*m", "");
         return msgWithoutColorCodes + " created.";
@@ -323,38 +231,20 @@ public class PlayerManagement implements PlayerManagementMBean {
             return;
         }
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.setPlayerClass(collect.get(0));
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.setPlayerClass(collect.get(0)));
         }
 
     }
 
     @Override
     public String getPlayerClass() {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-        if (!playerMetadataOptional.isPresent()) {
-            return "";
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        return playerData.getPlayerClass().getIdentifier();
+        return this.transact(playerData -> playerData.getPlayerClass().getIdentifier());
     }
 
     @Override
     public void clearAllCoolDowns() {
         synchronized (findInterner().intern(playerId)) {
-            Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(playerId);
-            if (!playerMetadataOptional.isPresent()) {
-                return;
-            }
-            PlayerData playerData = playerMetadataOptional.get();
-            playerData.resetCoolDowns();
-
-            gameManager.getPlayerManager().persist();
+            this.consume(playerData -> playerData.resetCoolDowns());
         }
     }
 
@@ -375,4 +265,20 @@ public class PlayerManagement implements PlayerManagementMBean {
         }
         return interner;
     }
+
+    private <T> T transact(Function<PlayerData, T> func) {
+        return PlayerUtil.transact(this.gameManager, this.playerId, func);
+    }
+
+    private void consume(Consumer<PlayerData> func) {
+        PlayerUtil.consume(this.gameManager, this.playerId, func);
+    }
+
+    private <T> T transactRead(Function<PlayerData, T> func) {
+        return PlayerUtil.transactRead(this.gameManager, this.playerId, func);
+    }
+
+    private void consumeRead(Consumer<PlayerData> func) {
+        PlayerUtil.consumeRead(this.gameManager, this.playerId, func);
+    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/player/PlayerManagementManager.java b/src/main/java/com/syncleus/aethermud/player/PlayerManagementManager.java
index 212e77d33123bdc042129d5b6a0b01b51bcd6eee..2c9858c41dd2791fec61d2c3e15c1bfcb412f43d 100644
--- a/src/main/java/com/syncleus/aethermud/player/PlayerManagementManager.java
+++ b/src/main/java/com/syncleus/aethermud/player/PlayerManagementManager.java
@@ -16,7 +16,8 @@
 package com.syncleus.aethermud.player;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import org.apache.log4j.Logger;
 
 import javax.management.*;
@@ -35,30 +36,34 @@ public class PlayerManagementManager {
     }
 
     public void createAndRegisterAllPlayerManagementMBeans() throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException {
-        Set<Map.Entry<String, PlayerData>> entrySet = gameManager.getPlayerManager().getPlayerMetadataStore().entrySet();
-        for (Map.Entry<String, PlayerData> entry : entrySet) {
-            registerPlayer(entry.getValue().getPlayerName(), entry.getValue().getPlayerId(), gameManager);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            Set<Map.Entry<String, PlayerData>> entrySet = tx.getStorage().getAllPlayerMetadata().entrySet();
+            for (Map.Entry<String, PlayerData> entry : entrySet) {
+                registerPlayer(entry.getValue().getPlayerName(), entry.getValue().getPlayerId(), gameManager);
+            }
         }
     }
 
     public void processPlayersMarkedForDeletion(){
-        Set<Map.Entry<String, PlayerData>> entrySet = gameManager.getPlayerManager().getPlayerMetadataStore().entrySet();
-        for (Map.Entry<String, PlayerData> entry : entrySet) {
-            String playerId = entry.getKey();
-            PlayerData playerData = entry.getValue();
-            if (playerData.isMarkedForDelete()) {
-                if (playerData.getInventory() != null) {
-                    for (String itemId : playerData.getInventory()) {
-                        gameManager.getEntityManager().removeItem(itemId);
-                        log.info("Removed itemId from " + playerData.getPlayerName() + "'s inventory: " + itemId);
-                    }
-                    for (String itemId : playerData.getLockerInventory()) {
-                        gameManager.getEntityManager().removeItem(itemId);
-                        log.info("Removed itemId from " + playerData.getPlayerName() + "'s locker inventory: " + itemId);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            Set<Map.Entry<String, PlayerData>> entrySet = tx.getStorage().getAllPlayerMetadata().entrySet();
+            for (Map.Entry<String, PlayerData> entry : entrySet) {
+                String playerId = entry.getKey();
+                PlayerData playerData = entry.getValue();
+                if (playerData.isMarkedForDelete()) {
+                    if (playerData.getInventory() != null) {
+                        for (String itemId : playerData.getInventory()) {
+                            gameManager.getEntityManager().removeItem(itemId);
+                            log.info("Removed itemId from " + playerData.getPlayerName() + "'s inventory: " + itemId);
+                        }
+                        for (String itemId : playerData.getLockerInventory()) {
+                            gameManager.getEntityManager().removeItem(itemId);
+                            log.info("Removed itemId from " + playerData.getPlayerName() + "'s locker inventory: " + itemId);
+                        }
                     }
-                }
-                    gameManager.getPlayerManager().getPlayerMetadataStore().remove(playerId);
+                    tx.getStorage().getAllPlayerMetadata().get(playerId).remove();
                     log.info(playerData.getPlayerName() + " has been removed from the game.");
+                }
             }
         }
     }
diff --git a/src/main/java/com/syncleus/aethermud/player/PlayerManager.java b/src/main/java/com/syncleus/aethermud/player/PlayerManager.java
index f08dee6156c5c4e7b794a59efc2aa26b66afd1bc..10f623ee869cff89abe0ea63b4c5b19b3d4884e3 100644
--- a/src/main/java/com/syncleus/aethermud/player/PlayerManager.java
+++ b/src/main/java/com/syncleus/aethermud/player/PlayerManager.java
@@ -20,28 +20,26 @@ import com.codahale.metrics.Gauge;
 import com.google.common.collect.Sets;
 import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.SessionManager;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.StatsData;
 import com.syncleus.aethermud.storage.AetherMudStorage;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.world.model.Room;
 import org.apache.commons.codec.binary.Base64;
 
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 
 import static com.codahale.metrics.MetricRegistry.name;
 
 public class PlayerManager {
 
-    private final AetherMudStorage aetherMudStorage;
+    private final GraphStorageFactory graphStorageFactory;
     private final SessionManager sessionManager;
     private ConcurrentHashMap<String, Player> players = new ConcurrentHashMap<>();
 
-    public PlayerManager(AetherMudStorage aetherMudStorage, SessionManager sessionManager) {
-        this.aetherMudStorage = aetherMudStorage;
+    public PlayerManager(GraphStorageFactory graphStorageFactory, SessionManager sessionManager) {
+        this.graphStorageFactory = graphStorageFactory;
         this.sessionManager = sessionManager;
     }
 
@@ -49,14 +47,6 @@ public class PlayerManager {
         return sessionManager;
     }
 
-    public PlayerData newPlayerData() {
-        return aetherMudStorage.newPlayerData();
-    }
-
-    public void persist() {
-        this.aetherMudStorage.persist();
-    }
-
     public Player addPlayer(Player player) {
         return players.put(player.getPlayerId(), player);
     }
@@ -100,77 +90,54 @@ public class PlayerManager {
     }
 
     public boolean hasRole(Player player, PlayerRole playerRole) {
-        Optional<PlayerData> playerMetadata = getPlayerMetadata(player.getPlayerId());
-        if (!playerMetadata.isPresent()) {
-            return false;
-        }
-        Set<PlayerRole> playerRoleSet = Sets.newHashSet(playerMetadata.get().getPlayerRoles());
-        return playerRoleSet != null && playerMetadata.get().getPlayerRoles().contains(playerRole);
-    }
-
-    public Optional<PlayerData> getPlayerMetadata(String playerId) {
-        return aetherMudStorage.getPlayerMetadata(playerId);
+        return player.getRoles().contains(playerRole);
     }
 
     public boolean hasAnyOfRoles(Player player, Set<PlayerRole> checkRoles) {
-        Optional<PlayerData> playerMetadata = getPlayerMetadata(player.getPlayerId());
-        if (!playerMetadata.isPresent()) {
-            return false;
-        }
-        Set<PlayerRole> playerRoleSet = Sets.newHashSet(playerMetadata.get().getPlayerRoles());
-        if (playerRoleSet != null) {
-            for (PlayerRole checkRole : checkRoles) {
-                if (playerRoleSet.contains(checkRole)) {
-                    return true;
-                }
-            }
-        } else {
-            return false;
-        }
-        return false;
+        return !Collections.disjoint(player.getRoles(), checkRoles);
     }
 
     public void createAllGauges() {
-        for (Map.Entry<String, PlayerData> next : aetherMudStorage.getAllPlayerMetadata().entrySet()) {
-            createGauges(next.getValue());
+        try( GraphStorageFactory.AetherMudTx tx = this.graphStorageFactory.beginTransaction() ) {
+            for (Map.Entry<String, PlayerData> next : tx.getStorage().getAllPlayerMetadata().entrySet()) {
+                createGauges(next.getValue());
+            }
         }
     }
 
     public void createGauges(final PlayerData playerData) {
         String playerId = playerData.getPlayerId();
         String guageName = name(PlayerManager.class, playerData.getPlayerName(), "gold");
-        if (!doesGaugeExist(guageName)) {
-            Main.metrics.register(guageName,
+        try( GraphStorageFactory.AetherMudTx tx = this.graphStorageFactory.beginTransaction() ) {
+            if (!doesGaugeExist(guageName)) {
+                Main.metrics.register(guageName,
                     (Gauge<Integer>) () -> {
-                        Optional<PlayerData> playerMetadataOpt = aetherMudStorage.getPlayerMetadata(playerId);
+                        Optional<PlayerData> playerMetadataOpt = tx.getStorage().getPlayerMetadata(playerId);
                         return playerMetadataOpt.map(PlayerData::getGold).orElse(0);
                     });
-        }
+            }
 
-        guageName = name(PlayerManager.class, playerData.getPlayerName(), "current-health");
-        if (!doesGaugeExist(guageName)) {
-            Main.metrics.register(name(PlayerManager.class, playerData.getPlayerName(), "current-health"),
+            guageName = name(PlayerManager.class, playerData.getPlayerName(), "current-health");
+            if (!doesGaugeExist(guageName)) {
+                Main.metrics.register(name(PlayerManager.class, playerData.getPlayerName(), "current-health"),
                     (Gauge<Integer>) () -> {
-                        Optional<PlayerData> playerMetadataOpt = aetherMudStorage.getPlayerMetadata(playerId);
+                        Optional<PlayerData> playerMetadataOpt = tx.getStorage().getPlayerMetadata(playerId);
                         return playerMetadataOpt.map(PlayerData::getStats).map(StatsData::getCurrentHealth).orElse(0);
                     });
-        }
+            }
 
-        guageName = name(PlayerManager.class, playerData.getPlayerName(), "xp");
-        if (!doesGaugeExist(guageName)) {
-            Main.metrics.register(name(PlayerManager.class, playerData.getPlayerName(), "xp"),
+            guageName = name(PlayerManager.class, playerData.getPlayerName(), "xp");
+            if (!doesGaugeExist(guageName)) {
+                Main.metrics.register(name(PlayerManager.class, playerData.getPlayerName(), "xp"),
                     (Gauge<Integer>) () -> {
-                        Optional<PlayerData> playerMetadataOpt = aetherMudStorage.getPlayerMetadata(playerId);
+                        Optional<PlayerData> playerMetadataOpt = tx.getStorage().getPlayerMetadata(playerId);
                         return playerMetadataOpt.map(PlayerData::getStats).map(StatsData::getExperience).orElse(0);
                     });
+            }
         }
     }
 
     private boolean doesGaugeExist(String name) {
         return Main.metrics.getGauges().containsKey(name);
     }
-
-    public Map<String, PlayerData> getPlayerMetadataStore() {
-        return aetherMudStorage.getAllPlayerMetadata();
-    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/player/PlayerUtil.java b/src/main/java/com/syncleus/aethermud/player/PlayerUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..0044162d2768301f2d643271470f27bb56a3cc94
--- /dev/null
+++ b/src/main/java/com/syncleus/aethermud/player/PlayerUtil.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2017 Syncleus, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.syncleus.aethermud.player;
+
+
+import com.google.common.base.Function;
+import com.syncleus.aethermud.core.GameManager;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+public final class PlayerUtil {
+    private PlayerUtil() {
+    }
+
+    public static <T> T transact(GameManager manager, String playerId, Function<PlayerData, T> func) {
+        try( GraphStorageFactory.AetherMudTx tx = manager.getGraphStorageFactory().beginTransaction() ) {
+            Optional<PlayerData> playerDataOptional = tx.getStorage().getPlayerMetadata(playerId);
+            if (!playerDataOptional.isPresent())
+                throw new IllegalStateException("Player exists in memory but not in the database");
+            PlayerData playerData = playerDataOptional.get();
+
+            T retVal = func.apply(playerData);
+            tx.success();
+            return retVal;
+        }
+    }
+
+    public static void consume(GameManager manager, String playerId, Consumer<PlayerData> func) {
+        try( GraphStorageFactory.AetherMudTx tx = manager.getGraphStorageFactory().beginTransaction() ) {
+            Optional<PlayerData> playerDataOptional = tx.getStorage().getPlayerMetadata(playerId);
+            if (!playerDataOptional.isPresent())
+                throw new IllegalStateException("Player exists in memory but not in the database");
+            PlayerData playerData = playerDataOptional.get();
+
+            func.accept(playerData);
+            tx.success();
+        }
+    }
+
+    public static <T> T transactRead(GameManager manager, String playerId, Function<PlayerData, T> func) {
+        try( GraphStorageFactory.AetherMudTx tx = manager.getGraphStorageFactory().beginTransaction() ) {
+            Optional<PlayerData> playerDataOptional = tx.getStorage().getPlayerMetadata(playerId);
+            if (!playerDataOptional.isPresent())
+                throw new IllegalStateException("Player exists in memory but not in the database");
+            PlayerData playerData = playerDataOptional.get();
+
+            return func.apply(playerData);
+        }
+    }
+
+    public static void consumeRead(GameManager manager, String playerId, Consumer<PlayerData> func) {
+        try( GraphStorageFactory.AetherMudTx tx = manager.getGraphStorageFactory().beginTransaction() ) {
+            Optional<PlayerData> playerDataOptional = tx.getStorage().getPlayerMetadata(playerId);
+            if (!playerDataOptional.isPresent())
+                throw new IllegalStateException("Player exists in memory but not in the database");
+            PlayerData playerData = playerDataOptional.get();
+
+            func.accept(playerData);
+        }
+    }
+}
diff --git a/src/main/java/com/syncleus/aethermud/server/auth/GameAuth.java b/src/main/java/com/syncleus/aethermud/server/auth/GameAuth.java
index 6b07db21af8ccc96c9c8c5eb5911476f4e623bd1..07b9303f71a319e8221b8149370fdbcf85c95edf 100644
--- a/src/main/java/com/syncleus/aethermud/server/auth/GameAuth.java
+++ b/src/main/java/com/syncleus/aethermud/server/auth/GameAuth.java
@@ -18,7 +18,9 @@ package com.syncleus.aethermud.server.auth;
 import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.world.model.Room;
 import org.jboss.netty.channel.Channel;
 
@@ -34,14 +36,17 @@ public class GameAuth implements AetherMudAuthenticator {
 
     @Override
     public boolean authenticateAndRegisterPlayer(String username, String password, Channel channel) {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(Main.createPlayerId(username));
-        if (!playerMetadataOptional.isPresent()) {
-            return false;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        if (!playerData.getPassword().equals(password)) {
-            return false;
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            Optional<PlayerData> playerMetadataOptional = tx.getStorage().getPlayerMetadata(Main.createPlayerId(username));
+            if (!playerMetadataOptional.isPresent()) {
+                return false;
+            }
+            PlayerData playerData = playerMetadataOptional.get();
+            if (!playerData.getPassword().equals(password)) {
+                return false;
+            }
         }
+
         Room currentRoom = null;
         if (gameManager.getPlayerManager().doesPlayerExist(username)) {
             currentRoom = gameManager.getPlayerManager().getPlayerByUsername(username).getCurrentRoom();
diff --git a/src/main/java/com/syncleus/aethermud/server/auth/GameAuthorizationAndRegistration.java b/src/main/java/com/syncleus/aethermud/server/auth/GameAuthorizationAndRegistration.java
index 78fff7f03795ca9e3e2e0734431e955a9c9c487d..4e10c4a601f1e8b8192ba56cb3df9c0d9716320c 100644
--- a/src/main/java/com/syncleus/aethermud/server/auth/GameAuthorizationAndRegistration.java
+++ b/src/main/java/com/syncleus/aethermud/server/auth/GameAuthorizationAndRegistration.java
@@ -18,7 +18,8 @@ package com.syncleus.aethermud.server.auth;
 import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.world.model.Room;
 import org.jboss.netty.channel.Channel;
 
@@ -34,41 +35,39 @@ public class GameAuthorizationAndRegistration implements AetherMudAuthenticator
 
     @Override
     public boolean authenticateAndRegisterPlayer(String username, String password, Channel channel) {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(Main.createPlayerId(username));
-        if (!playerMetadataOptional.isPresent()) {
-            return false;
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        if (!playerData.getPassword().equals(password)) {
-            return false;
-        }
-        Room currentRoom = null;
-        if (gameManager.getPlayerManager().doesPlayerExist(username)) {
-            currentRoom = gameManager.getPlayerManager().getPlayerByUsername(username).getCurrentRoom();
-            gameManager.getPlayerManager().removePlayer(username);
-        } else {
-            Optional<Room> playerCurrentRoom = gameManager.getRoomManager().getPlayerCurrentRoom(Main.createPlayerId(username));
-            if (playerCurrentRoom.isPresent()) {
-                currentRoom = playerCurrentRoom.get();
+        return PlayerUtil.transact(gameManager, Main.createPlayerId(username), playerData -> {
+            if (!playerData.getPassword().equals(password)) {
+                return false;
             }
-        }
 
-        Player player = new Player(username, gameManager);
-        if (currentRoom != null) {
-            player.setCurrentRoomAndPersist(currentRoom);
-            currentRoom.addPresentPlayer(player.getPlayerId());
-        } else {
-            currentRoom = player.getCurrentRoom();
+            Room currentRoom = null;
+            if (gameManager.getPlayerManager().doesPlayerExist(username)) {
+                currentRoom = gameManager.getPlayerManager().getPlayerByUsername(username).getCurrentRoom();
+                gameManager.getPlayerManager().removePlayer(username);
+            } else {
+                Optional<Room> playerCurrentRoom = gameManager.getRoomManager().getPlayerCurrentRoom(Main.createPlayerId(username));
+                if (playerCurrentRoom.isPresent()) {
+                    currentRoom = playerCurrentRoom.get();
+                }
+            }
+
+            Player player = new Player(username, gameManager);
             if (currentRoom != null) {
+                player.setCurrentRoomAndPersist(currentRoom);
                 currentRoom.addPresentPlayer(player.getPlayerId());
-                player.setCurrentRoom(player.getCurrentRoom());
+            } else {
+                currentRoom = player.getCurrentRoom();
+                if (currentRoom != null) {
+                    currentRoom.addPresentPlayer(player.getPlayerId());
+                    player.setCurrentRoom(player.getCurrentRoom());
+                }
+            }
+            player.setChannel(channel);
+            gameManager.getPlayerManager().addPlayer(player);
+            if (currentRoom == null) {
+                gameManager.placePlayerInLobby(player);
             }
-        }
-        player.setChannel(channel);
-        gameManager.getPlayerManager().addPlayer(player);
-        if (currentRoom == null) {
-            gameManager.placePlayerInLobby(player);
-        }
-        return true;
+            return true;
+        });
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/spawner/ItemSpawner.java b/src/main/java/com/syncleus/aethermud/spawner/ItemSpawner.java
index 85ab9971a5eb6ec509436565db7850f68470f292..523aa6700bfd2c6f774d1e9b97c35ea10d6d6f8e 100644
--- a/src/main/java/com/syncleus/aethermud/spawner/ItemSpawner.java
+++ b/src/main/java/com/syncleus/aethermud/spawner/ItemSpawner.java
@@ -20,11 +20,10 @@ import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.core.SentryManager;
 import com.syncleus.aethermud.entity.AetherMudEntity;
-import com.syncleus.aethermud.entity.EntityManager;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.items.ItemBuilder;
 import com.syncleus.aethermud.items.ItemMetadata;
-import com.syncleus.aethermud.storage.graphdb.ItemData;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
 import com.syncleus.aethermud.world.model.Area;
 import com.syncleus.aethermud.world.model.Room;
 import com.google.common.base.Predicate;
diff --git a/src/main/java/com/syncleus/aethermud/spells/LightningSpell.java b/src/main/java/com/syncleus/aethermud/spells/LightningSpell.java
index 603f8f253c126b0bbf21b06f1a98e2723c6ab65f..b44adbd64341ab377a3f4c72a7798c45ff3094be 100644
--- a/src/main/java/com/syncleus/aethermud/spells/LightningSpell.java
+++ b/src/main/java/com/syncleus/aethermud/spells/LightningSpell.java
@@ -18,7 +18,7 @@ package com.syncleus.aethermud.spells;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.EffectBuilder;
 import com.syncleus.aethermud.npc.NpcSpawn;
-import com.syncleus.aethermud.player.CoolDownPojo;
+import com.syncleus.aethermud.player.CoolDown;
 import com.syncleus.aethermud.player.CoolDownType;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.server.communication.Color;
@@ -55,7 +55,7 @@ public class LightningSpell implements SpellRunnable {
         if (destinationNpc.isPresent()) {
             executeSpellAgainstNpc(sourcePlayer, destinationNpc.get());
             sourcePlayer.updatePlayerMana(-manaCost);
-            sourcePlayer.addCoolDown(new CoolDownPojo(getName(), 5, CoolDownType.SPELL));
+            sourcePlayer.addCoolDown(new CoolDown(getName(), 5, CoolDownType.SPELL));
         }
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/stats/Stats.java b/src/main/java/com/syncleus/aethermud/stats/Stats.java
index 3b5732b84d91fe55a10614e61b87a791e5d98b40..938d3a2ef15e2e72ad9748059686c0b47b988682 100644
--- a/src/main/java/com/syncleus/aethermud/stats/Stats.java
+++ b/src/main/java/com/syncleus/aethermud/stats/Stats.java
@@ -50,7 +50,7 @@ public interface Stats {
 
     void setArmorRating(Integer armorRating);
 
-    Integer getMeleSkill();
+    Integer getMeleeSkill();
 
     void setMeleeSkill(Integer meleSkill);
 
diff --git a/src/main/java/com/syncleus/aethermud/stats/StatsBuilder.java b/src/main/java/com/syncleus/aethermud/stats/StatsBuilder.java
index afdf2e889adfa35cc4f983ae7e223452fb9e5425..7ec8007c06296a3e102f1c06a7d5fc4895cf8510 100644
--- a/src/main/java/com/syncleus/aethermud/stats/StatsBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/stats/StatsBuilder.java
@@ -15,8 +15,6 @@
  */
 package com.syncleus.aethermud.stats;
 
-import com.syncleus.aethermud.storage.graphdb.StatsData;
-
 public class StatsBuilder {
     private int strength;
     private int intelligence;
@@ -47,7 +45,7 @@ public class StatsBuilder {
         this.aim = stats.getAim();
         this.agile = stats.getAgile();
         this.armorRating = stats.getArmorRating();
-        this.meleSkill = stats.getMeleSkill();
+        this.meleSkill = stats.getMeleeSkill();
         this.currentHealth = stats.getCurrentHealth();
         this.maxHealth = stats.getMaxHealth();
         this.weaponRatingMax = stats.getWeaponRatingMax();
diff --git a/src/main/java/com/syncleus/aethermud/stats/StatsHelper.java b/src/main/java/com/syncleus/aethermud/stats/StatsHelper.java
index c73151acc08e2a9330c6876cd3c8412763fdaf74..106ccbbe92e2012eb2fe496fc62c25a109bcec35 100644
--- a/src/main/java/com/syncleus/aethermud/stats/StatsHelper.java
+++ b/src/main/java/com/syncleus/aethermud/stats/StatsHelper.java
@@ -26,7 +26,7 @@ public class StatsHelper {
         statsBuilder.setCurrentHealth(modifiedStats.getCurrentHealth() - origStats.getCurrentHealth());
         statsBuilder.setMaxHealth(modifiedStats.getMaxHealth() - origStats.getMaxHealth());
         statsBuilder.setExperience(modifiedStats.getExperience() - origStats.getExperience());
-        statsBuilder.setMeleSkill(modifiedStats.getMeleSkill() - origStats.getMeleSkill());
+        statsBuilder.setMeleSkill(modifiedStats.getMeleeSkill() - origStats.getMeleeSkill());
         statsBuilder.setNumberOfWeaponRolls(modifiedStats.getNumberOfWeaponRolls() - origStats.getNumberOfWeaponRolls());
         statsBuilder.setStrength(modifiedStats.getStrength() - origStats.getStrength());
         statsBuilder.setWeaponRatingMax(modifiedStats.getWeaponRatingMax() - origStats.getWeaponRatingMax());
@@ -48,7 +48,7 @@ public class StatsHelper {
         orig.setCurrentHealth(orig.getCurrentHealth() + combine.getCurrentHealth());
         orig.setMaxHealth(orig.getMaxHealth() + combine.getMaxHealth());
         orig.setExperience(orig.getExperience() + combine.getExperience());
-        orig.setMeleeSkill(orig.getMeleSkill() + combine.getMeleSkill());
+        orig.setMeleeSkill(orig.getMeleeSkill() + combine.getMeleeSkill());
         orig.setNumberOfWeaponRolls(orig.getNumberOfWeaponRolls() + combine.getNumberOfWeaponRolls());
         orig.setStrength(orig.getStrength() + combine.getStrength());
         orig.setWeaponRatingMax(orig.getWeaponRatingMax() + combine.getWeaponRatingMax());
@@ -69,7 +69,7 @@ public class StatsHelper {
         stats.setCurrentHealth(-stats.getCurrentHealth());
         stats.setMaxHealth(-stats.getMaxHealth());
         stats.setExperience(-stats.getExperience());
-        stats.setMeleeSkill(-stats.getMeleSkill());
+        stats.setMeleeSkill(-stats.getMeleeSkill());
         stats.setNumberOfWeaponRolls(-stats.getNumberOfWeaponRolls());
         stats.setStrength(-stats.getStrength());
         stats.setWeaponRatingMax(-stats.getWeaponRatingMax());
diff --git a/src/main/java/com/syncleus/aethermud/stats/StatsPojo.java b/src/main/java/com/syncleus/aethermud/stats/StatsPojo.java
index f71736fa05a8b5879d1fe3c39309e12e02fc5953..0de25e4b483a1baf2de502ac1d456f16077ddce5 100644
--- a/src/main/java/com/syncleus/aethermud/stats/StatsPojo.java
+++ b/src/main/java/com/syncleus/aethermud/stats/StatsPojo.java
@@ -22,7 +22,7 @@ public class StatsPojo implements Stats {
     private Integer aim;
     private Integer agile;
     private Integer armorRating;
-    private Integer meleSkill;
+    private Integer meleeSkill;
     private Integer currentHealth;
     private Integer maxHealth;
     private Integer weaponRatingMax;
@@ -35,6 +35,10 @@ public class StatsPojo implements Stats {
     private Integer inventorySize;
     private Integer maxEffects;
 
+    public StatsPojo() {
+
+    }
+
     public StatsPojo(Stats stats) {
         this.strength = ( stats == null ? 0 : stats.getStrength());
         this.intelligence = ( stats == null ? 0 : stats.getIntelligence());
@@ -42,7 +46,7 @@ public class StatsPojo implements Stats {
         this.aim = ( stats == null ? 0 : stats.getAim());
         this.agile = ( stats == null ? 0 : stats.getAgile());
         this.armorRating = ( stats == null ? 0 : stats.getArmorRating());
-        this.meleSkill = ( stats == null ? 0 : stats.getMeleSkill());
+        this.meleeSkill = ( stats == null ? 0 : stats.getMeleeSkill());
         this.currentHealth = ( stats == null ? 0 : stats.getCurrentHealth());
         this.maxHealth = ( stats == null ? 0 : stats.getMaxHealth());
         this.weaponRatingMax = ( stats == null ? 0 : stats.getWeaponRatingMax());
@@ -62,7 +66,7 @@ public class StatsPojo implements Stats {
                      Integer aim,
                      Integer agile,
                      Integer armorRating,
-                     Integer meleSkill,
+                     Integer meleeSkill,
                      Integer currentHealth,
                      Integer maxHealth,
                      Integer weaponRatingMax,
@@ -80,7 +84,7 @@ public class StatsPojo implements Stats {
         this.aim = aim;
         this.agile = agile;
         this.armorRating = armorRating;
-        this.meleSkill = meleSkill;
+        this.meleeSkill = meleeSkill;
         this.currentHealth = currentHealth;
         this.maxHealth = maxHealth;
         this.weaponRatingMax = weaponRatingMax;
@@ -159,12 +163,12 @@ public class StatsPojo implements Stats {
         this.armorRating = armorRating;
     }
 
-    public Integer getMeleSkill() {
-        return meleSkill;
+    public Integer getMeleeSkill() {
+        return meleeSkill;
     }
 
     public void setMeleeSkill(Integer meleSkill) {
-        this.meleSkill = meleSkill;
+        this.meleeSkill = meleSkill;
     }
 
     public Integer getCurrentHealth() {
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/BasicPlayerLevelStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/BasicPlayerLevelStatsModifier.java
index 0827f19867ef6ec3d780daf16ce36871a677059a..3a7931e1cec0000928d0417b38a2b6d68fe9d0bc 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/BasicPlayerLevelStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/BasicPlayerLevelStatsModifier.java
@@ -17,14 +17,7 @@ package com.syncleus.aethermud.stats.modifier;
 
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
-import com.syncleus.aethermud.stats.DefaultStats;
-import com.syncleus.aethermud.stats.Levels;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
-import com.syncleus.aethermud.stats.StatsBuilder;
-
-import java.util.Optional;
+import com.syncleus.aethermud.stats.*;
 
 import static java.lang.Math.pow;
 import static java.lang.StrictMath.sqrt;
@@ -94,12 +87,7 @@ public class BasicPlayerLevelStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(player.getPlayerId());
-        if (!playerMetadataOptional.isPresent()) {
-            return DefaultStats.DEFAULT_PLAYER.createStats();
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        StatsData baseStats = playerData.getStats();
+        StatsPojo baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
@@ -109,7 +97,7 @@ public class BasicPlayerLevelStatsModifier implements StatsModifier {
         int newWillpowerRating = getWillpowerForLevel(baseStats.getWillpower(), level);
         int newIntelligenceRating = getIntelligenceForLevel(baseStats.getIntelligence(), level);
         int newAgileRating = getAgileForLevel(baseStats.getAgile(), level);
-        int newMeleRating = getMeleForLevel(baseStats.getMeleSkill(), level);
+        int newMeleRating = getMeleForLevel(baseStats.getMeleeSkill(), level);
         StatsBuilder statsBuilder = new StatsBuilder(baseStats);
         statsBuilder.setMaxHealth(newMaxHealth);
         statsBuilder.setArmorRating(newArmorRating);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/RangerStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/RangerStatsModifier.java
index a380fb0f9656a275b1fa5850ddbe06eb8a0c7193..d3f6a90a8983a3bd59bd9eecd0dcfd2f010c107b 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/RangerStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/RangerStatsModifier.java
@@ -17,14 +17,7 @@ package com.syncleus.aethermud.stats.modifier;
 
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
-import com.syncleus.aethermud.stats.DefaultStats;
-import com.syncleus.aethermud.stats.Levels;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
-import com.syncleus.aethermud.stats.StatsBuilder;
-
-import java.util.Optional;
+import com.syncleus.aethermud.stats.*;
 
 import static java.lang.Math.pow;
 import static java.lang.StrictMath.sqrt;
@@ -94,12 +87,7 @@ public class RangerStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(player.getPlayerId());
-        if (!playerMetadataOptional.isPresent()) {
-            return DefaultStats.DEFAULT_PLAYER.createStats();
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        StatsData baseStats = playerData.getStats();
+        StatsPojo baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
@@ -109,7 +97,7 @@ public class RangerStatsModifier implements StatsModifier {
         int newWillpowerRating = getWillpowerForLevel(baseStats.getWillpower(), level);
         int newIntelligenceRating = getIntelligenceForLevel(baseStats.getIntelligence(), level);
         int newAgileRating = getAgileForLevel(baseStats.getAgile(), level);
-        int newMeleRating = getMeleForLevel(baseStats.getMeleSkill(), level);
+        int newMeleRating = getMeleForLevel(baseStats.getMeleeSkill(), level);
         StatsBuilder statsBuilder = new StatsBuilder(baseStats);
         statsBuilder.setMaxHealth(newMaxHealth);
         statsBuilder.setArmorRating(newArmorRating);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/ShamanStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/ShamanStatsModifier.java
index 70b67f08475b1cd13ee98483897dd41982ea9229..2022d4ffc674ceac7219baf4e8e5d07ff62c623c 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/ShamanStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/ShamanStatsModifier.java
@@ -17,14 +17,7 @@ package com.syncleus.aethermud.stats.modifier;
 
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
-import com.syncleus.aethermud.stats.DefaultStats;
-import com.syncleus.aethermud.stats.Levels;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
-import com.syncleus.aethermud.stats.StatsBuilder;
-
-import java.util.Optional;
+import com.syncleus.aethermud.stats.*;
 
 import static java.lang.Math.pow;
 import static java.lang.StrictMath.sqrt;
@@ -94,12 +87,7 @@ public class ShamanStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(player.getPlayerId());
-        if (!playerMetadataOptional.isPresent()) {
-            return DefaultStats.DEFAULT_PLAYER.createStats();
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        StatsData baseStats = playerData.getStats();
+        StatsPojo baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
@@ -109,7 +97,7 @@ public class ShamanStatsModifier implements StatsModifier {
         int newWillpowerRating = getWillpowerForLevel(baseStats.getWillpower(), level);
         int newIntelligenceRating = getIntelligenceForLevel(baseStats.getIntelligence(), level);
         int newAgileRating = getAgileForLevel(baseStats.getAgile(), level);
-        int newMeleRating = getMeleForLevel(baseStats.getMeleSkill(), level);
+        int newMeleRating = getMeleForLevel(baseStats.getMeleeSkill(), level);
         StatsBuilder statsBuilder = new StatsBuilder(baseStats);
         statsBuilder.setMaxHealth(newMaxHealth);
         statsBuilder.setArmorRating(newArmorRating);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/StatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/StatsModifier.java
index 3009c357de2f261e4527726a1dfb382b3405448e..b7474dfe7f9e2e7da4aad5931639a86ce1929a97 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/StatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/StatsModifier.java
@@ -17,7 +17,6 @@ package com.syncleus.aethermud.stats.modifier;
 
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
 
 @FunctionalInterface
 public interface StatsModifier {
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/StatsModifierFactory.java b/src/main/java/com/syncleus/aethermud/stats/modifier/StatsModifierFactory.java
index b99a8c1eb135ff0feb59b4c5a22cf493b4469397..f75962b7bfd728e38645c461dff753ba5e35c6d5 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/StatsModifierFactory.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/StatsModifierFactory.java
@@ -19,7 +19,6 @@ package com.syncleus.aethermud.stats.modifier;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
 
 public class StatsModifierFactory {
 
@@ -30,7 +29,7 @@ public class StatsModifierFactory {
     }
 
     public Stats getStatsModifier(Player player) {
-        StatsModifier modifer = new BasicPlayerLevelStatsModifier(gameManager);
+        StatsModifier modifer;
         switch (player.getPlayerClass()) {
             case WARRIOR:
                 modifer = new WarriorStatsModifier(gameManager);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/WarriorStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/WarriorStatsModifier.java
index ad68719de114cb2bf5663d2636b1649499fb0a1c..a230aa137d2234f1a425ba1ca2737faa6e455ca9 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/WarriorStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/WarriorStatsModifier.java
@@ -17,14 +17,7 @@ package com.syncleus.aethermud.stats.modifier;
 
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
-import com.syncleus.aethermud.stats.DefaultStats;
-import com.syncleus.aethermud.stats.Levels;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
-import com.syncleus.aethermud.stats.StatsBuilder;
-
-import java.util.Optional;
+import com.syncleus.aethermud.stats.*;
 
 import static java.lang.Math.pow;
 import static java.lang.StrictMath.sqrt;
@@ -94,12 +87,7 @@ public class WarriorStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(player.getPlayerId());
-        if (!playerMetadataOptional.isPresent()) {
-            return DefaultStats.DEFAULT_PLAYER.createStats();
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        StatsData baseStats = playerData.getStats();
+        StatsPojo baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
@@ -109,7 +97,7 @@ public class WarriorStatsModifier implements StatsModifier {
         int newWillpowerRating = getWillpowerForLevel(baseStats.getWillpower(), level);
         int newIntelligenceRating = getIntelligenceForLevel(baseStats.getIntelligence(), level);
         int newAgileRating = getAgileForLevel(baseStats.getAgile(), level);
-        int newMeleRating = getMeleForLevel(baseStats.getMeleSkill(), level);
+        int newMeleRating = getMeleForLevel(baseStats.getMeleeSkill(), level);
         StatsBuilder statsBuilder = new StatsBuilder(baseStats);
         statsBuilder.setMaxHealth(newMaxHealth);
         statsBuilder.setArmorRating(newArmorRating);
diff --git a/src/main/java/com/syncleus/aethermud/stats/modifier/WizardStatsModifier.java b/src/main/java/com/syncleus/aethermud/stats/modifier/WizardStatsModifier.java
index ad6ec92ce58bac8f67a5483dee5c9727e1ba739b..48406ba41b4165d2df4cf946d62c20868c0fc892 100644
--- a/src/main/java/com/syncleus/aethermud/stats/modifier/WizardStatsModifier.java
+++ b/src/main/java/com/syncleus/aethermud/stats/modifier/WizardStatsModifier.java
@@ -17,14 +17,7 @@ package com.syncleus.aethermud.stats.modifier;
 
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.player.Player;
-import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
-import com.syncleus.aethermud.stats.DefaultStats;
-import com.syncleus.aethermud.stats.Levels;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
-import com.syncleus.aethermud.stats.StatsBuilder;
-
-import java.util.Optional;
+import com.syncleus.aethermud.stats.*;
 
 import static java.lang.Math.pow;
 import static java.lang.StrictMath.sqrt;
@@ -95,12 +88,7 @@ public class WizardStatsModifier implements StatsModifier {
 
     @Override
     public Stats modify(Player player) {
-        Optional<PlayerData> playerMetadataOptional = gameManager.getPlayerManager().getPlayerMetadata(player.getPlayerId());
-        if (!playerMetadataOptional.isPresent()) {
-            return DefaultStats.DEFAULT_PLAYER.createStats();
-        }
-        PlayerData playerData = playerMetadataOptional.get();
-        StatsData baseStats = playerData.getStats();
+        StatsPojo baseStats = player.getStats();
         int level = Levels.getLevel(baseStats.getExperience());
         int newMaxHealth = getHealthForLevel(baseStats.getMaxHealth(), level);
         int newArmorRating = getArmorForLevel(baseStats.getArmorRating(), level);
@@ -110,7 +98,7 @@ public class WizardStatsModifier implements StatsModifier {
         int newWillpowerRating = getWillpowerForLevel(baseStats.getWillpower(), level);
         int newIntelligenceRating = getIntelligenceForLevel(baseStats.getIntelligence(), level);
         int newAgileRating = getAgileForLevel(baseStats.getAgile(), level);
-        int newMeleRating = getMeleForLevel(baseStats.getMeleSkill(), level);
+        int newMeleRating = getMeleForLevel(baseStats.getMeleeSkill(), level);
         StatsBuilder statsBuilder = new StatsBuilder(baseStats);
         statsBuilder.setMaxHealth(newMaxHealth);
         statsBuilder.setArmorRating(newArmorRating);
diff --git a/src/main/java/com/syncleus/aethermud/storage/AetherMudStorage.java b/src/main/java/com/syncleus/aethermud/storage/AetherMudStorage.java
index af363fadd4dfe9d0b2d36cceb0dfaee367db1853..6309bec259e987f945d772bcc80bc35336130c02 100644
--- a/src/main/java/com/syncleus/aethermud/storage/AetherMudStorage.java
+++ b/src/main/java/com/syncleus/aethermud/storage/AetherMudStorage.java
@@ -20,15 +20,15 @@ import com.google.common.util.concurrent.Service;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.npc.NpcSpawn;
-import com.syncleus.aethermud.storage.graphdb.ItemData;
-import com.syncleus.aethermud.storage.graphdb.NpcData;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
+import com.syncleus.aethermud.storage.graphdb.model.NpcData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
-public interface AetherMudStorage extends Service {
+public interface AetherMudStorage {
 
     PlayerData newPlayerData();
 
@@ -47,6 +47,4 @@ public interface AetherMudStorage extends Service {
     List<? extends NpcData> getNpcDatas();
 
     NpcData newNpcData();
-
-    void persist();
 }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/DataUtils.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/DataUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..07b4278296cc21f368ac6994ebac2ea431c8cca1
--- /dev/null
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/DataUtils.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright 2017 Syncleus, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.syncleus.aethermud.storage.graphdb;
+
+import com.syncleus.ferma.ElementFrame;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public final class DataUtils {
+    private DataUtils() {
+        // can not instantiate
+    }
+
+    public static <E extends ElementFrame> void setAllElements(Collection<? extends E> newElements, Supplier<Iterator<? extends E>> allExistingFunc, Consumer<? super E> addFunc, Runnable addEmptyFunc) {
+        Iterator<? extends ElementFrame> existingAll = allExistingFunc.get();
+        if( existingAll != null ) {
+            while( existingAll.hasNext() ) {
+                ElementFrame existing = existingAll.next();
+                existing.remove();
+            }
+        }
+
+        if( newElements == null || newElements.isEmpty() ) {
+            if( addEmptyFunc != null )
+                addEmptyFunc.run();
+            return;
+        }
+
+        for(E newElement : newElements)
+            addFunc.accept(newElement);
+    }
+}
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java
index 92a97b8583815fd8b3636b71466fbe034d15407a..c1c62ae20e752262c927b32a92001291d79d0860 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java
@@ -15,45 +15,32 @@
  */
 package com.syncleus.aethermud.storage.graphdb;
 
-import com.google.common.util.concurrent.AbstractIdleService;
-import com.syncleus.aethermud.Main;
+import com.google.common.base.Function;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.ItemPojo;
 import com.syncleus.aethermud.npc.NpcBuilder;
 import com.syncleus.aethermud.npc.NpcSpawn;
 import com.syncleus.aethermud.storage.AetherMudStorage;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
+import com.syncleus.aethermud.storage.graphdb.model.NpcData;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.ferma.WrappedFramedGraph;
-import org.apache.commons.beanutils.BeanUtils;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.log4j.Logger;
 import org.apache.tinkerpop.gremlin.structure.Graph;
-import org.apache.tinkerpop.gremlin.structure.io.IoCore;
 
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.*;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
-
-//TODO : multiple instances of this class could create conflicts in the DB
-public class GraphDbAetherMudStorage extends AbstractIdleService implements AetherMudStorage {
-    private static final int MAX_BACKUPS = 5;
+public class GraphDbAetherMudStorage implements AetherMudStorage {
     private static final Logger LOGGER = Logger.getLogger(GraphDbAetherMudStorage.class);
-    private final WrappedFramedGraph<Graph> framedGraph;
-    private final String graphDbFile;
+    private final WrappedFramedGraph<? extends Graph> framedGraph;
 
 
-    public GraphDbAetherMudStorage(WrappedFramedGraph<Graph> framedGraph, String graphDbFile) {
+    public GraphDbAetherMudStorage(WrappedFramedGraph<? extends Graph> framedGraph) {
         this.framedGraph = framedGraph;
-        this.graphDbFile = graphDbFile;
     }
 
     @Override
@@ -88,47 +75,6 @@ public class GraphDbAetherMudStorage extends AbstractIdleService implements Aeth
 
     }
 
-    public synchronized void persist() {
-        try {
-            Path defaultFilePath = Paths.get(this.graphDbFile);
-            if( Files.exists(defaultFilePath) )
-                Files.move(defaultFilePath, Paths.get(this.graphDbFile + new Date().getTime()), REPLACE_EXISTING);
-        } catch (IOException e) {
-            //do nothing, it probably doesnt exist
-        }
-
-        //remove all but the last 20 backedup files
-        NavigableSet<File> orderedFiles = new TreeSet<>(new Comparator<File>() {
-            @Override
-            public int compare(File file, File t1) {
-                if( file.lastModified() > t1.lastModified() )
-                    return 1;
-                else if( file.lastModified() < t1.lastModified() )
-                    return -1;
-                else
-                    return 0;
-            }
-        });
-        File folder = new File(".");
-        orderedFiles.addAll(Arrays.asList(folder.listFiles(new FileFilter() {
-            @Override
-            public boolean accept(File file) {
-                return file.getName().contains(graphDbFile);
-            }
-        })));
-        while(orderedFiles.size() > MAX_BACKUPS) {
-            File currentFile = orderedFiles.first();
-            orderedFiles.remove(currentFile);
-            currentFile.delete();
-        }
-
-        try {
-            this.framedGraph.getBaseGraph().io(IoCore.graphson()).writeGraph(this.graphDbFile);
-        } catch (IOException e) {
-            throw new IllegalStateException("Could not write to graph file.", e);
-        }
-    }
-
     @Override
     public ItemData saveItem(ItemPojo item) {
         ItemData itemData = framedGraph.addFramedVertex(ItemData.class);
@@ -155,12 +101,4 @@ public class GraphDbAetherMudStorage extends AbstractIdleService implements Aeth
     public NpcData newNpcData() {
         return framedGraph.addFramedVertex(NpcData.class);
     }
-
-    @Override
-    protected void startUp() {
-    }
-
-    @Override
-    protected void shutDown() {
-    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphStorageFactory.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphStorageFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e333bbb2747a350bbf45b80b7bbf7625c5aca43
--- /dev/null
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphStorageFactory.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright 2017 Syncleus, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.syncleus.aethermud.storage.graphdb;
+
+import com.google.common.base.Function;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
+import com.syncleus.ferma.ext.orientdb.OrientTransactionFactory;
+import com.syncleus.ferma.ext.orientdb.impl.OrientTransactionFactoryImpl;
+import com.syncleus.ferma.tx.Tx;
+import com.syncleus.ferma.tx.TxFactory;
+import org.apache.tinkerpop.gremlin.orientdb.OrientGraphFactory;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+public class GraphStorageFactory {
+    OrientGraphFactory graphFactory;
+    OrientTransactionFactory txFactory;
+
+    public GraphStorageFactory() {
+        graphFactory = new OrientGraphFactory("memory:tinkerpop");
+        txFactory = new OrientTransactionFactoryImpl(graphFactory, true, "com.syncleus.aethermud.storage.graphdb.model");
+        txFactory.setupElementClasses();
+        txFactory.addEdgeClass("ItemApplyStats");
+    }
+
+    public AetherMudTx beginTransaction() {
+        return new AetherMudTx(txFactory.tx());
+    }
+
+    public void close() {
+        if( this.graphFactory != null ) {
+            this.graphFactory.close();
+            this.graphFactory = null;
+            this.txFactory = null;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        super.finalize();
+        this.close();
+    }
+
+    public static class AetherMudTx implements AutoCloseable {
+        private final Tx tx;
+        private final GraphDbAetherMudStorage storage;
+
+        public AetherMudTx(Tx tx) {
+            this.tx = tx;
+            this.storage = new GraphDbAetherMudStorage(tx.getGraph());
+        }
+
+        public void success() {
+            tx.success();
+        }
+
+        public void failure() {
+            tx.failure();
+        }
+
+        public void close() {
+            tx.close();
+        }
+
+        public GraphDbAetherMudStorage getStorage() {
+            return storage;
+        }
+    }
+}
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/CoolDownData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/CoolDownData.java
similarity index 69%
rename from src/main/java/com/syncleus/aethermud/storage/graphdb/CoolDownData.java
rename to src/main/java/com/syncleus/aethermud/storage/graphdb/model/CoolDownData.java
index ba4cb1ebe7fc299fd8b3a5ed7ad5cd1abe5ae423..b7927aaa53d1213d264214273f1ef652ca56111c 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/CoolDownData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/CoolDownData.java
@@ -13,14 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.syncleus.aethermud.storage.graphdb;
+package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.aethermud.player.CoolDown;
 import com.syncleus.aethermud.player.CoolDownType;
-import com.syncleus.ferma.AbstractVertexFrame;
+import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
+import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 
-public abstract class CoolDownData extends AbstractVertexFrame implements CoolDown {
+@GraphElement
+public abstract class CoolDownData extends AbstractInterceptingVertexFrame {
     @Property("type")
     public abstract CoolDownType getCoolDownType();
 
@@ -50,12 +52,26 @@ public abstract class CoolDownData extends AbstractVertexFrame implements CoolDo
         return this.getCoolDownType().hashCode();
     }
 
+    public static CoolDown copyCoolDown(CoolDownData src) {
+        return new CoolDown(src.getName(), src.getNumberOfTicks(), src.getCoolDownType());
+    }
+
+    public void decrementTick() {
+        if (getNumberOfTicks() > 0) {
+            this.setNumberOfTicks(getNumberOfTicks() - 1);
+        }
+    }
+
+    public boolean isActive() {
+        return getNumberOfTicks() > 0;
+    }
+
     @Override
     public boolean equals(Object o) {
         if(o == null)
             return false;
-        if( o instanceof CoolDown )
-            return this.getCoolDownType().equals(((CoolDown)o).getCoolDownType());
+        if( o instanceof CoolDownData )
+            return this.getCoolDownType().equals(((CoolDownData)o).getCoolDownType());
         else if( o instanceof CoolDownType )
             return this.getCoolDownType().equals((CoolDownType)o);
         else
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/EffectData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
similarity index 76%
rename from src/main/java/com/syncleus/aethermud/storage/graphdb/EffectData.java
rename to src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
index 0c7053332f5d99040f62df936361aed5c77f94be..0c6ddc8b697416198c86fb199123c497c135e322 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/EffectData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
@@ -13,13 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.syncleus.aethermud.storage.graphdb;
+package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.stats.Stats;
-import com.syncleus.ferma.AbstractVertexFrame;
+import com.syncleus.aethermud.stats.StatsPojo;
 import com.syncleus.ferma.annotations.Adjacency;
+import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
+import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
@@ -27,70 +29,56 @@ import java.lang.reflect.InvocationTargetException;
 import java.util.Iterator;
 import java.util.List;
 
-public abstract class EffectData extends AbstractVertexFrame implements Effect {
-    @Override
+@GraphElement
+public abstract class EffectData extends AbstractInterceptingVertexFrame {
     @Property("EffectName")
     public abstract String getEffectName();
 
-    @Override
     @Property("EffectDescription")
     public abstract String getEffectDescription();
 
-    @Override
     @Property("EffectApplyMessages")
     public abstract List<String> getEffectApplyMessages();
 
-    @Override
     @Property("MaxEffectApplications")
     public abstract int getMaxEffectApplications();
 
-    @Override
     @Property("FrozenMovement")
     public abstract boolean isFrozenMovement();
 
-    @Override
     @Property("EffectApplications")
     public abstract int getEffectApplications();
 
-    @Override
     @Property("EffectApplications")
     public abstract void setEffectApplications(int effectApplications);
 
-    @Override
     @Property("PlayerId")
     public abstract String getPlayerId();
 
-    @Override
     @Property("PlayerId")
     public abstract void setPlayerId(String playerId);
 
-    @Override
     @Property("EffectName")
     public abstract void setEffectName(String effectName);
 
-    @Override
     @Property("EffectDescription")
     public abstract void setEffectDescription(String effectDescription);
 
-    @Override
     @Property("EffectApplyMessages")
     public abstract void setEffectApplyMessages(List<String> effectApplyMessages);
 
-    @Override
     @Property("MaxEffectApplications")
     public abstract void setMaxEffectApplications(int maxEffectApplications);
 
-    @Override
     @Property("FrozenMovement")
     public abstract void setFrozenMovement(boolean frozenMovement);
 
     @Adjacency(label = "ApplyStatsOnTick", direction = Direction.OUT)
     public abstract <N extends StatsData> Iterator<? extends N> getAllApplyStatsOnTick(Class<? extends N> type);
 
-    @Override
     public Stats getApplyStatsOnTick() {
         Iterator<? extends StatsData> allStats = this.getAllApplyStatsOnTick(StatsData.class);
-        if( allStats.hasNext() )
+        if (allStats.hasNext())
             return allStats.next();
         else
             return null;
@@ -102,28 +90,23 @@ public abstract class EffectData extends AbstractVertexFrame implements Effect {
     @Adjacency(label = "ApplyStatsOnTick", direction = Direction.OUT)
     public abstract void removeApplyStatsOnTick(StatsData stats);
 
-    @Override
     public void setApplyStatsOnTick(Stats stats) {
         Iterator<? extends StatsData> existingAll = this.getAllApplyStatsOnTick(StatsData.class);
-        if( existingAll != null ) {
-            while( existingAll.hasNext() ) {
+        if (existingAll != null) {
+            while (existingAll.hasNext()) {
                 StatsData existing = existingAll.next();
                 this.removeApplyStatsOnTick(existing);
                 existing.remove();
             }
-
         }
-
-        if( stats == null ) {
+        if (stats == null) {
             this.addApplyStatsOnTick(this.createStats());
             return;
         }
-
         StatsData statsData;
-        if( stats instanceof StatsData ) {
+        if (stats instanceof StatsData) {
             this.addApplyStatsOnTick((StatsData) stats);
-        }
-        else {
+        } else {
             StatsData createdData = this.createStats();
             try {
                 PropertyUtils.copyProperties(createdData, stats);
@@ -137,10 +120,9 @@ public abstract class EffectData extends AbstractVertexFrame implements Effect {
     @Adjacency(label = "DurationStats", direction = Direction.OUT)
     public abstract <N extends StatsData> Iterator<? extends N> getAllDurationStats(Class<? extends N> type);
 
-    @Override
     public Stats getDurationStats() {
         Iterator<? extends StatsData> allStats = this.getAllDurationStats(StatsData.class);
-        if( allStats.hasNext() )
+        if (allStats.hasNext())
             return allStats.next();
         else
             return null;
@@ -152,28 +134,23 @@ public abstract class EffectData extends AbstractVertexFrame implements Effect {
     @Adjacency(label = "DurationStats", direction = Direction.OUT)
     public abstract void removeDurationStats(StatsData stats);
 
-    @Override
     public void setDurationStats(Stats stats) {
         Iterator<? extends StatsData> existingAll = this.getAllDurationStats(StatsData.class);
-        if( existingAll != null ) {
-            while( existingAll.hasNext() ) {
+        if (existingAll != null) {
+            while (existingAll.hasNext()) {
                 StatsData existing = existingAll.next();
                 this.removeDurationStats(existing);
                 existing.remove();
             }
-
         }
-
-        if( stats == null ) {
+        if (stats == null) {
             this.addApplyStatsOnTick(this.createStats());
             return;
         }
-
         StatsData statsData;
-        if( stats instanceof StatsData ) {
+        if (stats instanceof StatsData) {
             this.addDurationStats((StatsData) stats);
-        }
-        else {
+        } else {
             StatsData createdData = this.createStats();
             try {
                 PropertyUtils.copyProperties(createdData, stats);
@@ -185,7 +162,7 @@ public abstract class EffectData extends AbstractVertexFrame implements Effect {
     }
 
     private StatsData createStats() {
-        if( this.getDurationStats() != null )
+        if (this.getDurationStats() != null)
             throw new IllegalStateException("Already has stats, can't create another");
         final StatsData stats = this.getGraph().addFramedVertex(StatsData.class);
         stats.setAgile(0);
@@ -208,4 +185,30 @@ public abstract class EffectData extends AbstractVertexFrame implements Effect {
         stats.setWillpower(0);
         return stats;
     }
+
+    public static void copyEffect(EffectData dest, Effect src) {
+        try {
+            PropertyUtils.copyProperties(dest, src);
+            dest.setApplyStatsOnTick(src.getApplyStatsOnTick());
+            dest.setDurationStats(src.getDurationStats());
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+    }
+
+    public static Effect copyEffect(EffectData src) {
+        Effect retVal = new Effect();
+        try {
+            PropertyUtils.copyProperties(retVal, src);
+            StatsPojo durationStats = new StatsPojo();
+            PropertyUtils.copyProperties(durationStats, src.getDurationStats());
+            retVal.setDurationStats(durationStats);
+            StatsPojo applyStats = new StatsPojo();
+            PropertyUtils.copyProperties(applyStats, src.getApplyStatsOnTick());
+            retVal.setApplyStatsOnTick(applyStats);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+        return retVal;
+    }
 }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/ItemData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
similarity index 88%
rename from src/main/java/com/syncleus/aethermud/storage/graphdb/ItemData.java
rename to src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
index cafc92e02167b2d5bc82b9996417aa3801b9e45b..73cf50c44b6ec290e1655b52dbdfbaae796f772d 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/ItemData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.syncleus.aethermud.storage.graphdb;
+package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.google.common.collect.Sets;
 import com.syncleus.aethermud.core.service.TimeTracker;
@@ -21,16 +21,17 @@ import com.syncleus.aethermud.items.*;
 import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.Adjacency;
+import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
+import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
 import java.lang.reflect.InvocationTargetException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 
-public abstract class ItemData extends AbstractVertexFrame implements Item {
+@GraphElement
+public abstract class ItemData extends AbstractInterceptingVertexFrame implements Item {
     @Override
     @Property("ValidTimeOfDays")
     public abstract List<TimeTracker.TimeOfDay> getValidTimeOfDays();
@@ -172,18 +173,24 @@ public abstract class ItemData extends AbstractVertexFrame implements Item {
     @Property("HasBeenWithPlayer")
     public abstract boolean isHasBeenWithPlayer();
 
-    @Adjacency(label = "Effects", direction = Direction.OUT)
+    @Adjacency(label = "Effect", direction = Direction.OUT)
     public abstract EffectData addEffect(EffectData effects);
 
-    @Adjacency(label = "Effects", direction = Direction.OUT)
+    @Adjacency(label = "Effect", direction = Direction.OUT)
     public abstract void removeEffect(EffectData stats);
 
-    @Adjacency(label = "Effects", direction = Direction.OUT)
+    @Adjacency(label = "Effect", direction = Direction.OUT)
     public abstract <N extends EffectData> Iterator<? extends N> getEffects(Class<? extends N> type);
 
     @Override
     public Set<Effect> getEffects() {
-        return Sets.newHashSet(this.getEffects(EffectData.class));
+        Set<Effect> retVal = new HashSet<>();
+        Iterator<? extends EffectData> iterator = this.getEffects(EffectData.class);
+        while(iterator.hasNext()) {
+            EffectData effectData = iterator.next();
+            retVal.add(EffectData.copyEffect(effectData));
+        }
+        return Collections.unmodifiableSet(retVal);
     }
 
     @Override
@@ -203,14 +210,10 @@ public abstract class ItemData extends AbstractVertexFrame implements Item {
         }
 
         for( Effect effect : effects ) {
-            if (effect instanceof EffectData) {
-                this.addEffect((EffectData) effect);
-            } else {
-                try {
-                    PropertyUtils.copyProperties(this.createEffect(), effect);
-                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-                    throw new IllegalStateException("Could not copy properties");
-                }
+            try {
+                PropertyUtils.copyProperties(this.createEffect(), effect);
+            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                throw new IllegalStateException("Could not copy properties");
             }
         }
     }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/LootData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
similarity index 82%
rename from src/main/java/com/syncleus/aethermud/storage/graphdb/LootData.java
rename to src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
index 29ce226c50aad5dc21d131464357e071efad2e4b..419c6c2e5cbcdf253c2a65413c4b325572ef65b0 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/LootData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
@@ -13,14 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.syncleus.aethermud.storage.graphdb;
+package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.ferma.AbstractVertexFrame;
+import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
+import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 
 import java.util.List;
 
-public abstract class LootData extends AbstractVertexFrame {
+@GraphElement
+public abstract class LootData extends AbstractInterceptingVertexFrame {
     @Property("InternalItemNames")
     public abstract List<String> getInternalItemNames();
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/NpcData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
similarity index 77%
rename from src/main/java/com/syncleus/aethermud/storage/graphdb/NpcData.java
rename to src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
index edf0fdeedccc41985165fe59144b49ee8d4387a5..e1274dfdf3a83d1b60c360406ab0dc73e4e48e18 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/NpcData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
@@ -13,25 +13,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.syncleus.aethermud.storage.graphdb;
+package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.google.common.collect.Lists;
 import com.syncleus.aethermud.common.AetherMudMessage;
 import com.syncleus.aethermud.common.ColorizedTextTemplate;
 import com.syncleus.aethermud.npc.Temperament;
 import com.syncleus.aethermud.stats.Stats;
+import com.syncleus.aethermud.storage.graphdb.DataUtils;
 import com.syncleus.aethermud.world.model.Area;
 import com.syncleus.ferma.AbstractVertexFrame;
+import com.syncleus.ferma.ElementFrame;
 import com.syncleus.ferma.annotations.Adjacency;
+import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
+import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
 import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
 
-public abstract class NpcData extends AbstractVertexFrame {
+@GraphElement
+public abstract class NpcData extends AbstractInterceptingVertexFrame {
     @Property("criticalAttackMessages")
     public abstract List<AetherMudMessage> getCriticalAttackMessages();
 
@@ -94,22 +104,7 @@ public abstract class NpcData extends AbstractVertexFrame {
     public abstract void removeSpawnRuleData(SpawnRuleData spawnRule);
 
     public void setSpawnRulesData(List<SpawnRuleData> spawnRules) {
-        Iterator<? extends SpawnRuleData> existingAll = this.getSpawnRulesDataIterator(SpawnRuleData.class);
-        if( existingAll != null ) {
-            while( existingAll.hasNext() ) {
-                SpawnRuleData existing = existingAll.next();
-                this.removeSpawnRuleData(existing);
-                existing.remove();
-            }
-        }
-
-        if( spawnRules == null || spawnRules.isEmpty() ) {
-            this.createLootData();
-            return;
-        }
-
-        for(SpawnRuleData spawnRuleData : spawnRules)
-            this.addSpawnRuleData(spawnRuleData);
+        DataUtils.setAllElements(spawnRules, () -> this.getSpawnRulesDataIterator(SpawnRuleData.class), ruleData -> this.addSpawnRuleData(ruleData), () -> this.createSpawnRuleData() );
     }
 
     public SpawnRuleData createSpawnRuleData() {
@@ -152,22 +147,7 @@ public abstract class NpcData extends AbstractVertexFrame {
     public abstract void removeLootData(LootData loot);
 
     public void setLootData(LootData loot) {
-        Iterator<? extends LootData> existingAll = this.getAllLootDatas(LootData.class);
-        if( existingAll != null ) {
-            while( existingAll.hasNext() ) {
-                LootData existing = existingAll.next();
-                this.removeLootData(existing);
-                existing.remove();
-            }
-
-        }
-
-        if( loot == null ) {
-            this.createLootData();
-            return;
-        }
-
-        this.addLootData(loot);
+        DataUtils.setAllElements(Collections.singletonList(loot), () -> this.getAllLootDatas(LootData.class), lootData -> this.addLootData(lootData), () -> this.createLootData() );
     }
 
     public LootData createLootData() {
@@ -198,33 +178,8 @@ public abstract class NpcData extends AbstractVertexFrame {
     @Adjacency(label = "stats", direction = Direction.OUT)
     public abstract void removeStats(StatsData stats);
 
-    public void setStats(Stats stats) {
-        Iterator<? extends StatsData> existingAll = this.getAllStats(StatsData.class);
-        if( existingAll != null ) {
-            while( existingAll.hasNext() ) {
-                StatsData existing = existingAll.next();
-                this.removeStats(existing);
-                existing.remove();
-            }
-
-        }
-
-        if( stats == null ) {
-            this.createStats();
-            return;
-        }
-
-        if( stats instanceof StatsData ) {
-            this.addStats((StatsData) stats);
-        }
-        else {
-            try {
-                PropertyUtils.copyProperties(this.createStats(), stats);
-            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-                throw new IllegalStateException("Could not copy properties");
-            }
-        }
-
+    public void setStats(StatsData stats) {
+        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getAllStats(StatsData.class), statsData -> this.addStats(statsData), () -> this.createStats() );
     }
 
     public StatsData createStats() {
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/PlayerData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/PlayerData.java
similarity index 87%
rename from src/main/java/com/syncleus/aethermud/storage/graphdb/PlayerData.java
rename to src/main/java/com/syncleus/aethermud/storage/graphdb/model/PlayerData.java
index 360a37bb47f1fbd5dc267ed511d8adfd13075889..01d21c70bbf793b1eb7203975d3905623b24039a 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/PlayerData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/PlayerData.java
@@ -13,24 +13,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.syncleus.aethermud.storage.graphdb;
+package com.syncleus.aethermud.storage.graphdb.model;
 
 
-import com.syncleus.aethermud.items.EffectPojo;
+import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.player.*;
 import com.google.common.collect.Sets;
-import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.ClassInitializer;
 import com.syncleus.ferma.DefaultClassInitializer;
 import com.syncleus.ferma.annotations.Adjacency;
+import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
+import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.*;
 
-public abstract class PlayerData extends AbstractVertexFrame {
+@GraphElement
+public abstract class PlayerData extends AbstractInterceptingVertexFrame {
 
     static final ClassInitializer<PlayerData> DEFAULT_INITIALIZER = new DefaultClassInitializer(PlayerData.class);
 
@@ -101,12 +103,6 @@ public abstract class PlayerData extends AbstractVertexFrame {
     @Property("equipment")
     public abstract void setPlayerEquipment(List<String> playerEquipment);
 
-    @Property("effects")
-    public abstract List<EffectPojo> getEffects();
-
-    @Property("effects")
-    public abstract void setEffects(List<EffectPojo> effects);
-
     @Property("markedForDelete")
     public abstract boolean isMarkedForDelete();
 
@@ -143,6 +139,56 @@ public abstract class PlayerData extends AbstractVertexFrame {
     @Property("currentRoomId")
     public abstract void setCurrentRoomId(Integer currentRoomId);
 
+    @Adjacency(label = "Effect", direction = Direction.OUT)
+    public abstract EffectData addEffect(EffectData effects);
+
+    @Adjacency(label = "Effect", direction = Direction.OUT)
+    public abstract void removeEffect(EffectData stats);
+
+    @Adjacency(label = "Effect", direction = Direction.OUT)
+    public abstract <N extends EffectData> Iterator<? extends N> getEffects(Class<? extends N> type);
+
+    public Set<EffectData> getEffects() {
+        return Sets.newHashSet(this.getEffects(EffectData.class));
+    }
+
+    public void setEffects(Set<EffectData> effects) {
+        this.resetEffects();
+
+        if( effects == null || effects.size() == 0 ) {
+            return;
+        }
+
+        for( EffectData effect : effects ) {
+            if (effect instanceof EffectData) {
+                this.addEffect((EffectData) effect);
+            } else {
+                try {
+                    PropertyUtils.copyProperties(this.createEffect(), effect);
+                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                    throw new IllegalStateException("Could not copy properties");
+                }
+            }
+        }
+    }
+
+    public EffectData createEffect() {
+        final EffectData effect = this.getGraph().addFramedVertex(EffectData.class);
+        this.addEffect(effect);
+        return effect;
+    }
+
+    public void resetEffects(){
+        Iterator<? extends EffectData> existingAll = this.getEffects(EffectData.class);
+        if( existingAll != null ) {
+            while( existingAll.hasNext() ) {
+                EffectData existing = existingAll.next();
+                this.removeEffect(existing);
+                existing.remove();
+            }
+        }
+    }
+
     @Adjacency(label = "coolDowns", direction = Direction.OUT)
     public abstract <N extends CoolDownData> Iterator<? extends N> getCoolDowns(Class<? extends N> type);
 
@@ -358,21 +404,6 @@ public abstract class PlayerData extends AbstractVertexFrame {
         this.setPlayerEquipment(playerEquipment);
     }
 
-    public void addEffect(EffectPojo effect) {
-        List<EffectPojo> effects = this.getEffects();
-        if (effects == null) {
-            effects = new ArrayList<>();
-        }
-        effects.add(effect);
-        this.setEffects(effects);
-    }
-
-    public void removeEffect(EffectPojo effect) {
-        List<EffectPojo> effects = this.getEffects();
-        effects.remove(effect);
-        this.setEffects(effects);
-    }
-
     public void incrementGold(int amt) {
         int gold = this.getGold();
         gold = gold + amt;
@@ -410,10 +441,6 @@ public abstract class PlayerData extends AbstractVertexFrame {
         this.setPlayerRoles(new HashSet());
     }
 
-    public void resetEffects(){
-        this.setEffects(new ArrayList<>());
-    }
-
     public boolean setSetting(String key, String value) {
         Map<String, String> playerSettings = this.getPlayerSettings();
         if (playerSettings == null) {
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/SpawnRuleData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
similarity index 85%
rename from src/main/java/com/syncleus/aethermud/storage/graphdb/SpawnRuleData.java
rename to src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
index 0102f101318235fd395e7a8a5c6649752ba756bc..5465f88a1ac3642d4308a93b90f8648e7411b949 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/SpawnRuleData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
@@ -13,13 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.syncleus.aethermud.storage.graphdb;
+package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.aethermud.world.model.Area;
 import com.syncleus.ferma.AbstractVertexFrame;
+import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
+import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 
-public abstract class SpawnRuleData extends AbstractVertexFrame {
+@GraphElement
+public abstract class SpawnRuleData extends AbstractInterceptingVertexFrame {
     @Property("RandomChance")
     public abstract int getRandomChance();
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/StatsData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatsData.java
similarity index 78%
rename from src/main/java/com/syncleus/aethermud/storage/graphdb/StatsData.java
rename to src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatsData.java
index 0107142262e21ffabf2707ab8922b373524b038b..b7c4b33f6ca6bbd757b14acb4372ff668fbe2df8 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/StatsData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatsData.java
@@ -13,13 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.syncleus.aethermud.storage.graphdb;
+package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.aethermud.stats.Stats;
+import com.syncleus.aethermud.stats.StatsPojo;
 import com.syncleus.ferma.AbstractVertexFrame;
+import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
+import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
+import org.apache.commons.beanutils.PropertyUtils;
 
-public abstract class StatsData extends AbstractVertexFrame implements Stats {
+import java.lang.reflect.InvocationTargetException;
+
+@GraphElement
+public abstract class StatsData extends AbstractInterceptingVertexFrame implements Stats {
     @Override
     @Property("intelligence")
     public abstract Integer getIntelligence();
@@ -86,7 +93,7 @@ public abstract class StatsData extends AbstractVertexFrame implements Stats {
 
     @Override
     @Property("meleSkill")
-    public abstract Integer getMeleSkill();
+    public abstract Integer getMeleeSkill();
 
     @Override
     @Property("meleSkill")
@@ -163,4 +170,22 @@ public abstract class StatsData extends AbstractVertexFrame implements Stats {
     @Override
     @Property("inventorySize")
     public abstract void setInventorySize(Integer inventorySize);
+
+    public static void copyStats(StatsData dest, StatsPojo src) {
+        try {
+            PropertyUtils.copyProperties(dest, src);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+    }
+
+    public static StatsPojo copyStats(Stats src) {
+        StatsPojo retVal = new StatsPojo();
+        try {
+            PropertyUtils.copyProperties(retVal, src);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            throw new IllegalStateException("Could not copy properties");
+        }
+        return retVal;
+    }
 }
diff --git a/src/test/java/com/syncleus/aethermud/AetherMudUtilsTest.java b/src/test/java/com/syncleus/aethermud/AetherMudUtilsTest.java
index 3495dfab2d078b3ade0b73f2acce9bcda6116c1a..510c5712bd42e4b6f5307201061db500e53d8d50 100644
--- a/src/test/java/com/syncleus/aethermud/AetherMudUtilsTest.java
+++ b/src/test/java/com/syncleus/aethermud/AetherMudUtilsTest.java
@@ -26,8 +26,11 @@ import com.syncleus.aethermud.stats.DefaultStats;
 import com.syncleus.aethermud.stats.modifier.StatsModifierFactory;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
-import com.syncleus.aethermud.storage.graphdb.StatsData;
+import com.syncleus.aethermud.storage.AetherMudStorage;
+import com.syncleus.aethermud.storage.graphdb.GraphDbAetherMudStorage;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.model.StatsData;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.junit.Assert;
 import org.junit.Test;
@@ -54,7 +57,7 @@ public class AetherMudUtilsTest {
         PlayerData playerData = mock(PlayerData.class);
         playerData.setNpcKillLog(new HashMap<>());
         playerData.setCoolDowns(new HashMap<>());
-        playerData.setEffects(new ArrayList<>());
+        playerData.setEffects(new HashSet<>());
         playerData.setGold(0);
         playerData.setGoldInBank(0);
         playerData.setInventory(new ArrayList<>());
@@ -80,8 +83,16 @@ public class AetherMudUtilsTest {
         StatsModifierFactory statsModifierFactory = mock(StatsModifierFactory.class);
         when(statsModifierFactory.getStatsModifier(Matchers.any())).thenReturn(DefaultStats.DEFAULT_PLAYER.createStats());
         when(gameManager.getStatsModifierFactory()).thenReturn(statsModifierFactory);
+
+        GraphStorageFactory storageFactory = mock(GraphStorageFactory.class);
+        GraphStorageFactory.AetherMudTx tx = mock(GraphStorageFactory.AetherMudTx.class);
+        GraphDbAetherMudStorage aetherStorage = mock(GraphDbAetherMudStorage.class);
+        when(gameManager.getGraphStorageFactory()).thenReturn(storageFactory);
+        when(storageFactory.beginTransaction()).thenReturn(tx);
+        when( tx.getStorage() ).thenReturn(aetherStorage);
+        when(aetherStorage.getPlayerMetadata(Matchers.any())).thenReturn(java.util.Optional.ofNullable(playerData));
+
         PlayerManager playerManager = mock(PlayerManager.class);
-        when(playerManager.getPlayerMetadata(Matchers.any())).thenReturn(java.util.Optional.ofNullable(playerData));
         when(gameManager.getPlayerManager()).thenReturn(playerManager);
         EntityManager entityManager = mock(EntityManager.class);
         when(gameManager.getEntityManager()).thenReturn(entityManager);
diff --git a/src/test/java/com/syncleus/aethermud/common/ColorizedTextTemplateTest.java b/src/test/java/com/syncleus/aethermud/common/ColorizedTextTemplateTest.java
index 2b74a2ee314f537a7da5389357fd5e46a30ad2f4..5ec476f171910d5384a2edb5c6e44d2755a54cea 100644
--- a/src/test/java/com/syncleus/aethermud/common/ColorizedTextTemplateTest.java
+++ b/src/test/java/com/syncleus/aethermud/common/ColorizedTextTemplateTest.java
@@ -20,12 +20,15 @@ import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.storage.AetherMudStorage;
 import com.syncleus.aethermud.storage.graphdb.GraphDbAetherMudStorage;
-import com.syncleus.aethermud.storage.graphdb.NpcData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.NpcData;
 import com.google.common.collect.Maps;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.syncleus.ferma.DelegatingFramedGraph;
 import com.syncleus.ferma.WrappedFramedGraph;
+import com.syncleus.ferma.ext.orientdb.impl.OrientTransactionFactoryImpl;
+import org.apache.tinkerpop.gremlin.orientdb.OrientGraphFactory;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
 import org.junit.Assert;
@@ -65,17 +68,19 @@ public class ColorizedTextTemplateTest {
 
         Gson gson = new GsonBuilder().setPrettyPrinting().create();
 
-        WrappedFramedGraph<Graph> framedGraph = new DelegatingFramedGraph(TinkerGraph.open(), Main.FRAMED_TYPES);
-        AetherMudStorage npcStorage = new GraphDbAetherMudStorage(framedGraph, null);
+        GraphStorageFactory txFactory = new GraphStorageFactory();
+        try( GraphStorageFactory.AetherMudTx tx = txFactory.beginTransaction() ) {
+            AetherMudStorage npcStorage = tx.getStorage();
 
-        List<? extends NpcData> npcData = npcStorage.getNpcDatas();
-        Assert.assertTrue(npcData.isEmpty());
+            List<? extends NpcData> npcData = npcStorage.getNpcDatas();
+            Assert.assertTrue(npcData.isEmpty());
 
-        NpcData newNpc = npcStorage.newNpcData();
-        newNpc.setColorName("blue");
+            NpcData newNpc = npcStorage.newNpcData();
+            newNpc.setColorName("blue");
 
-        npcData = npcStorage.getNpcDatas();
-        Assert.assertFalse(npcData.isEmpty());
+            npcData = npcStorage.getNpcDatas();
+            Assert.assertFalse(npcData.isEmpty());
+        }
 
 //        npcData.forEach(new Consumer<NpcData>() {
 //            @Override
diff --git a/src/test/java/com/syncleus/aethermud/player/CoolDownPojoTest.java b/src/test/java/com/syncleus/aethermud/player/CoolDownPojoTest.java
deleted file mode 100644
index 7e6e03eceda5a97eefd7047a8c0c78fa84621a3e..0000000000000000000000000000000000000000
--- a/src/test/java/com/syncleus/aethermud/player/CoolDownPojoTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright 2017 Syncleus, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.syncleus.aethermud.player;
-
-import com.google.gson.GsonBuilder;
-import org.junit.Test;
-
-public class CoolDownPojoTest {
-
-
-    @Test
-    public void testSerialization() throws Exception {
-
-
-        CoolDownPojo coolDown = new CoolDownPojo(CoolDownType.DETAINMENT);
-
-        String s = new GsonBuilder().create().toJson(coolDown, CoolDownPojo.class);
-
-        System.out.println(s);
-
-
-    }
-
-}
diff --git a/src/test/java/com/syncleus/aethermud/player/CoolDownTest.java b/src/test/java/com/syncleus/aethermud/player/CoolDownTest.java
index f76ac618de9528263100183363d5e82a2d24b384..a077a598e5439ae511f333e47c1c2b8f2b4b3b22 100644
--- a/src/test/java/com/syncleus/aethermud/player/CoolDownTest.java
+++ b/src/test/java/com/syncleus/aethermud/player/CoolDownTest.java
@@ -20,12 +20,11 @@ import org.junit.Test;
 
 public class CoolDownTest {
 
-
     @Test
     public void testSerialization() throws Exception {
 
 
-        CoolDownPojo coolDown = new CoolDownPojo(CoolDownType.DETAINMENT);
+        CoolDown coolDown = new CoolDown(CoolDownType.DETAINMENT);
 
         String s = new GsonBuilder().create().toJson(coolDown, CoolDown.class);
 
diff --git a/src/test/java/com/syncleus/aethermud/player/PlayerManagerTest.java b/src/test/java/com/syncleus/aethermud/player/PlayerManagerTest.java
index b1cfb5dfda93f135dbc7374d4da88fde9834c7c2..5cae09e350c55047076a9590ca8ab106edfe1646 100644
--- a/src/test/java/com/syncleus/aethermud/player/PlayerManagerTest.java
+++ b/src/test/java/com/syncleus/aethermud/player/PlayerManagerTest.java
@@ -18,6 +18,7 @@ package com.syncleus.aethermud.player;
 import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.SessionManager;
 import com.syncleus.aethermud.storage.graphdb.GraphDbAetherMudStorage;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
 import com.syncleus.aethermud.world.model.Room;
 import com.google.common.collect.Sets;
 import com.syncleus.ferma.DelegatingFramedGraph;
@@ -48,9 +49,8 @@ public class PlayerManagerTest {
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        WrappedFramedGraph<Graph> framedGraph = new DelegatingFramedGraph(TinkerGraph.open(), Main.FRAMED_TYPES);
-        GraphDbAetherMudStorage graphStorage = new GraphDbAetherMudStorage(framedGraph, "aethermud-test-graph.json");
-        playerManager = new PlayerManager(graphStorage, sessionManager);
+        GraphStorageFactory storageFactory = new GraphStorageFactory();
+        playerManager = new PlayerManager(storageFactory, sessionManager);
     }
 
     @Test
diff --git a/src/test/java/com/syncleus/aethermud/player/combatsimuation/NpcTestHarness.java b/src/test/java/com/syncleus/aethermud/player/combatsimuation/NpcTestHarness.java
index b27f65a87f79895daf8764c2a6b97c3d24ffbdca..ec22d581407094c403528697a288c1774d930c6c 100644
--- a/src/test/java/com/syncleus/aethermud/player/combatsimuation/NpcTestHarness.java
+++ b/src/test/java/com/syncleus/aethermud/player/combatsimuation/NpcTestHarness.java
@@ -32,7 +32,8 @@ import com.syncleus.aethermud.stats.Levels;
 import com.syncleus.aethermud.stats.experience.Experience;
 import com.syncleus.aethermud.storage.WorldStorage;
 import com.syncleus.aethermud.storage.graphdb.GraphDbAetherMudStorage;
-import com.syncleus.aethermud.storage.graphdb.PlayerData;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.aethermud.world.MapsManager;
 import com.syncleus.aethermud.world.RoomManager;
 import com.google.common.collect.Maps;
@@ -68,65 +69,77 @@ public class NpcTestHarness {
     // Levels 1-3
     @Test
     public void testDemonCat() throws Exception {
-        List<? extends NpcSpawn> npcsFromFile = gameManager.getGraphStorage().getAllNpcs(gameManager);
-        NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("demon cat")).collect(Collectors.toList()).get(0);
-        processRunAndVerify(npcSpawnFromFile, 1, Sets.newHashSet(), 98f, 90f, 10, 5, 3, 0);
-        processRunAndVerify(npcSpawnFromFile, 2, Sets.newHashSet(), 99.9f, 96f, 7, 4, 3, 0);
-        processRunAndVerify(npcSpawnFromFile, 3, Sets.newHashSet(), 100f, 98f, 6, 3, 3, 0);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            List<? extends NpcSpawn> npcsFromFile = tx.getStorage().getAllNpcs(gameManager);
+            NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("demon cat")).collect(Collectors.toList()).get(0);
+            processRunAndVerify(npcSpawnFromFile, 1, Sets.newHashSet(), 98f, 90f, 10, 5, 3, 0);
+            processRunAndVerify(npcSpawnFromFile, 2, Sets.newHashSet(), 99.9f, 96f, 7, 4, 3, 0);
+            processRunAndVerify(npcSpawnFromFile, 3, Sets.newHashSet(), 100f, 98f, 6, 3, 3, 0);
+        }
     }
 
     // Levels 2-4
     @Test
     public void testSwampBerserker() throws Exception {
-        List<? extends NpcSpawn> npcsFromFile = gameManager.getGraphStorage().getAllNpcs(gameManager);
-        NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("swamp berserker")).collect(Collectors.toList()).get(0);
-        processRunAndVerify(npcSpawnFromFile, 1, Sets.newHashSet(), 15f, 0f, 10, 0, 10, 5);
-        processRunAndVerify(npcSpawnFromFile, 2, Sets.newHashSet(), 25f, 0f, 10, 0, 10, 5);
-        processRunAndVerify(npcSpawnFromFile, 3, Sets.newHashSet(), 55f, 0f, 10, 0, 10, 5);
-        processRunAndVerify(npcSpawnFromFile, 4, Sets.newHashSet(), 90f, 0f, 10, 0, 10, 5);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            List<? extends NpcSpawn> npcsFromFile = tx.getStorage().getAllNpcs(gameManager);
+            NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("swamp berserker")).collect(Collectors.toList()).get(0);
+            processRunAndVerify(npcSpawnFromFile, 1, Sets.newHashSet(), 15f, 0f, 10, 0, 10, 5);
+            processRunAndVerify(npcSpawnFromFile, 2, Sets.newHashSet(), 25f, 0f, 10, 0, 10, 5);
+            processRunAndVerify(npcSpawnFromFile, 3, Sets.newHashSet(), 55f, 0f, 10, 0, 10, 5);
+            processRunAndVerify(npcSpawnFromFile, 4, Sets.newHashSet(), 90f, 0f, 10, 0, 10, 5);
+        }
     }
 
     // Levels 2-4
     @Test
     public void testBloodWolf() throws Exception {
-        List<? extends NpcSpawn> npcsFromFile = gameManager.getGraphStorage().getAllNpcs(gameManager);
-        NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("blood wolf")).collect(Collectors.toList()).get(0);
-        processRunAndVerify(npcSpawnFromFile, 1, Sets.newHashSet(), 15f, 0f, 10, 0, 14, 5);
-        processRunAndVerify(npcSpawnFromFile, 2, Sets.newHashSet(), 25f, 0f, 10, 0, 14, 5);
-        processRunAndVerify(npcSpawnFromFile, 3, Sets.newHashSet(), 55f, 0f, 10, 0, 14, 5);
-        processRunAndVerify(npcSpawnFromFile, 4, Sets.newHashSet(), 90f, 0f, 10, 0, 14, 5);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            List<? extends NpcSpawn> npcsFromFile = tx.getStorage().getAllNpcs(gameManager);
+            NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("blood wolf")).collect(Collectors.toList()).get(0);
+            processRunAndVerify(npcSpawnFromFile, 1, Sets.newHashSet(), 15f, 0f, 10, 0, 14, 5);
+            processRunAndVerify(npcSpawnFromFile, 2, Sets.newHashSet(), 25f, 0f, 10, 0, 14, 5);
+            processRunAndVerify(npcSpawnFromFile, 3, Sets.newHashSet(), 55f, 0f, 10, 0, 14, 5);
+            processRunAndVerify(npcSpawnFromFile, 4, Sets.newHashSet(), 90f, 0f, 10, 0, 14, 5);
+        }
     }
 
     // Levels 4-6
     @Test
     public void testTreeBerserker() throws Exception {
-        List<? extends NpcSpawn> npcsFromFile = gameManager.getGraphStorage().getAllNpcs(gameManager);
-        NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("tree berserker")).collect(Collectors.toList()).get(0);
-        processRunAndVerify(npcSpawnFromFile, 3, Sets.newHashSet(), 20f, 12f, 10, 0, 14, 8);
-        processRunAndVerify(npcSpawnFromFile, 4, Sets.newHashSet(), 40f, 33f, 10, 0, 14, 8);
-        processRunAndVerify(npcSpawnFromFile, 5, Sets.newHashSet(), 80f, 70f, 10, 0, 14, 8);
-        processRunAndVerify(npcSpawnFromFile, 6, Sets.newHashSet(), 95f, 86f, 10, 0, 14, 8);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            List<? extends NpcSpawn> npcsFromFile = tx.getStorage().getAllNpcs(gameManager);
+            NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("tree berserker")).collect(Collectors.toList()).get(0);
+            processRunAndVerify(npcSpawnFromFile, 3, Sets.newHashSet(), 20f, 12f, 10, 0, 14, 8);
+            processRunAndVerify(npcSpawnFromFile, 4, Sets.newHashSet(), 40f, 33f, 10, 0, 14, 8);
+            processRunAndVerify(npcSpawnFromFile, 5, Sets.newHashSet(), 80f, 70f, 10, 0, 14, 8);
+            processRunAndVerify(npcSpawnFromFile, 6, Sets.newHashSet(), 95f, 86f, 10, 0, 14, 8);
+        }
     }
 
     // Levels 6-8
     @Test
     public void testSwampBear() throws Exception {
-        List<? extends NpcSpawn> npcsFromFile = gameManager.getGraphStorage().getAllNpcs(gameManager);
-        NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("swamp bear")).collect(Collectors.toList()).get(0);
-        processRunAndVerify(npcSpawnFromFile, 6, getEarlyLevelArmorSet(), 55, 40f, 10, 0, 18, 12);
-        processRunAndVerify(npcSpawnFromFile, 7, getEarlyLevelArmorSet(), 85, 70f, 10, 0, 18, 12);
-        processRunAndVerify(npcSpawnFromFile, 8, getEarlyLevelArmorSet(), 95f, 86f, 10, 0, 18, 12);
-        processRunAndVerify(npcSpawnFromFile, 9, getEarlyLevelArmorSet(), 100f, 0f, 10, 0, 18, 12);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            List<? extends NpcSpawn> npcsFromFile = tx.getStorage().getAllNpcs(gameManager);
+            NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("swamp bear")).collect(Collectors.toList()).get(0);
+            processRunAndVerify(npcSpawnFromFile, 6, getEarlyLevelArmorSet(), 55, 40f, 10, 0, 18, 12);
+            processRunAndVerify(npcSpawnFromFile, 7, getEarlyLevelArmorSet(), 85, 70f, 10, 0, 18, 12);
+            processRunAndVerify(npcSpawnFromFile, 8, getEarlyLevelArmorSet(), 95f, 86f, 10, 0, 18, 12);
+            processRunAndVerify(npcSpawnFromFile, 9, getEarlyLevelArmorSet(), 100f, 0f, 10, 0, 18, 12);
+        }
     }
 
     // Levels 8-10
     @Test
     public void testRedEyeBear() throws Exception {
-        List<? extends NpcSpawn> npcsFromFile = gameManager.getGraphStorage().getAllNpcs(gameManager);
-        NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("red-eyed bear")).collect(Collectors.toList()).get(0);
-        processRunAndVerify(npcSpawnFromFile, 8, getMidLevelArmorSet(), 55, 36f, 10, 0, 24, 18);
-        processRunAndVerify(npcSpawnFromFile, 9, getMidLevelArmorSet(), 85, 70f, 10, 0, 24, 18);
-        processRunAndVerify(npcSpawnFromFile, 10, getMidLevelArmorSet(), 100f, 0f, 10, 0, 24, 18);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            List<? extends NpcSpawn> npcsFromFile = tx.getStorage().getAllNpcs(gameManager);
+            NpcSpawn npcSpawnFromFile = npcsFromFile.stream().filter(npc -> npc.getName().equals("red-eyed bear")).collect(Collectors.toList()).get(0);
+            processRunAndVerify(npcSpawnFromFile, 8, getMidLevelArmorSet(), 55, 36f, 10, 0, 24, 18);
+            processRunAndVerify(npcSpawnFromFile, 9, getMidLevelArmorSet(), 85, 70f, 10, 0, 24, 18);
+            processRunAndVerify(npcSpawnFromFile, 10, getMidLevelArmorSet(), 100f, 0f, 10, 0, 24, 18);
+        }
     }
 
     private Set<ItemPojo> getEarlyLevelArmorSet() {
@@ -291,27 +304,29 @@ public class NpcTestHarness {
     }
 
     private void createUser(String username, String password) {
-        PlayerData playerData = gameManager.getPlayerManager().newPlayerData();
-        playerData.setNpcKillLog(new HashMap<>());
-        playerData.setCoolDowns(new HashMap<>());
-        playerData.setEffects(new ArrayList<>());
-        playerData.setGold(0);
-        playerData.setGoldInBank(0);
-        playerData.setInventory(new ArrayList<>());
-        playerData.setLearnedSpells(new ArrayList<>());
-        playerData.setLockerInventory(new ArrayList<>());
-        playerData.setIsMarkedForDelete(false);
-        playerData.setPlayerName(username);
-        playerData.setPassword(password);
-        playerData.setPlayerClass(PlayerClass.BASIC);
-        playerData.setPlayerEquipment(new ArrayList<>());
-        playerData.setPlayerId(Main.createPlayerId(username));
-        playerData.setPlayerRoles(Sets.newHashSet(PlayerRole.MORTAL));
-        playerData.setPlayerSettings(new HashMap<>());
-        try {
-            PropertyUtils.copyProperties(playerData.createStats(), DefaultStats.DEFAULT_PLAYER.createStats());
-        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            throw new IllegalStateException("Could not create a stats object", e);
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            PlayerData playerData = tx.getStorage().newPlayerData();
+            playerData.setNpcKillLog(new HashMap<>());
+            playerData.setCoolDowns(new HashMap<>());
+            playerData.setEffects(new HashSet<>());
+            playerData.setGold(0);
+            playerData.setGoldInBank(0);
+            playerData.setInventory(new ArrayList<>());
+            playerData.setLearnedSpells(new ArrayList<>());
+            playerData.setLockerInventory(new ArrayList<>());
+            playerData.setIsMarkedForDelete(false);
+            playerData.setPlayerName(username);
+            playerData.setPassword(password);
+            playerData.setPlayerClass(PlayerClass.BASIC);
+            playerData.setPlayerEquipment(new ArrayList<>());
+            playerData.setPlayerId(Main.createPlayerId(username));
+            playerData.setPlayerRoles(Sets.newHashSet(PlayerRole.MORTAL));
+            playerData.setPlayerSettings(new HashMap<>());
+            try {
+                PropertyUtils.copyProperties(playerData.createStats(), DefaultStats.DEFAULT_PLAYER.createStats());
+            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+                throw new IllegalStateException("Could not create a stats object", e);
+            }
         }
     }
 
@@ -329,13 +344,12 @@ public class NpcTestHarness {
             }
         };
         AetherMudConfiguration aetherMudConfiguration = new AetherMudConfiguration(new MapConfiguration(Maps.newHashMap()));
-        WrappedFramedGraph<Graph> framedGraph = new DelegatingFramedGraph(TinkerGraph.open(), Main.FRAMED_TYPES);
-        GraphDbAetherMudStorage graphStorage = new GraphDbAetherMudStorage(framedGraph, "aethermud-tests-graph.json");
-        PlayerManager playerManager = new PlayerManager(graphStorage, new SessionManager());
+        GraphStorageFactory storageFactory = new GraphStorageFactory();
+        PlayerManager playerManager = new PlayerManager(storageFactory, new SessionManager());
         RoomManager roomManager = new RoomManager(playerManager);
         MapsManager mapsManager = new MapsManager(aetherMudConfiguration, roomManager);
-        EntityManager entityManager = new EntityManager(graphStorage, roomManager, playerManager);
-        GameManager gameManager = new GameManager(null, framedGraph, aetherMudConfiguration, roomManager, playerManager, entityManager, mapsManager, channelUtils, HttpClients.createDefault());
+        EntityManager entityManager = new EntityManager(storageFactory, roomManager, playerManager);
+        GameManager gameManager = new GameManager(storageFactory, aetherMudConfiguration, roomManager, playerManager, entityManager, mapsManager, channelUtils, HttpClients.createDefault());
         WorldStorage worldExporter = new WorldStorage(roomManager, mapsManager, gameManager.getFloorManager(), entityManager, gameManager);
         worldExporter.buildTestworld();
         ConfigureCommands.configure(gameManager);
diff --git a/src/test/java/com/syncleus/aethermud/storage/graphdb/PlayerDataTest.java b/src/test/java/com/syncleus/aethermud/storage/graphdb/PlayerDataTest.java
index 160121902d60f91f058fe718eed5260f3066ce78..294f160df1daa1d44906577998df1a1979ccb9c6 100644
--- a/src/test/java/com/syncleus/aethermud/storage/graphdb/PlayerDataTest.java
+++ b/src/test/java/com/syncleus/aethermud/storage/graphdb/PlayerDataTest.java
@@ -15,6 +15,7 @@
  */
 package com.syncleus.aethermud.storage.graphdb;
 
+import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 import com.syncleus.ferma.DelegatingFramedGraph;
 import com.syncleus.ferma.FramedGraph;
 import org.apache.tinkerpop.gremlin.structure.Graph;
@@ -30,23 +31,24 @@ public class PlayerDataTest {
     @Test
     public void testPersist() {
 
-        Graph graph = TinkerGraph.open();
+        GraphStorageFactory txFactory = new GraphStorageFactory();
+        try( GraphStorageFactory.AetherMudTx tx = txFactory.beginTransaction() ) {
 
-        final FramedGraph fg = new DelegatingFramedGraph(graph, TEST_TYPES);
+            Map<String, Long> testMap = new HashMap<>();
+            testMap.put("foo", 77L);
+            testMap.put("bar", 23L);
 
-        Map<String, Long> testMap = new HashMap<>();
-        testMap.put("foo", 77L);
-        testMap.put("bar", 23L);
+            PlayerData p1 = tx.getStorage().newPlayerData();
+            p1.setPlayerName("Jeff");
+            p1.setPlayerId("Jeff");
+            p1.setNpcKillLog(testMap);
 
-        PlayerData p1 = fg.addFramedVertex(PlayerData.class);
-        p1.setPlayerName("Jeff");
-        p1.setNpcKillLog(testMap);
+            PlayerData jeff = tx.getStorage().getPlayerMetadata("Jeff").get();
 
-        PlayerData jeff = fg.traverse((g) -> g.V().has("name", "Jeff")).next(PlayerData.class);
-
-        Assert.assertTrue(PlayerData.class.isAssignableFrom(jeff.getClass()));
-        Assert.assertEquals(testMap, jeff.getNpcKillLog());
-        Assert.assertEquals(2, jeff.getNpcKillLog().size());
-        Assert.assertEquals(2, testMap.size());
+            Assert.assertTrue(PlayerData.class.isAssignableFrom(jeff.getClass()));
+            Assert.assertEquals(testMap, jeff.getNpcKillLog());
+            Assert.assertEquals(2, jeff.getNpcKillLog().size());
+            Assert.assertEquals(2, testMap.size());
+        }
     }
 }