diff --git a/src/main/java/com/syncleus/aethermud/command/commands/DropCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/DropCommand.java
index 1e382f17b8a462e2551e103816a9b2e8adf74d62..9558b595f725cabf7cf52309fff40c37dae0b9d3 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/DropCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/DropCommand.java
@@ -15,7 +15,7 @@
  */
 package com.syncleus.aethermud.command.commands;
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.core.GameManager;
 import com.google.common.base.Joiner;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -43,7 +43,7 @@ public class DropCommand extends Command {
             }
             originalMessageParts.remove(0);
             String itemTarget = Joiner.on(" ").join(originalMessageParts);
-            for (Item item : player.getInventory()) {
+            for (ItemInstance item : player.getInventory()) {
                 if (item.getItemTriggers().contains(itemTarget)) {
                     item.setWithPlayer(false);
                     gameManager.placeItemInRoom(currentRoom.getRoomId(), item.getItemId());
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/EquipCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/EquipCommand.java
index 19ed11e65e1aeac7699361d2d1ed8c22ddebcd56..34d334dd2a684681d1a7f7afd11213e83120d0a1 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/EquipCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/EquipCommand.java
@@ -15,7 +15,7 @@
  */
 package com.syncleus.aethermud.command.commands;
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.core.GameManager;
 import com.google.common.base.Joiner;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -43,9 +43,9 @@ public class EquipCommand extends Command {
             }
             originalMessageParts.remove(0);
             String itemTarget = Joiner.on(" ").join(originalMessageParts);
-            List<Item> inventory = player.getInventory();
+            List<ItemInstance> inventory = player.getInventory();
             if (inventory != null) {
-                for (Item item : inventory) {
+                for (ItemInstance item : inventory) {
                     if (item.getItemTriggers().contains(itemTarget)) {
                         if (item.getEquipment() == null) {
                             write("Item is not equipable.");
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/InventoryCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/InventoryCommand.java
index 6cc9d39e673e544c5befb94bf0e1836a270aae9c..bb6ecadcb2525790b08beec8f3c28e6c0248da82 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/InventoryCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/InventoryCommand.java
@@ -16,7 +16,7 @@
 package com.syncleus.aethermud.command.commands;
 
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.core.GameManager;
 import org.apache.commons.lang.StringUtils;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -40,7 +40,7 @@ public class InventoryCommand extends Command {
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         this.execCommand(ctx, e, () -> {
-            List<Item> inventory = player.getInventory();
+            List<ItemInstance> inventory = player.getInventory();
             if (inventory == null) {
                 write("You aren't carrying anything.");
                 return;
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/LootCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/LootCommand.java
index e33e6ece3c4d2e965ef44fe19653819c395d321e..74b456d5f37ef3692f304c7d6d4e2d8c267c710e 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/LootCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/LootCommand.java
@@ -16,7 +16,8 @@
 package com.syncleus.aethermud.command.commands;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.server.communication.Color;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -42,8 +43,8 @@ public class LootCommand extends Command {
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
         execCommand(ctx, e, () -> {
             if (originalMessageParts.size() > 1) {
-                for (Item item : player.getInventory()) {
-                    if (item.getInternalItemName().equals(Item.CORPSE_INTENAL_NAME)) {
+                for (ItemInstance item : player.getInventory()) {
+                    if (item.getInternalItemName().equals(ItemInstance.CORPSE_INTENAL_NAME)) {
                         Loot loot = item.getLoot();
                         if (loot != null) {
                             int gold = lootManager.lootGoldAmountReturn(loot);
@@ -51,8 +52,8 @@ public class LootCommand extends Command {
                                 write("You looted " + NumberFormat.getNumberInstance(Locale.US).format(gold) + Color.YELLOW + " gold" + Color.RESET + " from a " + item.getItemName() + ".\r\n");
                                 player.incrementGold(gold);
                             }
-                            Set<Item> items = lootManager.lootItemsReturn(loot);
-                            for (Item i: items) {
+                            Set<ItemInstance> items = lootManager.lootItemsReturn(loot);
+                            for (ItemInstance i: items) {
                                 gameManager.acquireItem(player, i.getItemId(), true);
                                 write("You looted " + i.getItemName() +  " from a " + item.getItemName() + ".\r\n");
                             }
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/PickUpCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/PickUpCommand.java
index a94643f4247d4ebfef57662fc09a7fda1f675397..227f4cb0766cdf9b812a4e38c01041291bde11c6 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/PickUpCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/PickUpCommand.java
@@ -15,7 +15,7 @@
  */
 package com.syncleus.aethermud.command.commands;
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.core.GameManager;
 import com.google.common.base.Joiner;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -43,7 +43,7 @@ public class PickUpCommand extends Command {
             originalMessageParts.remove(0);
             String desiredPickUpItem = Joiner.on(" ").join(originalMessageParts);
             for (String next : itemIds) {
-                Optional<Item> itemEntityOptional = entityManager.getItemEntity(next);
+                Optional<ItemInstance> itemEntityOptional = entityManager.getItemEntity(next);
                 if (!itemEntityOptional.isPresent()) {
                     continue;
                 }
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/ShowCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/ShowCommand.java
index 4324607df7d121d5413fa57616b82517e5fdba97..5070d2d15a467106cd6003cd17a1e870ac959f49 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/ShowCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/ShowCommand.java
@@ -15,7 +15,7 @@
  */
 package com.syncleus.aethermud.command.commands;
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.core.GameManager;
 import com.google.common.base.Joiner;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -41,7 +41,7 @@ public class ShowCommand extends Command {
             }
             originalMessageParts.remove(0);
             String target = Joiner.on(" ").join(originalMessageParts);
-            for (Item next : player.getInventory()) {
+            for (ItemInstance next : player.getInventory()) {
                 for (String s : next.getItemTriggers()) {
                     if (s.equalsIgnoreCase(target)) {
                         writeToRoom(player.getPlayerName() + " whips out " + next.getItemName() + ".\r\n");
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/UnequipCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/UnequipCommand.java
index 56dbe5df985500ae4917db4b1ea67778df2f7a44..5f65c87258241689225e5cbb0ccbf92c900a74e3 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/UnequipCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/UnequipCommand.java
@@ -16,7 +16,7 @@
 package com.syncleus.aethermud.command.commands;
 
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.core.GameManager;
 import com.google.common.base.Joiner;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -44,8 +44,8 @@ public class UnequipCommand extends Command {
             }
             originalMessageParts.remove(0);
             String itemTarget = Joiner.on(" ").join(originalMessageParts);
-            Set<Item> equipment = player.getEquipment();
-            for (Item item : equipment) {
+            Set<ItemInstance> equipment = player.getEquipment();
+            for (ItemInstance item : equipment) {
                 if (item.getItemTriggers().contains(itemTarget)) {
                     player.unEquip(item);
                     return;
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/UseCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/UseCommand.java
index 4370d81ba280c639efa28a21627af70333a08ff4..d84a32a24e9920b6cf251b75fc022b7c37fae7c8 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/UseCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/UseCommand.java
@@ -17,7 +17,7 @@ package com.syncleus.aethermud.command.commands;
 
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
 import org.apache.commons.lang.StringUtils;
@@ -49,7 +49,7 @@ public class UseCommand extends Command {
                 return;
             }
 
-            Optional<Item> inventoryItemOptional = player.getInventoryItem(useItemOn.getItem());
+            Optional<ItemInstance> inventoryItemOptional = player.getInventoryItem(useItemOn.getItem());
             if (!inventoryItemOptional.isPresent()) {
                 write("Useable item is not found in your inventory.\r\n");
                 return;
diff --git a/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadItemCommand.java b/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadItemCommand.java
index b9e82de009c127a48c1fd5a3f7521c0350e7dc6f..1b7036fdf728abfee1eae5b8eefc3b23790fb976 100644
--- a/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadItemCommand.java
+++ b/src/main/java/com/syncleus/aethermud/command/commands/admin/LoadItemCommand.java
@@ -17,9 +17,12 @@ 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.ItemMetadata;
+import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemImpl;
 import com.syncleus.aethermud.player.PlayerRole;
 import com.google.common.collect.Sets;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
@@ -33,6 +36,7 @@ import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 
 public class LoadItemCommand extends Command {
@@ -79,17 +83,20 @@ public class LoadItemCommand extends Command {
 
             String npcJson = EntityUtils.toString(entity);
 
-            ItemMetadata itemMetadata = null;
+            Item item;
             try {
-                itemMetadata = gameManager.getGson().fromJson(npcJson, ItemMetadata.class);
+                item = gameManager.getGson().fromJson(npcJson, ItemImpl.class);
             } catch (Exception ex) {
                 write("Retrieved JSON file is malformed. " + ex.getLocalizedMessage() + "\r\n");
                 return;
             }
             httpGet.reset();
 
-            gameManager.getItemStorage().saveItemMetadata(itemMetadata);
-            write("Item Saved. - " + itemMetadata.getInternalItemName() + "\r\n");
+            try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                tx.getStorage().saveItem(item);
+                tx.success();
+            }
+            write("Item Saved. - " + item.getInternalItemName() + "\r\n");
 
         });
     }
diff --git a/src/main/java/com/syncleus/aethermud/configuration/ConfigureNpc.java b/src/main/java/com/syncleus/aethermud/configuration/ConfigureNpc.java
index 9aae9c6cb020b16e55698f5a64e2a285d9195b6b..19ee41dc6a675e7dbf199899daafa7a0bdb7677b 100644
--- a/src/main/java/com/syncleus/aethermud/configuration/ConfigureNpc.java
+++ b/src/main/java/com/syncleus/aethermud/configuration/ConfigureNpc.java
@@ -20,13 +20,14 @@ import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.entity.EntityManager;
 import com.syncleus.aethermud.items.Forage;
-import com.syncleus.aethermud.items.ItemMetadata;
+import com.syncleus.aethermud.items.Item;
 import com.syncleus.aethermud.merchant.Merchant;
 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 com.syncleus.aethermud.storage.graphdb.model.ItemData;
 
 import java.io.IOException;
 import java.util.List;
@@ -53,20 +54,24 @@ public class ConfigureNpc {
 
         configureAllNpcs(gameManager);
 
-        List<ItemMetadata> allItemMetadata = gameManager.getItemStorage().getItemMetadatas();
-
-        for (ItemMetadata itemMetadata : allItemMetadata) {
-            for (SpawnRule spawnRule : itemMetadata.getSpawnRules()) {
-                Main.startUpMessage("Adding item spawn: " + itemMetadata.getInternalItemName());
-                ItemSpawner itemSpawner = new ItemSpawner(itemMetadata, spawnRule, gameManager);
-                entityManager.addEntity(itemSpawner);
-            }
-        }
+        try( GraphStorageFactory.AetherMudTx tx = gameManager.getGraphStorageFactory().beginTransaction() ) {
+            List<? extends ItemData> allItem = tx.getStorage().getAllItems();
 
-        for (ItemMetadata itemMetadata : allItemMetadata) {
-            for (Forage forage : itemMetadata.getForages()) {
-                Main.startUpMessage("Adding forage: " + itemMetadata.getInternalItemName());
-                gameManager.getForageManager().addForage(itemMetadata.getInternalItemName(), forage);
+            for (ItemData itemData : allItem) {
+                Item item = ItemData.copyItem(itemData);
+                if( item.getSpawnRules() != null ) {
+                    for (SpawnRule spawnRule : item.getSpawnRules()) {
+                        Main.startUpMessage("Adding item spawn: " + item.getInternalItemName());
+                        ItemSpawner itemSpawner = new ItemSpawner(item, spawnRule, gameManager);
+                        entityManager.addEntity(itemSpawner);
+                    }
+                }
+                if( item.getForages() != null ) {
+                    for (Forage forage : item.getForages()) {
+                        Main.startUpMessage("Adding forage: " + item.getInternalItemName());
+                        gameManager.getForageManager().addForage(item.getInternalItemName(), forage);
+                    }
+                }
             }
         }
 
diff --git a/src/main/java/com/syncleus/aethermud/core/GameManager.java b/src/main/java/com/syncleus/aethermud/core/GameManager.java
index 3c9aac2e0d115ed29ac45e757a002304663f1075..32eca58eb58435e534a805f589b52428b27ed71b 100644
--- a/src/main/java/com/syncleus/aethermud/core/GameManager.java
+++ b/src/main/java/com/syncleus/aethermud/core/GameManager.java
@@ -102,7 +102,6 @@ public class GameManager {
     private final Spells spells;
     private final MultiThreadedEventProcessor eventProcessor = new MultiThreadedEventProcessor(new ArrayBlockingQueue<>(10000));
     private final Room detainmentRoom;
-    private final ItemStorage itemStorage;
     private final HttpClient httpclient;
     private final Gson gson;
     private final FilebasedJsonStorage filebasedJsonStorage;
@@ -145,7 +144,6 @@ public class GameManager {
         this.detainmentRoom = buildDetainmentRoom();
         this.gson = new GsonBuilder().setPrettyPrinting().create();
         this.filebasedJsonStorage = new FilebasedJsonStorage(gson);
-        this.itemStorage = new ItemStorage(filebasedJsonStorage);
         this.merchantStorage = new MerchantStorage(this, filebasedJsonStorage);
         this.httpclient = httpClient;
     }
@@ -158,10 +156,6 @@ public class GameManager {
         return gson;
     }
 
-    public ItemStorage getItemStorage() {
-        return itemStorage;
-    }
-
     private Room buildDetainmentRoom() {
         BasicRoomBuilder basicRoomBuilder = new BasicRoomBuilder(this);
 
@@ -343,12 +337,12 @@ public class GameManager {
         }
 
         for (String itemId : playerCurrentRoom.getItemIds()) {
-            Optional<Item> itemOptional = entityManager.getItemEntity(itemId);
+            Optional<ItemInstance> itemOptional = entityManager.getItemEntity(itemId);
             if (!itemOptional.isPresent()) {
                 playerCurrentRoom.removePresentItem(itemId);
                 continue;
             }
-            Item item = itemOptional.get();
+            ItemInstance item = itemOptional.get();
             sb.append("   ").append(item.getRestingName()).append("\r\n");
         }
 
@@ -465,11 +459,11 @@ public class GameManager {
     }
 
     public void placeItemInRoom(Integer roomId, String itemId) {
-        Optional<Item> itemOptional = entityManager.getItemEntity(itemId);
+        Optional<ItemInstance> itemOptional = entityManager.getItemEntity(itemId);
         if (!itemOptional.isPresent()) {
             throw new IllegalArgumentException("itemId not valid.");
         }
-        Item item = itemOptional.get();
+        ItemInstance item = itemOptional.get();
         roomManager.getRoom(roomId).addPresentItem(item.getItemId());
     }
 
@@ -494,21 +488,21 @@ public class GameManager {
         synchronized (interner.intern(itemId)) {
             Stats playerStatsWithEquipmentAndLevel = player.getPlayerStatsWithEquipmentAndLevel();
             if (player.getInventory().size() < playerStatsWithEquipmentAndLevel.getInventorySize()) {
-                Optional<Item> itemOptional = entityManager.getItemEntity(itemId);
+                Optional<ItemInstance> itemOptional = entityManager.getItemEntity(itemId);
                 if (!itemOptional.isPresent()) {
                     return false;
                 }
-                Item itemEntity = itemOptional.get();
+                ItemInstance itemEntity = itemOptional.get();
                 itemEntity.setWithPlayer(true);
                 player.addInventoryId(itemId);
                 entityManager.saveItem(itemEntity);
                 return true;
             } else {
-                Optional<Item> itemOptional = entityManager.getItemEntity(itemId);
+                Optional<ItemInstance> itemOptional = entityManager.getItemEntity(itemId);
                 if (!itemOptional.isPresent()) {
                     return false;
                 }
-                Item itemEntity = itemOptional.get();
+                ItemInstance itemEntity = itemOptional.get();
                 channelUtils.write(player.getPlayerId(), "Your inventory is full, drop some items to free up room.\r\n");
                 if (isFromLoot) {
                     player.getCurrentRoom().addPresentItem(itemId);
diff --git a/src/main/java/com/syncleus/aethermud/entity/EntityManager.java b/src/main/java/com/syncleus/aethermud/entity/EntityManager.java
index 48e6f2040b2733fbccc481aef62673263fd75bbf..61347dd9b7fca66185b18b32b959e184b02f542f 100644
--- a/src/main/java/com/syncleus/aethermud/entity/EntityManager.java
+++ b/src/main/java/com/syncleus/aethermud/entity/EntityManager.java
@@ -16,16 +16,18 @@
 package com.syncleus.aethermud.entity;
 
 import com.google.common.base.Function;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
 import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.SentryManager;
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.items.ItemBuilder;
 import com.syncleus.aethermud.npc.NpcSpawn;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.player.PlayerManager;
 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.ItemInstanceData;
 import com.syncleus.aethermud.world.RoomManager;
 import com.syncleus.aethermud.world.model.Room;
 import org.apache.log4j.Logger;
@@ -50,6 +52,7 @@ public class EntityManager {
     private final Map<String, NpcSpawn> npcs = new ConcurrentHashMap<>();
     private final Map<String, AetherMudEntity> entities = new ConcurrentHashMap<>();
     private final ExecutorService mainTickExecutorService = Executors.newFixedThreadPool(50);
+    private final Interner<String> interner = Interners.newWeakInterner();
 
     public EntityManager(GraphStorageFactory graphStorageFactory, RoomManager roomManager, PlayerManager playerManager) {
         this.graphStorageFactory = graphStorageFactory;
@@ -81,23 +84,31 @@ public class EntityManager {
         }
     }
 
-    public ItemData saveItem(Item item) {
-        return this.transact(storage -> storage.saveItem(item));
+    public void saveItem(ItemInstance item) {
+        synchronized (interner.intern(item.getItemId())) {
+            this.transact(storage -> storage.saveItemEntity(item));
+        }
     }
 
-    public void removeItem(Item item) {
-        this.consume(storage -> storage.removeItem(item.getItemId()));
+    public void removeItem(ItemInstance item) {
+        synchronized (interner.intern(item.getItemId())) {
+            this.consume(storage -> storage.removeItemEntity(item.getItemId()));
+        }
     }
 
     public void removeItem(String itemId) {
-        this.consume(storage -> storage.removeItem(itemId));
+        synchronized (interner.intern(itemId)) {
+            this.consume(storage -> storage.removeItemEntity(itemId));
+        }
     }
 
-    public Optional<Item> getItemEntity(String itemId) {
-        return this.transactRead(storage -> {
-            Optional<ItemData> item = storage.getItemEntity(itemId);
-            return item.map(itemData -> new ItemBuilder().from(ItemData.copyItem(itemData)).create());
-        });
+    public Optional<ItemInstance> getItemEntity(String itemId) {
+        synchronized (interner.intern(itemId)) {
+            return this.transactRead(storage -> {
+                Optional<ItemInstanceData> item = storage.getItemEntity(itemId);
+                return item.map(itemData -> new ItemBuilder().from(ItemInstanceData.copyItem(itemData)).create());
+            });
+        }
     }
 
     public void deleteNpcEntity(String npcId) {
diff --git a/src/main/java/com/syncleus/aethermud/items/ForageManager.java b/src/main/java/com/syncleus/aethermud/items/ForageManager.java
index 0e9198830059bd03d0e518e7f2f6b72e1ea9c36f..0fc82b06849184a57c4c2785d56d5bb9a6bdbf3d 100644
--- a/src/main/java/com/syncleus/aethermud/items/ForageManager.java
+++ b/src/main/java/com/syncleus/aethermud/items/ForageManager.java
@@ -20,6 +20,8 @@ import com.syncleus.aethermud.player.CoolDownType;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.server.communication.Color;
 import com.syncleus.aethermud.stats.Stats;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
 import com.syncleus.aethermud.world.model.Area;
 import com.syncleus.aethermud.world.model.Room;
 import org.apache.log4j.Logger;
@@ -81,24 +83,27 @@ public class ForageManager {
                 //System.out.prlongln("you get a boost of " + pctSuccessBoostForLevel);
                 foragePctOfSuccess = (foragePctOfSuccess * pctSuccessBoostForLevel) + foragePctOfSuccess;
                 //System.out.prlongln("final pct of success for forage: " + foragePctOfSuccess);
-                Optional<ItemMetadata> itemMetadataOptional = gameManager.getItemStorage().get(forage.getInternalItemName());
-                if (!itemMetadataOptional.isPresent()) {
-                    continue;
+                Item item;
+                try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                    Optional<ItemData> itemOptional = tx.getStorage().getItem(forage.getInternalItemName());
+                    if (!itemOptional.isPresent()) {
+                        continue;
+                    }
+                    item = ItemData.copyItem(itemOptional.get());
                 }
-                ItemMetadata itemMetadata = itemMetadataOptional.get();
                 if (getRandPercent(foragePctOfSuccess)) {
                     player.updatePlayerForageExperience(forage.getForageExperience());
                     long numberToHarvest = randInt(forage.getMinAmt(), forage.getMaxAmt());
                     totalForageXp += forage.getForageExperience();
                     for (long i = 0; i < numberToHarvest; i++) {
                         countOfForagesFound++;
-                        Item item = new ItemBuilder().from(itemMetadata).create();
-                        gameManager.getEntityManager().saveItem(item);
-                        gameManager.acquireItem(player, item.getItemId());
+                        ItemInstance itemInstance = new ItemBuilder().from(item).create();
+                        gameManager.getEntityManager().saveItem(itemInstance);
+                        gameManager.acquireItem(player, itemInstance.getItemId());
                     }
-                    gameManager.writeToRoom(room.getRoomId(), player.getPlayerName() + " foraged (" + numberToHarvest + ") " + itemMetadata.getItemName() + "\r\n");
+                    gameManager.writeToRoom(room.getRoomId(), player.getPlayerName() + " foraged (" + numberToHarvest + ") " + item.getItemName() + "\r\n");
                 } else {
-                    gameManager.getChannelUtils().write(player.getPlayerId(), "Attempt to forage " + itemMetadata.getItemName() + " failed.\r\n");
+                    gameManager.getChannelUtils().write(player.getPlayerId(), "Attempt to forage " + item.getItemName() + " failed.\r\n");
                     //System.out.prlongln("failed to obtain forage, random pctsuccess failed.");
                 }
             }
diff --git a/src/main/java/com/syncleus/aethermud/items/Item.java b/src/main/java/com/syncleus/aethermud/items/Item.java
index d12c09b9c6653ddbc756ca381729579aad55dcc7..30ed0410d2c892ea87987cb0fbeb48da1efc121a 100644
--- a/src/main/java/com/syncleus/aethermud/items/Item.java
+++ b/src/main/java/com/syncleus/aethermud/items/Item.java
@@ -15,242 +15,81 @@
  */
 package com.syncleus.aethermud.items;
 
-
 import com.syncleus.aethermud.core.service.TimeTracker;
+import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.stats.Stats;
-import com.google.common.collect.Lists;
 
-import java.io.Serializable;
 import java.util.List;
 import java.util.Set;
-import java.util.UUID;
-
-public class Item implements Serializable {
-
-    private String itemName;
-    private String itemDescription;
-    private String internalItemName;
-    private List<String> itemTriggers;
-    private String restingName;
-    private String itemId;
-    private int numberOfUses;
-    private boolean isWithPlayer;
-    private Loot loot;
-    private int itemHalfLifeTicks;
-    private Equipment equipment;
-    private Rarity rarity;
-    private int valueInGold;
-    private Set<Effect> effects;
-    private boolean hasBeenWithPlayer;
-    private int maxUses;
-    private boolean isDisposable;
-    private List<TimeTracker.TimeOfDay> validTimeOfDays;
-    private Stats itemApplyStats;
-
-    public static final String CORPSE_INTENAL_NAME = "corpse";
-
-    public Item() {
-    }
-
-    protected Item(String itemName, String itemDescription, String internalItemName, List<String> itemTriggers, String restingName, String itemId, int numberOfUses, boolean isWithPlayer, Loot loot, int itemHalfLifeTicks, Equipment equipment, Rarity rarity, int valueInGold, Set<Effect> effects, boolean hasBeenWithPlayer, int maxUses, boolean isDisposable, Set<TimeTracker.TimeOfDay> validTimeOfDays, Stats itemApplyStats) {
-        this.itemName = itemName;
-        this.itemDescription = itemDescription;
-        this.internalItemName = internalItemName;
-        this.itemTriggers = itemTriggers;
-        this.restingName = restingName;
-        this.itemId = itemId;
-        this.numberOfUses = numberOfUses;
-        this.isWithPlayer = isWithPlayer;
-        this.loot = loot;
-        this.itemHalfLifeTicks = itemHalfLifeTicks;
-        this.equipment = equipment;
-        this.rarity = rarity;
-        this.valueInGold = valueInGold;
-        this.effects = effects;
-        this.hasBeenWithPlayer = hasBeenWithPlayer;
-        this.maxUses = maxUses;
-        this.isDisposable = isDisposable;
-        this.validTimeOfDays = ( validTimeOfDays == null ? Lists.newArrayList() : Lists.newArrayList(validTimeOfDays));
-        this.itemApplyStats = itemApplyStats;
-    }
-
-    public Stats getItemApplyStats() {
-        return itemApplyStats;
-    }
-
-    public List<TimeTracker.TimeOfDay> getValidTimeOfDays() {
-        return validTimeOfDays;
-    }
-
-    public void setValidTimeOfDays(Set<TimeTracker.TimeOfDay> validTimeOfDays) {
-        this.validTimeOfDays = Lists.newArrayList(validTimeOfDays);
-    }
-
-    public void setValidTimeOfDays(List<TimeTracker.TimeOfDay> validTimeOfDays) {
-        this.validTimeOfDays = Lists.newArrayList(validTimeOfDays);
-    }
-
-    public boolean isDisposable() {
-        return isDisposable;
-    }
-
-    public int getMaxUses() {
-        return maxUses;
-    }
-
-    public boolean isWithPlayer() {
-        return isWithPlayer;
-    }
-
-    public void setWithPlayer(boolean isWithPlayer) {
-        if (isWithPlayer) {
-            setHasBeenWithPlayer(true);
-        }
-        this.isWithPlayer = isWithPlayer;
-    }
-
-    public int getNumberOfUses() {
-        return numberOfUses;
-    }
-
-    public void setNumberOfUses(int numberOfUses) {
-        this.numberOfUses = numberOfUses;
-    }
-
-    public String getItemId() {
-        return itemId;
-    }
-
-
-    public String getInternalItemName() {
-        return internalItemName;
-    }
-
-    public String getItemName() {
-        return itemName;
-    }
-
-    public String getItemDescription() {
-        return itemDescription;
-    }
-
-    public List<String> getItemTriggers() {
-        return itemTriggers;
-    }
-
-    public String getRestingName() {
-        return restingName;
-    }
-
-    public int getItemHalfLifeTicks() {
-        return itemHalfLifeTicks;
-    }
-
-    public Loot getLoot() {
-        return loot;
-    }
-
-    public void setEquipment(Equipment equipment) {
-        this.equipment = equipment;
-    }
-
-    public void setHasBeenWithPlayer(boolean hasBeenWithPlayer) {
-        this.hasBeenWithPlayer = hasBeenWithPlayer;
-    }
-
-    public Equipment getEquipment() {
-        return equipment;
-    }
-
-    public Rarity getRarity() {
-        return rarity;
-    }
-
-    public int getValueInGold() {
-        return valueInGold;
-    }
-
-    public void setEffects(Set<Effect> effects) {
-        this.effects = effects;
-    }
-
-    public Set<Effect> getEffects() {
-        return effects;
-    }
-
-    public void setItemName(String itemName) {
-        this.itemName = itemName;
-    }
-
-    public void setItemDescription(String itemDescription) {
-        this.itemDescription = itemDescription;
-    }
-
-    public void setInternalItemName(String internalItemName) {
-        this.internalItemName = internalItemName;
-    }
-
-    public void setItemTriggers(List<String> itemTriggers) {
-        this.itemTriggers = itemTriggers;
-    }
-
-    public void setRestingName(String restingName) {
-        this.restingName = restingName;
-    }
-
-    public void setItemId(String itemId) {
-        this.itemId = itemId;
-    }
-
-    public void setLoot(Loot loot) {
-        this.loot = loot;
-    }
-
-    public void setItemHalfLifeTicks(int itemHalfLifeTicks) {
-        this.itemHalfLifeTicks = itemHalfLifeTicks;
-    }
-
-    public void setRarity(Rarity rarity) {
-        this.rarity = rarity;
-    }
-
-    public void setValueInGold(int valueInGold) {
-        this.valueInGold = valueInGold;
-    }
-
-    public void setMaxUses(int maxUses) {
-        this.maxUses = maxUses;
-    }
-
-    public void setDisposable(boolean disposable) {
-        isDisposable = disposable;
-    }
-
-    public void setItemApplyStats(Stats itemApplyStats) {
-        this.itemApplyStats = itemApplyStats;
-    }
-
-    public boolean isHasBeenWithPlayer() {
-        return hasBeenWithPlayer;
-    }
-
-    public static Item createCorpseItem(String name, Loot loot) {
-
-        Item item = new ItemBuilder()
-                .internalItemName(Item.CORPSE_INTENAL_NAME)
-                .itemName(name + " corpse")
-                .itemDescription("a bloody corpse")
-                .itemTriggers(Lists.newArrayList("corpse", "c", name, name + " corpse"))
-                .itemId(UUID.randomUUID().toString())
-                .itemHalfLifeTicks(120)
-                .rarity(Rarity.BASIC)
-                .valueInGold(5)
-                .isDisposable(false)
-                .restingName("a corpse lies on the ground.")
-                .loot(loot)
-                .create();
-
-        return item;
-
-    }
+
+public interface Item {
+    Stats getItemApplyStats();
+
+    List<TimeTracker.TimeOfDay> getValidTimeOfDays();
+
+    void setValidTimeOfDays(Set<TimeTracker.TimeOfDay> validTimeOfDays);
+
+    void setValidTimeOfDays(List<TimeTracker.TimeOfDay> validTimeOfDays);
+
+    boolean isDisposable();
+
+    int getMaxUses();
+
+    String getInternalItemName();
+
+    String getItemName();
+
+    String getItemDescription();
+
+    List<String> getItemTriggers();
+
+    String getRestingName();
+
+    int getItemHalfLifeTicks();
+
+    Loot getLoot();
+
+    void setEquipment(Equipment equipment);
+
+    Equipment getEquipment();
+
+    Rarity getRarity();
+
+    int getValueInGold();
+
+    void setEffects(Set<Effect> effects);
+
+    Set<Effect> getEffects();
+
+    void setItemName(String itemName);
+
+    void setItemDescription(String itemDescription);
+
+    void setInternalItemName(String internalItemName);
+
+    void setItemTriggers(List<String> itemTriggers);
+
+    void setRestingName(String restingName);
+
+    void setLoot(Loot loot);
+
+    void setItemHalfLifeTicks(int itemHalfLifeTicks);
+
+    void setRarity(Rarity rarity);
+
+    void setValueInGold(int valueInGold);
+
+    void setMaxUses(int maxUses);
+
+    void setDisposable(boolean disposable);
+
+    void setItemApplyStats(Stats itemApplyStats);
+
+    Set<SpawnRule> getSpawnRules();
+
+    void setSpawnRules(Set<SpawnRule> spawnRules);
+
+    Set<Forage> getForages();
+
+    void setForages(Set<Forage> forages);
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java b/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
index 03299fa980b3b29363be85147ff2e9f03be7953e..5d7d49d40ba5e9a2e377dc04c36c97a68403e156 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemBuilder.java
@@ -18,8 +18,10 @@ package com.syncleus.aethermud.items;
 
 import com.google.common.collect.Sets;
 import com.syncleus.aethermud.core.service.TimeTracker;
+import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.stats.Stats;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
@@ -45,32 +47,36 @@ public class ItemBuilder {
     private boolean isDisposable;
     private Set<TimeTracker.TimeOfDay> validTimeOfDays;
     private Stats itemApplyStats;
-
-    public ItemBuilder from(ItemMetadata itemMetadata) {
-        this.internalItemName = itemMetadata.getInternalItemName();
-        this.itemName = itemMetadata.getItemName();
-        this.itemDescription = itemMetadata.getItemDescription();
-        this.itemTriggers = itemMetadata.getItemTriggers();
-        this.restingName = itemMetadata.getRestingName();
+    private Set<SpawnRule> spawnRules;
+    private Set<Forage> forages;
+
+    public ItemBuilder from(Item item) {
+        this.internalItemName = item.getInternalItemName();
+        this.itemName = item.getItemName();
+        this.itemDescription = item.getItemDescription();
+        this.itemTriggers = item.getItemTriggers();
+        this.restingName = item.getRestingName();
         this.itemId = UUID.randomUUID().toString();
         // zero uses, its new.
         this.numberOfUses = 0;
         this.isWithPlayer = false;
-        this.itemHalfLifeTicks = itemMetadata.getItemHalfLifeTicks();
-        this.rarity = itemMetadata.getRarity();
-        this.valueInGold = itemMetadata.getValueInGold();
-        this.maxUses = itemMetadata.getMaxUses();
+        this.itemHalfLifeTicks = item.getItemHalfLifeTicks();
+        this.rarity = item.getRarity();
+        this.valueInGold = item.getValueInGold();
+        this.maxUses = item.getMaxUses();
         this.loot = null;
-        this.isDisposable = itemMetadata.isDisposable();
-        this.equipment = itemMetadata.getEquipment();
-        this.validTimeOfDays = itemMetadata.getValidTimeOfDays();
-        Set<Effect> effects = itemMetadata.getEffects();
-        this.effects = (effects != null ? Sets.newHashSet(itemMetadata.getEffects()) : null );
-        this.itemApplyStats = itemMetadata.getItemApplyStats();
+        this.isDisposable = item.isDisposable();
+        this.equipment = item.getEquipment();
+        this.validTimeOfDays = new HashSet<>(item.getValidTimeOfDays());
+        Set<Effect> effects = item.getEffects();
+        this.effects = (effects != null ? Sets.newHashSet(item.getEffects()) : null );
+        this.itemApplyStats = item.getItemApplyStats();
+        this.spawnRules = (spawnRules != null ? Sets.newHashSet(item.getSpawnRules()) : null);
+        this.forages = (forages != null ? Sets.newHashSet(item.getForages()) : null);
         return this;
     }
 
-    public ItemBuilder from(Item origItem) {
+    public ItemBuilder from(ItemInstance origItem) {
         this.internalItemName = origItem.getInternalItemName();
         this.itemName = origItem.getItemName();
         this.itemDescription = origItem.getItemDescription();
@@ -92,6 +98,8 @@ public class ItemBuilder {
         this.isDisposable = origItem.isDisposable();
         this.validTimeOfDays = Sets.newHashSet(origItem.getValidTimeOfDays());
         this.itemApplyStats = origItem.getItemApplyStats();
+        this.spawnRules = (spawnRules != null ? Sets.newHashSet(origItem.getSpawnRules()) : null);
+        this.forages = (forages != null ? Sets.newHashSet(origItem.getForages()) : null);
         return this;
     }
 
@@ -190,8 +198,9 @@ public class ItemBuilder {
         return this;
     }
 
-    public Item create() {
-        return new Item(itemName, itemDescription, internalItemName, itemTriggers, restingName, itemId, numberOfUses, isWithPlayer, loot, itemHalfLifeTicks, equipment, rarity, valueInGold, effects, hasBeenWithPlayer, maxUses, isDisposable, validTimeOfDays, itemApplyStats);
+    public ItemInstance create() {
+        Item item = new ItemImpl(itemName, itemDescription, internalItemName, itemTriggers, restingName, loot, itemHalfLifeTicks, equipment, rarity, valueInGold, effects, maxUses, isDisposable, validTimeOfDays, itemApplyStats, spawnRules, forages);
+        return new ItemInstanceImpl(item, itemId, numberOfUses, isWithPlayer, hasBeenWithPlayer);
     }
 
 
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemDecayManager.java b/src/main/java/com/syncleus/aethermud/items/ItemDecayManager.java
index e4a46875671e1d222fa6cf40b4f2084fdd277f6a..c854e08e255780986ceefb77369d23a2c5a54eb9 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemDecayManager.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemDecayManager.java
@@ -38,7 +38,7 @@ public class ItemDecayManager extends AetherMudEntity {
         this.gameManager = gameManager;
     }
 
-    public void addItem(Item item) {
+    public void addItem(ItemInstance item) {
         item.setWithPlayer(false);
         itemDecayTracker.put(item.getItemId(), new DecayProgress(item.getItemHalfLifeTicks()));
     }
@@ -58,12 +58,12 @@ public class ItemDecayManager extends AetherMudEntity {
                 ConcurrentHashMap<String, DecayProgress> itemDecayTracker1 = getItemDecayTracker();
                 for (Map.Entry<String, DecayProgress> next : itemDecayTracker1.entrySet()) {
                     DecayProgress decayProgress = next.getValue();
-                    Optional<Item> itemOptional = entityManager.getItemEntity(next.getKey());
+                    Optional<ItemInstance> itemOptional = entityManager.getItemEntity(next.getKey());
                     if (!itemOptional.isPresent()) {
                         removeItemFromDecayManager(next.getKey());
                         continue;
                     }
-                    Item item = itemOptional.get();
+                    ItemInstance item = itemOptional.get();
                     if (item.isWithPlayer()) {
                         removeItemFromDecayManager(item.getItemId());
                         continue;
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java b/src/main/java/com/syncleus/aethermud/items/ItemImpl.java
similarity index 58%
rename from src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
rename to src/main/java/com/syncleus/aethermud/items/ItemImpl.java
index 375e2b298c13290c597ae720260f9377193a9432..18d60ec8167f91c4985ee64cb4f615fd1563a836 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemMetadata.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemImpl.java
@@ -16,169 +16,233 @@
 package com.syncleus.aethermud.items;
 
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.syncleus.aethermud.common.ColorizedTextTemplate;
 import com.syncleus.aethermud.core.service.TimeTracker;
 import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.stats.Stats;
-import com.google.common.collect.Sets;
 
 import java.util.List;
 import java.util.Set;
 
-public class ItemMetadata {
+public class ItemImpl implements Item {
 
-    // Used for persisting to disk (file-name)
-    // Spaces become underscores.
-    // Needs to be unique across all itemmetadata's.
-    // is essentially serving as the item "type".
-    private String internalItemName;
-    // This is the unique identifier to represent this itemmetadata (which drives itemtype)
     private String itemName;
     private String itemDescription;
+    private String internalItemName;
+    private List<String> itemTriggers;
     private String restingName;
-    private int valueInGold;
+    private Loot loot;
     private int itemHalfLifeTicks;
-    private Rarity rarity;
     private Equipment equipment;
+    private Rarity rarity;
+    private int valueInGold;
     private Set<Effect> effects;
-    private List<String> itemTriggers;
-    private Set<TimeTracker.TimeOfDay> validTimeOfDays;
-    private boolean isDisposable;
     private int maxUses;
-    private Set<SpawnRule> spawnRules;
+    private boolean isDisposable;
+    private List<TimeTracker.TimeOfDay> validTimeOfDays;
     private Stats itemApplyStats;
+    private Set<SpawnRule> spawnRules;
     private Set<Forage> forages;
 
-    public Set<Forage> getForages() {
-        if (forages == null) {
-            return Sets.newHashSet();
-        }
-        return forages;
+    public ItemImpl() {
     }
 
-    public void setForages(Set<Forage> forages) {
-        this.forages = forages;
+    public ItemImpl(String itemName, String itemDescription, String internalItemName, List<String> itemTriggers, String restingName, Loot loot, int itemHalfLifeTicks, Equipment equipment, Rarity rarity, int valueInGold, Set<Effect> effects, int maxUses, boolean isDisposable, Set<TimeTracker.TimeOfDay> validTimeOfDays, Stats itemApplyStats, Set<SpawnRule> spawnRules, Set<Forage> forages) {
+        this.itemName = itemName;
+        this.itemDescription = itemDescription;
+        this.internalItemName = internalItemName;
+        this.itemTriggers = Lists.newArrayList(itemTriggers);
+        this.restingName = restingName;
+        this.loot = loot;
+        this.itemHalfLifeTicks = itemHalfLifeTicks;
+        this.equipment = equipment;
+        this.rarity = rarity;
+        this.valueInGold = valueInGold;
+        this.effects = Sets.newHashSet(effects);
+        this.maxUses = maxUses;
+        this.isDisposable = isDisposable;
+        this.validTimeOfDays = (validTimeOfDays == null ? Lists.newArrayList() : Lists.newArrayList(validTimeOfDays));
+        this.itemApplyStats = itemApplyStats;
+        this.spawnRules = (spawnRules == null ? Sets.newHashSet() : Sets.newHashSet(spawnRules));
+        this.forages = (forages == null ? Sets.newHashSet() : Sets.newHashSet(forages));
     }
 
+    @Override
     public Stats getItemApplyStats() {
         return itemApplyStats;
     }
 
-    public void setItemApplyStats(Stats itemApplyStats) {
-        this.itemApplyStats = itemApplyStats;
+    @Override
+    public List<TimeTracker.TimeOfDay> getValidTimeOfDays() {
+        return validTimeOfDays;
     }
 
-    public String getItemName() {
-        return itemName;
+    @Override
+    public void setValidTimeOfDays(Set<TimeTracker.TimeOfDay> validTimeOfDays) {
+        this.validTimeOfDays = Lists.newArrayList(validTimeOfDays);
     }
 
-    public void setItemName(String itemName) {
-        this.itemName = itemName;
+    @Override
+    public void setValidTimeOfDays(List<TimeTracker.TimeOfDay> validTimeOfDays) {
+        this.validTimeOfDays = Lists.newArrayList(validTimeOfDays);
     }
 
-    public String getItemDescription() {
-        return itemDescription;
+    @Override
+    public boolean isDisposable() {
+        return isDisposable;
     }
 
-    public void setItemDescription(String itemDescription) {
-        this.itemDescription = itemDescription;
+    @Override
+    public int getMaxUses() {
+        return maxUses;
     }
 
-    public String getRestingName() {
-        return restingName;
+
+    @Override
+    public String getInternalItemName() {
+        return internalItemName;
     }
 
-    public void setRestingName(String restingName) {
-        this.restingName = restingName;
+    @Override
+    public String getItemName() {
+        return ColorizedTextTemplate.renderFromTemplateLanguage(itemName);
     }
 
-    public int getValueInGold() {
-        return valueInGold;
+    @Override
+    public void setItemName(String itemName) {
+        this.itemName = ColorizedTextTemplate.renderToTemplateLanguage(itemName);
     }
 
-    public void setValueInGold(int valueInGold) {
-        this.valueInGold = valueInGold;
+    @Override
+    public String getItemDescription() {
+        return ColorizedTextTemplate.renderFromTemplateLanguage(itemDescription);
     }
 
-    public int getItemHalfLifeTicks() {
-        return itemHalfLifeTicks;
+    @Override
+    public List<String> getItemTriggers() {
+        return itemTriggers;
     }
 
-    public void setItemHalfLifeTicks(int itemHalfLifeTicks) {
-        this.itemHalfLifeTicks = itemHalfLifeTicks;
+    @Override
+    public String getRestingName() {
+        return ColorizedTextTemplate.renderFromTemplateLanguage(restingName);
+
     }
 
-    public Rarity getRarity() {
-        return rarity;
+    @Override
+    public int getItemHalfLifeTicks() {
+        return itemHalfLifeTicks;
     }
 
-    public void setRarity(Rarity rarity) {
-        this.rarity = rarity;
+    @Override
+    public Loot getLoot() {
+        return loot;
+    }
+
+    @Override
+    public void setEquipment(Equipment equipment) {
+        this.equipment = equipment;
     }
 
+    @Override
     public Equipment getEquipment() {
         return equipment;
     }
 
-    public void setEquipment(Equipment equipment) {
-        this.equipment = equipment;
+    @Override
+    public Rarity getRarity() {
+        return rarity;
     }
 
-    public Set<Effect> getEffects() {
-        return effects;
+    @Override
+    public int getValueInGold() {
+        return valueInGold;
     }
 
+    @Override
     public void setEffects(Set<Effect> effects) {
         this.effects = effects;
     }
 
-    public String getInternalItemName() {
-        return internalItemName;
+    @Override
+    public Set<Effect> getEffects() {
+        return effects;
     }
 
-    public List<String> getItemTriggers() {
-        return itemTriggers;
+    @Override
+    public void setItemDescription(String itemDescription) {
+        this.itemDescription = ColorizedTextTemplate.renderToTemplateLanguage(itemDescription);
     }
 
+    @Override
+    public void setInternalItemName(String internalItemName) {
+        this.internalItemName = internalItemName;
+    }
+
+    @Override
     public void setItemTriggers(List<String> itemTriggers) {
         this.itemTriggers = itemTriggers;
     }
 
-    public void setInternalItemName(String internalItemName) {
-        this.internalItemName = internalItemName;
+    @Override
+    public void setRestingName(String restingName) {
+        this.restingName = ColorizedTextTemplate.renderToTemplateLanguage(restingName);
     }
 
-    public Set<TimeTracker.TimeOfDay> getValidTimeOfDays() {
-        return validTimeOfDays;
+    @Override
+    public void setLoot(Loot loot) {
+        this.loot = loot;
     }
 
-    public void setValidTimeOfDays(Set<TimeTracker.TimeOfDay> validTimeOfDays) {
-        this.validTimeOfDays = validTimeOfDays;
+    @Override
+    public void setItemHalfLifeTicks(int itemHalfLifeTicks) {
+        this.itemHalfLifeTicks = itemHalfLifeTicks;
     }
 
-    public int getMaxUses() {
-        return maxUses;
+    @Override
+    public void setRarity(Rarity rarity) {
+        this.rarity = rarity;
     }
 
-    public boolean isDisposable() {
-        return isDisposable;
+    @Override
+    public void setValueInGold(int valueInGold) {
+        this.valueInGold = valueInGold;
+    }
+
+    @Override
+    public void setMaxUses(int maxUses) {
+        this.maxUses = maxUses;
     }
 
+    @Override
     public void setDisposable(boolean disposable) {
         isDisposable = disposable;
     }
 
+    @Override
+    public void setItemApplyStats(Stats itemApplyStats) {
+        this.itemApplyStats = itemApplyStats;
+    }
+
+    @Override
     public Set<SpawnRule> getSpawnRules() {
-        if (spawnRules == null) {
-            return Sets.newHashSet();
-        }
         return spawnRules;
     }
 
+    @Override
     public void setSpawnRules(Set<SpawnRule> spawnRules) {
         this.spawnRules = spawnRules;
     }
 
-    public void setMaxUses(int maxUses) {
-        this.maxUses = maxUses;
+    @Override
+    public Set<Forage> getForages() {
+        return forages;
+    }
+
+    @Override
+    public void setForages(Set<Forage> forages) {
+        this.forages = forages;
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemInstance.java b/src/main/java/com/syncleus/aethermud/items/ItemInstance.java
new file mode 100644
index 0000000000000000000000000000000000000000..3562264b7672dc939a33eb4c4e51da9111ff220a
--- /dev/null
+++ b/src/main/java/com/syncleus/aethermud/items/ItemInstance.java
@@ -0,0 +1,64 @@
+/**
+ * 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.google.common.collect.Lists;
+
+import java.util.UUID;
+
+public interface ItemInstance extends Item {
+    String CORPSE_INTENAL_NAME = "corpse";
+
+    Item getItem();
+
+    void setItem(Item item);
+
+    boolean isWithPlayer();
+
+    void setWithPlayer(boolean isWithPlayer);
+
+    int getNumberOfUses();
+
+    void setNumberOfUses(int numberOfUses);
+
+    String getItemId();
+
+    void setHasBeenWithPlayer(boolean hasBeenWithPlayer);
+
+    void setItemId(String itemId);
+
+    boolean isHasBeenWithPlayer();
+
+    static ItemInstance createCorpseItem(String name, Loot loot) {
+
+        ItemInstance item = new ItemBuilder()
+            .internalItemName(CORPSE_INTENAL_NAME)
+            .itemName(name + " corpse")
+            .itemDescription("a bloody corpse")
+            .itemTriggers(Lists.newArrayList("corpse", "c", name, name + " corpse"))
+            .itemId(UUID.randomUUID().toString())
+            .itemHalfLifeTicks(120)
+            .rarity(Rarity.BASIC)
+            .valueInGold(5)
+            .isDisposable(false)
+            .restingName("a corpse lies on the ground.")
+            .loot(loot)
+            .create();
+
+        return item;
+
+    }
+}
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemInstanceImpl.java b/src/main/java/com/syncleus/aethermud/items/ItemInstanceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..b672f04a4f92f2cc3efeb3c6008c44d6b2401868
--- /dev/null
+++ b/src/main/java/com/syncleus/aethermud/items/ItemInstanceImpl.java
@@ -0,0 +1,271 @@
+/**
+ * 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.core.service.TimeTracker;
+import com.syncleus.aethermud.spawner.SpawnRule;
+import com.syncleus.aethermud.stats.Stats;
+
+import java.util.List;
+import java.util.Set;
+
+public class ItemInstanceImpl implements ItemInstance {
+
+    private Item item;
+    private String itemId;
+    private int numberOfUses;
+    private boolean isWithPlayer;
+    private boolean hasBeenWithPlayer;
+
+    public ItemInstanceImpl() {
+    }
+
+    public ItemInstanceImpl(Item item, String itemId, int numberOfUses, boolean isWithPlayer, boolean hasBeenWithPlayer) {
+        this.item = item;
+        this.itemId = itemId;
+        this.numberOfUses = numberOfUses;
+        this.isWithPlayer = isWithPlayer;
+        this.hasBeenWithPlayer = hasBeenWithPlayer;
+    }
+
+    @Override
+    public Item getItem() {
+        return item;
+    }
+
+    @Override
+    public void setItem(Item item) {
+        this.item = item;
+    }
+
+    @Override
+    public Set<SpawnRule> getSpawnRules() {
+        return item.getSpawnRules();
+    }
+
+    @Override
+    public void setSpawnRules(Set<SpawnRule> spawnRules) {
+        item.setSpawnRules(spawnRules);
+    }
+
+    @Override
+    public Set<Forage> getForages() {
+        return item.getForages();
+    }
+
+    @Override
+    public void setForages(Set<Forage> forages) {
+        item.setForages(forages);
+    }
+
+    @Override
+    public boolean isWithPlayer() {
+        return isWithPlayer;
+    }
+
+    @Override
+    public void setWithPlayer(boolean isWithPlayer) {
+        if (isWithPlayer) {
+            setHasBeenWithPlayer(true);
+        }
+        this.isWithPlayer = isWithPlayer;
+    }
+
+    @Override
+    public int getNumberOfUses() {
+        return numberOfUses;
+    }
+
+    @Override
+    public void setNumberOfUses(int numberOfUses) {
+        this.numberOfUses = numberOfUses;
+    }
+
+    @Override
+    public String getItemId() {
+        return itemId;
+    }
+
+    @Override
+    public void setHasBeenWithPlayer(boolean hasBeenWithPlayer) {
+        this.hasBeenWithPlayer = hasBeenWithPlayer;
+    }
+
+    @Override
+    public void setItemId(String itemId) {
+        this.itemId = itemId;
+    }
+
+    @Override
+    public boolean isHasBeenWithPlayer() {
+        return hasBeenWithPlayer;
+    }
+
+    @Override
+    public Stats getItemApplyStats() {
+        return item.getItemApplyStats();
+    }
+
+    @Override
+    public List<TimeTracker.TimeOfDay> getValidTimeOfDays() {
+        return item.getValidTimeOfDays();
+    }
+
+    @Override
+    public void setValidTimeOfDays(Set<TimeTracker.TimeOfDay> validTimeOfDays) {
+        item.setValidTimeOfDays(validTimeOfDays);
+    }
+
+    @Override
+    public void setValidTimeOfDays(List<TimeTracker.TimeOfDay> validTimeOfDays) {
+        item.setValidTimeOfDays(validTimeOfDays);
+    }
+
+    @Override
+    public boolean isDisposable() {
+        return item.isDisposable();
+    }
+
+    @Override
+    public int getMaxUses() {
+        return item.getMaxUses();
+    }
+
+    @Override
+    public String getInternalItemName() {
+        return item.getInternalItemName();
+    }
+
+    @Override
+    public String getItemName() {
+        return item.getItemName();
+    }
+
+    @Override
+    public String getItemDescription() {
+        return item.getItemDescription();
+    }
+
+    @Override
+    public List<String> getItemTriggers() {
+        return item.getItemTriggers();
+    }
+
+    @Override
+    public String getRestingName() {
+        return item.getRestingName();
+    }
+
+    @Override
+    public int getItemHalfLifeTicks() {
+        return item.getItemHalfLifeTicks();
+    }
+
+    @Override
+    public Loot getLoot() {
+        return item.getLoot();
+    }
+
+    @Override
+    public void setEquipment(Equipment equipment) {
+        item.setEquipment(equipment);
+    }
+
+    @Override
+    public Equipment getEquipment() {
+        return item.getEquipment();
+    }
+
+    @Override
+    public Rarity getRarity() {
+        return item.getRarity();
+    }
+
+    @Override
+    public int getValueInGold() {
+        return item.getValueInGold();
+    }
+
+    @Override
+    public void setEffects(Set<Effect> effects) {
+        item.setEffects(effects);
+    }
+
+    @Override
+    public Set<Effect> getEffects() {
+        return item.getEffects();
+    }
+
+    @Override
+    public void setItemName(String itemName) {
+        item.setItemName(itemName);
+    }
+
+    @Override
+    public void setItemDescription(String itemDescription) {
+        item.setItemDescription(itemDescription);
+    }
+
+    @Override
+    public void setInternalItemName(String internalItemName) {
+        item.setInternalItemName(internalItemName);
+    }
+
+    @Override
+    public void setItemTriggers(List<String> itemTriggers) {
+        item.setItemTriggers(itemTriggers);
+    }
+
+    @Override
+    public void setRestingName(String restingName) {
+        item.setRestingName(restingName);
+    }
+
+    @Override
+    public void setLoot(Loot loot) {
+        item.setLoot(loot);
+    }
+
+    @Override
+    public void setItemHalfLifeTicks(int itemHalfLifeTicks) {
+        item.setItemHalfLifeTicks(itemHalfLifeTicks);
+    }
+
+    @Override
+    public void setRarity(Rarity rarity) {
+        item.setRarity(rarity);
+    }
+
+    @Override
+    public void setValueInGold(int valueInGold) {
+        item.setValueInGold(valueInGold);
+    }
+
+    @Override
+    public void setMaxUses(int maxUses) {
+        item.setMaxUses(maxUses);
+    }
+
+    @Override
+    public void setDisposable(boolean disposable) {
+        item.setDisposable(disposable);
+    }
+
+    @Override
+    public void setItemApplyStats(Stats itemApplyStats) {
+        item.setItemApplyStats(itemApplyStats);
+    }
+}
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java b/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
index ec3ebe0ed48c4542778e5ca4301cfd5c1bcb8464..378d3c694e990eb797491815a46400602b673061 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemUseAction.java
@@ -24,9 +24,9 @@ import java.util.Set;
 public interface ItemUseAction {
     String getInternalItemName();
 
-    void executeAction(GameManager gameManager, Player player, Item item, UseCommand.UseItemOn useItemOn);
+    void executeAction(GameManager gameManager, Player player, ItemInstance item, UseCommand.UseItemOn useItemOn);
 
-    void postExecuteAction(GameManager gameManager, Player player, Item item);
+    void postExecuteAction(GameManager gameManager, Player player, ItemInstance item);
 
     Set<Effect> getEffects();
 
diff --git a/src/main/java/com/syncleus/aethermud/items/ItemUseHandler.java b/src/main/java/com/syncleus/aethermud/items/ItemUseHandler.java
index ce88b311bc0fd80453d92ddd8db6fea3c572a7b3..5546a1493f3d47239dbae1b7842f7c80f0dddb5f 100644
--- a/src/main/java/com/syncleus/aethermud/items/ItemUseHandler.java
+++ b/src/main/java/com/syncleus/aethermud/items/ItemUseHandler.java
@@ -22,6 +22,8 @@ import com.syncleus.aethermud.items.use.DefaultApplyEffectsStats;
 import com.syncleus.aethermud.items.use.LightningSpellBookUseAction;
 import com.syncleus.aethermud.items.use.StickOfJusticeUseAction;
 import com.syncleus.aethermud.player.Player;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
 import org.apache.log4j.Logger;
 
 import java.util.Optional;
@@ -35,33 +37,36 @@ public class ItemUseHandler {
         this.gameManager = gameManager;
     }
 
-    public void handle(Player player, Item item, UseCommand.UseItemOn useItemOn) {
+    public void handle(Player player, ItemInstance itemInstance, UseCommand.UseItemOn useItemOn) {
         ItemUseAction itemUseAction = null;
-        Optional<ItemMetadata> itemMetadataOptional = gameManager.getItemStorage().get(item.getInternalItemName());
-        if (!itemMetadataOptional.isPresent()) {
-            return;
+        Item item;
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            Optional<ItemData> itemOptional = tx.getStorage().getItem(itemInstance.getInternalItemName());
+            if (!itemOptional.isPresent()) {
+                return;
+            }
+            item = ItemData.copyItem(itemOptional.get());
         }
-        ItemMetadata itemMetadata = itemMetadataOptional.get();
-        switch (itemMetadata.getInternalItemName()) {
+        switch (item.getInternalItemName()) {
             case "lightning spellbook":
-                itemUseAction = new LightningSpellBookUseAction(itemMetadata);
+                itemUseAction = new LightningSpellBookUseAction(item);
                 break;
             case "stick of justice":
-                itemUseAction = new StickOfJusticeUseAction(itemMetadata);
+                itemUseAction = new StickOfJusticeUseAction(item);
                 break;
             default:
-                if ((item.getEffects() != null && item.getEffects().size() > 0) || (item.getItemApplyStats() != null)) {
-                    itemUseAction = new DefaultApplyEffectsStats(itemMetadata);
+                if ((itemInstance.getEffects() != null && itemInstance.getEffects().size() > 0) || (itemInstance.getItemApplyStats() != null)) {
+                    itemUseAction = new DefaultApplyEffectsStats(item);
                 }
                 break;
         }
         if (itemUseAction != null) {
-            itemUseAction.executeAction(gameManager, player, item, useItemOn);
-            itemUseAction.postExecuteAction(gameManager, player, item);
+            itemUseAction.executeAction(gameManager, player, itemInstance, useItemOn);
+            itemUseAction.postExecuteAction(gameManager, player, itemInstance);
         }
     }
 
-    public static void incrementUses(Item item) {
+    public static void incrementUses(ItemInstance item) {
         item.setNumberOfUses(item.getNumberOfUses() + 1);
     }
 }
diff --git a/src/main/java/com/syncleus/aethermud/items/LootManager.java b/src/main/java/com/syncleus/aethermud/items/LootManager.java
index ac9705af35786cb63e0acf50209763f5a7d6cf7f..176fdd87403f956a1f2394af289be8d0273d1a85 100644
--- a/src/main/java/com/syncleus/aethermud/items/LootManager.java
+++ b/src/main/java/com/syncleus/aethermud/items/LootManager.java
@@ -15,8 +15,12 @@
  */
 package com.syncleus.aethermud.items;
 
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
 import com.syncleus.aethermud.core.GameManager;
 import com.google.common.collect.Sets;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
 import org.apache.commons.lang.math.JVMRandom;
 
 import java.util.Optional;
@@ -24,14 +28,12 @@ import java.util.Random;
 import java.util.Set;
 
 public class LootManager {
-
     private final GameManager gameManager;
-
     public LootManager(GameManager gameManager) {
         this.gameManager = gameManager;
     }
-
     private final Random  random = new Random();
+    private final Interner<String> interner = Interners.newWeakInterner();
 
     private int randInt(int min, int max) {
         return (int) JVMRandom.nextLong((max - min) + 1) + min;
@@ -48,18 +50,23 @@ public class LootManager {
         return randInt(loot.getLootGoldMin(), loot.getLootGoldMax());
     }
 
-    public Set<Item> lootItemsReturn(Loot loot) {
-        Set<Item> lootItems = Sets.newHashSet();
+    public Set<ItemInstance> lootItemsReturn(Loot loot) {
+        Set<ItemInstance> lootItems = Sets.newHashSet();
         for (String internalItemName: loot.getInternalItemNames()) {
-            Optional<ItemMetadata> itemMetadataOptional = gameManager.getItemStorage().get(internalItemName);
-            if (!itemMetadataOptional.isPresent()) {
-                continue;
-            }
-            ItemMetadata itemMetadata = itemMetadataOptional.get();
-            if (lootDropSuccess(itemMetadata.getRarity().getPercentToLoot())) {
-                Item i = new ItemBuilder().from(itemMetadata).create();
-                gameManager.getEntityManager().saveItem(i);
-                lootItems.add(i);
+            synchronized (interner.intern(internalItemName)) {
+                try (GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction()) {
+                    Optional<ItemData> itemOptional = tx.getStorage().getItem(internalItemName);
+                    if (!itemOptional.isPresent()) {
+                        continue;
+                    }
+                    ItemData item = itemOptional.get();
+                    if (lootDropSuccess(item.getRarity().getPercentToLoot())) {
+                        ItemInstance i = new ItemBuilder().from(ItemData.copyItem(item)).create();
+                        tx.getStorage().saveItemEntity(i);
+                        lootItems.add(i);
+                    }
+                    tx.success();
+                }
             }
         }
         return lootItems;
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 8fb814775a666340e284f42f0d8e4d10e95e19a9..b9a13a2fb9909c10b0247702e10c1be7678f7756 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/DefaultApplyEffectsStats.java
@@ -31,10 +31,10 @@ public class DefaultApplyEffectsStats implements ItemUseAction {
     private final Stats itemApplyStats;
     private static final Logger log = Logger.getLogger(DefaultApplyEffectsStats.class);
 
-    public DefaultApplyEffectsStats(ItemMetadata itemMetadata) {
-        this.internalItemName = itemMetadata.getInternalItemName();
-        this.effectSet = itemMetadata.getEffects();
-        this.itemApplyStats = itemMetadata.getItemApplyStats();
+    public DefaultApplyEffectsStats(Item item) {
+        this.internalItemName = item.getInternalItemName();
+        this.effectSet = item.getEffects();
+        this.itemApplyStats = item.getItemApplyStats();
     }
 
     @Override
@@ -43,7 +43,7 @@ public class DefaultApplyEffectsStats implements ItemUseAction {
     }
 
     @Override
-    public void executeAction(GameManager gameManager, Player player, Item item, UseCommand.UseItemOn useItemOn) {
+    public void executeAction(GameManager gameManager, Player player, ItemInstance item, UseCommand.UseItemOn useItemOn) {
         String playerName = player.getPlayerName();
 
         gameManager.writeToPlayerCurrentRoom(player.getPlayerId(), playerName + " uses " + item.getItemName() + ".\r\n");
@@ -60,7 +60,7 @@ public class DefaultApplyEffectsStats implements ItemUseAction {
     }
 
     @Override
-    public void postExecuteAction(GameManager gameManager, Player player, Item item) {
+    public void postExecuteAction(GameManager gameManager, Player player, ItemInstance item) {
         ItemUseHandler.incrementUses(item);
         if (item.isDisposable()) {
             if (item.getNumberOfUses() < item.getMaxUses()) {
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 9ce835a8438d1b8c6cba4db2deb82b4e9b3557fc..44a11d32fa8c06ea3a8952b95d5dda0e73c9fd11 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/DirtyBombUseAction.java
@@ -32,19 +32,19 @@ import java.util.Set;
 
 public class DirtyBombUseAction implements ItemUseAction {
 
-    private final ItemMetadata itemMetadata;
+    private final Item item;
 
-    public DirtyBombUseAction(ItemMetadata itemMetadata) {
-        this.itemMetadata = itemMetadata;
+    public DirtyBombUseAction(Item item) {
+        this.item = item;
     }
 
     @Override
     public String getInternalItemName() {
-        return itemMetadata.getInternalItemName();
+        return item.getInternalItemName();
     }
 
     @Override
-    public void executeAction(GameManager gameManager, Player player, Item item, UseCommand.UseItemOn useItemOn) {
+    public void executeAction(GameManager gameManager, Player player, ItemInstance item, UseCommand.UseItemOn useItemOn) {
         Room currentRoom = player.getCurrentRoom();
         if (currentRoom.getRoomId().equals(1)) {
             gameManager.writeToPlayerCurrentRoom(player.getPlayerId(), player.getPlayerName() + " tried to detonate a " + item.getItemName() + "!");
@@ -74,7 +74,7 @@ public class DirtyBombUseAction implements ItemUseAction {
     }
 
     @Override
-    public void postExecuteAction(GameManager gameManager, Player player, Item item) {
+    public void postExecuteAction(GameManager gameManager, Player player, ItemInstance item) {
         ItemUseHandler.incrementUses(item);
         if (item.isDisposable()) {
             if (item.getNumberOfUses() < item.getMaxUses()) {
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 614692c86502c76983364a7ef0c013f294547e6e..11dff6b19c1f88d1f233e26dd309c59fd0e9910d 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/LightningSpellBookUseAction.java
@@ -19,7 +19,7 @@ import com.syncleus.aethermud.command.commands.UseCommand;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.items.Item;
-import com.syncleus.aethermud.items.ItemMetadata;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.items.ItemUseAction;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.spells.LightningSpell;
@@ -28,21 +28,21 @@ import java.util.Set;
 
 public class LightningSpellBookUseAction implements ItemUseAction {
 
-    private final ItemMetadata itemMetadata;
+    private final Item item;
 
-    public LightningSpellBookUseAction(ItemMetadata itemMetadata) {
-        this.itemMetadata = itemMetadata;
+    public LightningSpellBookUseAction(Item item) {
+        this.item = item;
     }
 
     private Boolean dontDelete = Boolean.FALSE;
 
     @Override
     public String getInternalItemName() {
-        return itemMetadata.getInternalItemName();
+        return item.getInternalItemName();
     }
 
     @Override
-    public void executeAction(GameManager gameManager, Player player, Item item, UseCommand.UseItemOn useItemOn) {
+    public void executeAction(GameManager gameManager, Player player, ItemInstance item, UseCommand.UseItemOn useItemOn) {
         if (player.getLearnedSpells().contains(LightningSpell.name)) {
             gameManager.getChannelUtils().write(player.getPlayerId(), "You already know how to use " + LightningSpell.name);
             dontDelete = true;
@@ -53,7 +53,7 @@ public class LightningSpellBookUseAction implements ItemUseAction {
     }
 
     @Override
-    public void postExecuteAction(GameManager gameManager, Player player, Item item) {
+    public void postExecuteAction(GameManager gameManager, Player player, ItemInstance item) {
         if (!dontDelete) {
             player.removeInventoryId(item.getItemId());
             gameManager.getEntityManager().removeItem(item);
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 e5fe47c0a07f1e74b10cac0950172982495cdaca..5a49a9a11dc45870a8cc7f3e9c753eb08f14c364 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/ResetAllEffectsUseAction.java
@@ -24,25 +24,25 @@ import java.util.Set;
 
 public class ResetAllEffectsUseAction implements ItemUseAction {
 
-    private final ItemMetadata itemMetadata;
+    private final Item item;
 
-    public ResetAllEffectsUseAction(ItemMetadata itemMetadata) {
-        this.itemMetadata = itemMetadata;
+    public ResetAllEffectsUseAction(Item item) {
+        this.item = item;
     }
 
     @Override
     public String getInternalItemName() {
-        return itemMetadata.getInternalItemName();
+        return item.getInternalItemName();
     }
 
     @Override
-    public void executeAction(GameManager gameManager, Player player, Item item, UseCommand.UseItemOn useItemOn) {
+    public void executeAction(GameManager gameManager, Player player, ItemInstance item, UseCommand.UseItemOn useItemOn) {
         player.resetEffects();
         gameManager.getChannelUtils().write(player.getPlayerId(), "All Effects are removed." + "\r\n");
     }
 
     @Override
-    public void postExecuteAction(GameManager gameManager, Player player, Item item) {
+    public void postExecuteAction(GameManager gameManager, Player player, ItemInstance item) {
         ItemUseHandler.incrementUses(item);
         if (item.isDisposable()) {
             if (item.getNumberOfUses() < item.getMaxUses()) {
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 de9251abdbaa4f2852f6f2e607e867cbfa70819f..e34a9b77bf68d125d2779ad356ec25d7fdbc1794 100644
--- a/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java
+++ b/src/main/java/com/syncleus/aethermud/items/use/StickOfJusticeUseAction.java
@@ -19,7 +19,7 @@ import com.syncleus.aethermud.command.commands.UseCommand;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.items.Item;
-import com.syncleus.aethermud.items.ItemMetadata;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.items.ItemUseAction;
 import com.syncleus.aethermud.player.Player;
 
@@ -28,20 +28,20 @@ import java.util.Set;
 
 public class StickOfJusticeUseAction implements ItemUseAction {
 
-    private final ItemMetadata itemMetadata;
+    private final Item item;
 
-    public StickOfJusticeUseAction(ItemMetadata itemMetadata) {
-        this.itemMetadata = itemMetadata;
+    public StickOfJusticeUseAction(Item item) {
+        this.item = item;
     }
 
     @Override
     public String getInternalItemName() {
-        return itemMetadata.getInternalItemName();
+        return item.getInternalItemName();
     }
 
 
     @Override
-    public void executeAction(GameManager gameManager, Player player, Item item, UseCommand.UseItemOn useItemOn) {
+    public void executeAction(GameManager gameManager, Player player, ItemInstance item, UseCommand.UseItemOn useItemOn) {
 
         if (!useItemOn.getTarget().isPresent()) {
             gameManager.getChannelUtils().write(player.getPlayerId(), "You must use the Stick Of Justice on someone who deserves it.");
@@ -60,7 +60,7 @@ public class StickOfJusticeUseAction implements ItemUseAction {
     }
 
     @Override
-    public void postExecuteAction(GameManager gameManager, Player player, Item item) {
+    public void postExecuteAction(GameManager gameManager, Player player, ItemInstance item) {
     }
 
     @Override
diff --git a/src/main/java/com/syncleus/aethermud/merchant/Merchant.java b/src/main/java/com/syncleus/aethermud/merchant/Merchant.java
index 93177bb4514016b29c424dbf302b841c2193163a..3dc9ff8e14b965d0580142f765dac92a737bee63 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/Merchant.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/Merchant.java
@@ -16,7 +16,9 @@
 package com.syncleus.aethermud.merchant;
 
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.ItemMetadata;
+import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
 import org.nocrala.tools.texttablefmt.BorderStyle;
 import org.nocrala.tools.texttablefmt.ShownBorders;
 import org.nocrala.tools.texttablefmt.Table;
@@ -71,14 +73,17 @@ public class Merchant {
         while (iterator.hasNext()) {
             i++;
             MerchantItemForSale merchantItemForSale = iterator.next();
-            Optional<ItemMetadata> itemMetadataOptional = gameManager.getItemStorage().get(merchantItemForSale.getInternalItemName());
-            if (!itemMetadataOptional.isPresent()) {
-                continue;
+            Item item;
+            try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                Optional<ItemData> itemOptional = tx.getStorage().getItem(merchantItemForSale.getInternalItemName());
+                if (!itemOptional.isPresent()) {
+                    continue;
+                }
+                item = ItemData.copyItem(itemOptional.get());
             }
-            ItemMetadata itemMetadata = itemMetadataOptional.get();
             t.addCell(String.valueOf(i));
             t.addCell(NumberFormat.getNumberInstance(Locale.US).format(merchantItemForSale.getCost()));
-            t.addCell(itemMetadata.getItemDescription());
+            t.addCell(item.getItemDescription());
         }
         return t.render();
     }
diff --git a/src/main/java/com/syncleus/aethermud/merchant/MerchantCommandHandler.java b/src/main/java/com/syncleus/aethermud/merchant/MerchantCommandHandler.java
index 9fb9802bafaed48287b51a126b98dcfb473a64de..65cbfb84a0ac96d3d690de0a01181d00fecee3c6 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/MerchantCommandHandler.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/MerchantCommandHandler.java
@@ -17,7 +17,7 @@ package com.syncleus.aethermud.merchant;
 
 import com.syncleus.aethermud.command.commands.CommandAuditLog;
 import com.syncleus.aethermud.core.GameManager;
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.server.model.AetherMudSession;
 import com.syncleus.aethermud.server.communication.Color;
@@ -70,7 +70,7 @@ public class MerchantCommandHandler extends SimpleChannelUpstreamHandler {
                     Integer desiredItem = Integer.parseInt(split[1]);
                     if (inventoryMenu.containsKey(desiredItem)) {
                         InventoryItemForSale inventoryItemForSale = inventoryMenu.get(desiredItem);
-                        Item item = inventoryItemForSale.getItem();
+                        ItemInstance item = inventoryItemForSale.getItem();
                         playerByUsername.incrementGold(inventoryItemForSale.getCost());
                         gameManager.getChannelUtils().write(playerByUsername.getPlayerId(), "You have received: " + inventoryItemForSale.getCost() + Color.YELLOW + " gold" + Color.RESET + " for " + item.getItemName() + "\r\n");
                         playerByUsername.removeInventoryId(item.getItemId());
@@ -103,9 +103,9 @@ public class MerchantCommandHandler extends SimpleChannelUpstreamHandler {
 
     public Map<Integer, InventoryItemForSale> getInventoryMenu(Player player) {
         Map<Integer, InventoryItemForSale> inventoryItemsForSale = Maps.newHashMap();
-        List<Item> inventory = player.getInventory();
+        List<ItemInstance> inventory = player.getInventory();
         int inv = 1;
-        for (Item itemEntity : inventory) {
+        for (ItemInstance itemEntity : inventory) {
             int valueInGold = itemEntity.getValueInGold();
             if (valueInGold == 0) {
                 valueInGold = itemEntity.getValueInGold();
@@ -150,10 +150,10 @@ public class MerchantCommandHandler extends SimpleChannelUpstreamHandler {
     }
 
     class InventoryItemForSale {
-        private final Item item;
+        private final ItemInstance item;
         private final Integer cost;
 
-        public InventoryItemForSale(Integer cost, Item item) {
+        public InventoryItemForSale(Integer cost, ItemInstance item) {
             this.cost = cost;
             this.item = item;
         }
@@ -162,7 +162,7 @@ public class MerchantCommandHandler extends SimpleChannelUpstreamHandler {
             return cost;
         }
 
-        public Item getItem() {
+        public ItemInstance getItem() {
             return item;
         }
     }
diff --git a/src/main/java/com/syncleus/aethermud/merchant/MerchantManager.java b/src/main/java/com/syncleus/aethermud/merchant/MerchantManager.java
index 18d46243e4a91503e33f7e61ffa4d32d8d1b23aa..554c19de0926b442cc1960b8c7a7e81868c29118 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/MerchantManager.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/MerchantManager.java
@@ -20,10 +20,12 @@ import com.codahale.metrics.Timer;
 import com.syncleus.aethermud.Main;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.items.ItemBuilder;
-import com.syncleus.aethermud.items.ItemMetadata;
 import com.syncleus.aethermud.player.Player;
 import com.syncleus.aethermud.player.PlayerUtil;
+import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
 
 import java.util.Optional;
 
@@ -47,11 +49,14 @@ public class MerchantManager {
                 i++;
                 if (i == itemNo) {
                     String internalItemName = merchantItemForSale.getInternalItemName();
-                    Optional<ItemMetadata> itemMetadataOptional = gameManager.getItemStorage().get(internalItemName);
-                    if (!itemMetadataOptional.isPresent()) {
-                        continue;
+                    Item item;
+                    try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+                        Optional<ItemData> itemOptional = tx.getStorage().getItem(internalItemName);
+                        if (!itemOptional.isPresent()) {
+                            continue;
+                        }
+                        item = ItemData.copyItem(itemOptional.get());
                     }
-                    ItemMetadata itemMetadata = itemMetadataOptional.get();
                     long maxInventorySize = player.getPlayerStatsWithEquipmentAndLevel().getInventorySize();
                     if (player.getInventory().size() >= maxInventorySize) {
                         gameManager.getChannelUtils().write(player.getPlayerId(), "Your inventory is full, drop some items and come back.\r\n");
@@ -61,13 +66,13 @@ public class MerchantManager {
                     PlayerUtil.consume(gameManager, player.getPlayerId(), playerData -> {
                         long availableGold = playerData.getGold();
                         if (availableGold >= price) {
-                            Item item = new ItemBuilder().from(itemMetadata).create();
-                            gameManager.getEntityManager().saveItem(item);
-                            gameManager.acquireItem(player, item.getItemId());
+                            ItemInstance itemInstance = new ItemBuilder().from(item).create();
+                            gameManager.getEntityManager().saveItem(itemInstance);
+                            gameManager.acquireItem(player, itemInstance.getItemId());
                             player.incrementGold(-price);
-                            gameManager.getChannelUtils().write(player.getPlayerId(), "You have purchased: " + item.getItemName() + "\r\n");
+                            gameManager.getChannelUtils().write(player.getPlayerId(), "You have purchased: " + itemInstance.getItemName() + "\r\n");
                         } else {
-                            gameManager.getChannelUtils().write(player.getPlayerId(), "You can't afford: " + itemMetadata.getItemName() + "\r\n");
+                            gameManager.getChannelUtils().write(player.getPlayerId(), "You can't afford: " + item.getItemName() + "\r\n");
                         }
                     });
                 }
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 cfd3723e91c397c7b3bf9e26bf9f5b73f845fcab..a97dc283cf46a9a267caeea9172ab88b7cc04c0c 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/lockers/GetCommand.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/lockers/GetCommand.java
@@ -15,7 +15,7 @@
  */
 package com.syncleus.aethermud.merchant.lockers;
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
 import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
@@ -50,11 +50,11 @@ public class GetCommand extends LockerCommand {
                 }
                 PlayerData playerData = playerMetadataOptional.get();
                 for (String entityId : playerData.getLockerInventory()) {
-                    Optional<Item> itemEntityOptional = gameManager.getEntityManager().getItemEntity(entityId);
+                    Optional<ItemInstance> itemEntityOptional = gameManager.getEntityManager().getItemEntity(entityId);
                     if (!itemEntityOptional.isPresent()) {
                         continue;
                     }
-                    Item itemEntity = itemEntityOptional.get();
+                    ItemInstance itemEntity = itemEntityOptional.get();
                     if (itemEntity.getItemTriggers().contains(desiredRetrieveOption)) {
                         player.transferItemFromLocker(entityId);
                         write(itemEntity.getItemName() + " retrieved from locker.\r\n");
diff --git a/src/main/java/com/syncleus/aethermud/merchant/lockers/PutCommand.java b/src/main/java/com/syncleus/aethermud/merchant/lockers/PutCommand.java
index fac190129f2f7e8e75b02e2c90da1c995dccbe05..04d5b62afb74e6baca013f3308daa0b8a90558f0 100644
--- a/src/main/java/com/syncleus/aethermud/merchant/lockers/PutCommand.java
+++ b/src/main/java/com/syncleus/aethermud/merchant/lockers/PutCommand.java
@@ -15,7 +15,7 @@
  */
 package com.syncleus.aethermud.merchant.lockers;
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.core.GameManager;
 import com.google.common.base.Joiner;
 import org.jboss.netty.channel.ChannelHandlerContext;
@@ -39,7 +39,7 @@ public class PutCommand extends LockerCommand {
             configure(e);
             originalMessageParts.remove(0);
             String desiredDropOffItem = Joiner.on(" ").join(originalMessageParts);
-            for (Item item : player.getInventory()) {
+            for (ItemInstance item : player.getInventory()) {
                 if (item.getItemTriggers().contains(desiredDropOffItem)) {
                     item.setWithPlayer(false);
                     player.transferItemToLocker(item.getItemId());
diff --git a/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java b/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
index 6bde9d2157deb99a88a5d283039cb218a9068766..20878b952c283ebc8cdba0a49c2257c41c7fcf68 100644
--- a/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
+++ b/src/main/java/com/syncleus/aethermud/npc/NpcSpawn.java
@@ -22,7 +22,8 @@ import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.core.SentryManager;
 import com.syncleus.aethermud.entity.AetherMudEntity;
 import com.syncleus.aethermud.items.Effect;
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.player.CoolDown;
 import com.syncleus.aethermud.player.CoolDownType;
@@ -278,7 +279,7 @@ public class NpcSpawn extends AetherMudEntity {
         isAlive.set(false);
         player.removeActiveAlertStatus(this);
         Map<String, Double> damagePercents;
-        Item corpse = Item.createCorpseItem(getName(), getLoot());
+        ItemInstance corpse = ItemInstance.createCorpseItem(getName(), getLoot());
         if (!player.isActive(CoolDownType.DEATH)) {
             gameManager.writeToPlayerCurrentRoom(player.getPlayerId(), getDieMessage() + "\r\n");
         }
diff --git a/src/main/java/com/syncleus/aethermud/player/Player.java b/src/main/java/com/syncleus/aethermud/player/Player.java
index 8569237ce8a21ecfd9e7e352e5a9edb3a0ae6500..cfe8f576bd12ab89910ab0e54f3e2695bef81717 100644
--- a/src/main/java/com/syncleus/aethermud/player/Player.java
+++ b/src/main/java/com/syncleus/aethermud/player/Player.java
@@ -698,16 +698,16 @@ public class Player extends AetherMudEntity {
         }
     }
 
-    public Optional<Item> getInventoryItem(String itemKeyword) {
+    public Optional<ItemInstance> getInventoryItem(String itemKeyword) {
         synchronized (interner.intern(playerId)) {
             final List<String> inventory = new ArrayList<>();
             this.consumeRead(playerData -> inventory.addAll(playerData.getInventory()));
             for (String itemId : inventory) {
-                Optional<Item> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                Optional<ItemInstance> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
                 if (!itemOptional.isPresent()) {
                     continue;
                 }
-                Item itemEntity = itemOptional.get();
+                ItemInstance itemEntity = itemOptional.get();
                 if (itemEntity.getItemTriggers().contains(itemKeyword)) {
                     return Optional.of(itemEntity);
                 }
@@ -723,10 +723,10 @@ public class Player extends AetherMudEntity {
     public List<String> getRolledUpLockerInventory() {
         synchronized (interner.intern(playerId)) {
             List<String> rolledUp = Lists.newArrayList();
-            List<Item> inventory = getLockerInventory();
+            List<ItemInstance> inventory = getLockerInventory();
             Map<String, Integer> itemAndCounts = Maps.newHashMap();
             if (inventory != null) {
-                for (Item item : inventory) {
+                for (ItemInstance item : inventory) {
                     StringBuilder invItem = new StringBuilder();
                     invItem.append(item.getItemName());
                     int maxUses = item.getMaxUses();
@@ -761,14 +761,14 @@ public class Player extends AetherMudEntity {
         }
     }
 
-    public List<Item> getLockerInventory() {
+    public List<ItemInstance> getLockerInventory() {
         synchronized (interner.intern(playerId)) {
             return this.transactRead(playerData -> {
-                List<Item> inventoryItems = Lists.newArrayList();
+                List<ItemInstance> inventoryItems = Lists.newArrayList();
                 List<String> inventory = playerData.getLockerInventory();
                 if (inventory != null) {
                     for (String itemId : inventory) {
-                        Optional<Item> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                        Optional<ItemInstance> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
                         if (!itemOptional.isPresent()) {
                             log.info("Orphaned inventoryId:" + itemId + " player: " + getPlayerName());
                             continue;
@@ -776,7 +776,7 @@ public class Player extends AetherMudEntity {
                         inventoryItems.add(itemOptional.get());
                     }
                 }
-                inventoryItems.sort(Comparator.comparing(Item::getItemName));
+                inventoryItems.sort(Comparator.comparing(ItemInstance::getItemName));
                 return inventoryItems;
             });
         }
@@ -785,10 +785,10 @@ public class Player extends AetherMudEntity {
     public List<String> getRolledUpIntentory() {
         synchronized (interner.intern(playerId)) {
             List<String> rolledUp = Lists.newArrayList();
-            List<Item> inventory = getInventory();
+            List<ItemInstance> inventory = getInventory();
             Map<String, Integer> itemAndCounts = Maps.newHashMap();
             if (inventory != null) {
-                for (Item item : inventory) {
+                for (ItemInstance item : inventory) {
                     StringBuilder invItem = new StringBuilder();
                     invItem.append(item.getItemName());
                     int maxUses = item.getMaxUses();
@@ -823,14 +823,14 @@ public class Player extends AetherMudEntity {
         }
     }
 
-    public List<Item> getInventory() {
+    public List<ItemInstance> getInventory() {
         synchronized (interner.intern(playerId)) {
             return this.transactRead(playerData -> {
-                List<Item> inventoryItems = Lists.newArrayList();
+                List<ItemInstance> inventoryItems = Lists.newArrayList();
                 List<String> inventory = playerData.getInventory();
                 if (inventory != null) {
                     for (String itemId : inventory) {
-                        Optional<Item> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                        Optional<ItemInstance> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
                         if (!itemOptional.isPresent()) {
                             log.info("Orphaned inventoryId:" + itemId + " player: " + getPlayerName());
                             continue;
@@ -838,20 +838,20 @@ public class Player extends AetherMudEntity {
                         inventoryItems.add(itemOptional.get());
                     }
                 }
-                inventoryItems.sort(Comparator.comparing(Item::getItemName));
+                inventoryItems.sort(Comparator.comparing(ItemInstance::getItemName));
                 return inventoryItems;
             });
         }
     }
 
-    public Set<Item> getEquipment() {
+    public Set<ItemInstance> getEquipment() {
         synchronized (interner.intern(playerId)) {
             return this.transactRead(playerData -> {
-                Set<Item> equipmentItems = Sets.newHashSet();
+                Set<ItemInstance> equipmentItems = Sets.newHashSet();
                 List<String> equipment = playerData.getPlayerEquipment();
                 if (equipment != null) {
                     for (String itemId : equipment) {
-                        Optional<Item> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                        Optional<ItemInstance> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
                         if (!itemOptional.isPresent()) {
                             log.info("Orphaned equipmentId:" + itemId + " player: " + getPlayerName());
                             continue;
@@ -864,14 +864,14 @@ public class Player extends AetherMudEntity {
         }
     }
 
-    public void equip(Item item) {
+    public void equip(ItemInstance item) {
         synchronized (interner.intern(playerId)) {
             if (item.getEquipment() == null) {
                 return;
             }
             Equipment equipment = item.getEquipment();
             EquipmentSlotType equipmentSlotType = equipment.getEquipmentSlotType();
-            Optional<Item> slotItemOptional = getSlotItem(equipmentSlotType);
+            Optional<ItemInstance> slotItemOptional = getSlotItem(equipmentSlotType);
             if (slotItemOptional.isPresent()) {
                 if (!unEquip(slotItemOptional.get())) {
                     return;
@@ -883,17 +883,17 @@ public class Player extends AetherMudEntity {
         }
     }
 
-    public Optional<Item> getSlotItem(EquipmentSlotType slot) {
+    public Optional<ItemInstance> getSlotItem(EquipmentSlotType slot) {
         return this.transactRead(playerData -> {
             if (playerData.getPlayerEquipment() == null) {
                 return Optional.empty();
             }
             for (String item : playerData.getPlayerEquipment()) {
-                Optional<Item> itemOptional = gameManager.getEntityManager().getItemEntity(item);
+                Optional<ItemInstance> itemOptional = gameManager.getEntityManager().getItemEntity(item);
                 if (!itemOptional.isPresent()) {
                     continue;
                 }
-                Item itemEntity = itemOptional.get();
+                ItemInstance itemEntity = itemOptional.get();
                 EquipmentSlotType equipmentSlotType = itemEntity.getEquipment().getEquipmentSlotType();
                 if (equipmentSlotType.equals(slot)) {
                     return Optional.of(itemEntity);
@@ -903,7 +903,7 @@ public class Player extends AetherMudEntity {
         });
     }
 
-    public boolean unEquip(Item item) {
+    public boolean unEquip(ItemInstance item) {
         synchronized (interner.intern(playerId)) {
             gameManager.getChannelUtils().write(playerId, "Un-equipping " + item.getItemName() + "\r\n");
             if (gameManager.acquireItem(this, item.getItemId())) {
@@ -966,11 +966,11 @@ public class Player extends AetherMudEntity {
                     return playerStats;
                 }
                 for (String equipId : playerEquipment) {
-                    Optional<Item> itemOptional = gameManager.getEntityManager().getItemEntity(equipId);
+                    Optional<ItemInstance> itemOptional = gameManager.getEntityManager().getItemEntity(equipId);
                     if (!itemOptional.isPresent()) {
                         continue;
                     }
-                    Item itemEntity = itemOptional.get();
+                    ItemInstance itemEntity = itemOptional.get();
                     Equipment equipment = itemEntity.getEquipment();
                     Stats stats = equipment.getStats();
                     StatsHelper.combineStats(newStats, stats);
@@ -1011,7 +1011,7 @@ public class Player extends AetherMudEntity {
         List<EquipmentSlotType> all = EquipmentSlotType.getAll();
         for (EquipmentSlotType slot : all) {
             t.addCell(capitalize(slot.getName()));
-            Optional<Item> slotItemOptional = getSlotItem(slot);
+            Optional<ItemInstance> slotItemOptional = getSlotItem(slot);
             if (slotItemOptional.isPresent()) {
                 t.addCell(slotItemOptional.get().getItemName());
             } else {
diff --git a/src/main/java/com/syncleus/aethermud/player/PlayerManagement.java b/src/main/java/com/syncleus/aethermud/player/PlayerManagement.java
index beec826e84d12339a5202c1e3b08f254462ede72..15748f1de44550289fda987df8d83ea42fb407d9 100644
--- a/src/main/java/com/syncleus/aethermud/player/PlayerManagement.java
+++ b/src/main/java/com/syncleus/aethermud/player/PlayerManagement.java
@@ -18,15 +18,17 @@ 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.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.items.ItemBuilder;
-import com.syncleus.aethermud.items.ItemMetadata;
+import com.syncleus.aethermud.items.Item;
 import com.syncleus.aethermud.server.communication.Color;
 import com.google.api.client.util.Lists;
 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.GraphStorageFactory;
+import com.syncleus.aethermud.storage.graphdb.model.ItemData;
 import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 
 import java.util.*;
@@ -175,11 +177,11 @@ public class PlayerManagement implements PlayerManagementMBean {
             Map<String, String> inventoryContents = Maps.newHashMap();
             List<String> inventory = playerData.getInventory();
             for (String itemId : inventory) {
-                Optional<Item> itemEntityOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                Optional<ItemInstance> itemEntityOptional = gameManager.getEntityManager().getItemEntity(itemId);
                 if (!itemEntityOptional.isPresent()) {
                     continue;
                 }
-                Item itemEntity = itemEntityOptional.get();
+                ItemInstance itemEntity = itemEntityOptional.get();
                 String itemName = itemEntity.getItemName();
                 final String msgWithoutColorCodes =
                     itemName.replaceAll("\u001B\\[[;\\d]*m", "");
@@ -195,11 +197,11 @@ public class PlayerManagement implements PlayerManagementMBean {
             Map<String, String> inventoryContents = Maps.newHashMap();
             List<String> inventory = playerData.getLockerInventory();
             for (String itemId : inventory) {
-                Optional<Item> itemEntityOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                Optional<ItemInstance> itemEntityOptional = gameManager.getEntityManager().getItemEntity(itemId);
                 if (!itemEntityOptional.isPresent()) {
                     continue;
                 }
-                Item itemEntity = itemEntityOptional.get();
+                ItemInstance itemEntity = itemEntityOptional.get();
                 String itemName = itemEntity.getItemName();
                 final String msgWithoutColorCodes =
                     itemName.replaceAll("\u001B\\[[;\\d]*m", "");
@@ -211,16 +213,20 @@ public class PlayerManagement implements PlayerManagementMBean {
 
     @Override
     public String createItemInInventory(String internalItemName){
-        Optional<ItemMetadata> itemMetadata = gameManager.getItemStorage().get(internalItemName);
-        if (!itemMetadata.isPresent()) {
-            return "No such item exists with internal name: " + internalItemName;
+        Item item;
+        try( GraphStorageFactory.AetherMudTx tx = this.gameManager.getGraphStorageFactory().beginTransaction() ) {
+            Optional<ItemData> itemOptional = tx.getStorage().getItem(internalItemName);
+            if (!itemOptional.isPresent()) {
+                return "No such item exists with internal name: " + internalItemName;
+            }
+            item = ItemData.copyItem(itemOptional.get());
         }
-        Item item = new ItemBuilder().from(itemMetadata.get()).create();
-        gameManager.getEntityManager().saveItem(item);
+        ItemInstance itemInstance = new ItemBuilder().from(item).create();
+        gameManager.getEntityManager().saveItem(itemInstance);
         synchronized (findInterner().intern(playerId)) {
-            this.consume(playerData -> playerData.addInventoryEntityId(item.getItemId()));
+            this.consume(playerData -> playerData.addInventoryEntityId(itemInstance.getItemId()));
         }
-        final String msgWithoutColorCodes = item.getItemName().replaceAll("\u001B\\[[;\\d]*m", "");
+        final String msgWithoutColorCodes = itemInstance.getItemName().replaceAll("\u001B\\[[;\\d]*m", "");
         return msgWithoutColorCodes + " created.";
     }
 
diff --git a/src/main/java/com/syncleus/aethermud/spawner/ItemSpawner.java b/src/main/java/com/syncleus/aethermud/spawner/ItemSpawner.java
index bfd8a0eab416d710a090ccc284c006a841c825a0..b2cdec879839530d3058351ee3376542676e4c19 100644
--- a/src/main/java/com/syncleus/aethermud/spawner/ItemSpawner.java
+++ b/src/main/java/com/syncleus/aethermud/spawner/ItemSpawner.java
@@ -16,14 +16,16 @@
 package com.syncleus.aethermud.spawner;
 
 import com.codahale.metrics.MetricRegistry;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
 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.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.items.ItemBuilder;
-import com.syncleus.aethermud.items.ItemMetadata;
-import com.syncleus.aethermud.storage.graphdb.model.ItemData;
+import com.syncleus.aethermud.storage.graphdb.model.ItemInstanceData;
 import com.syncleus.aethermud.world.model.Area;
 import com.syncleus.aethermud.world.model.Room;
 import com.google.common.base.Predicate;
@@ -38,17 +40,18 @@ import java.util.Set;
 
 public class ItemSpawner extends AetherMudEntity {
     private static final Logger LOGGER = Logger.getLogger(ItemSpawner.class);
-    private final ItemMetadata itemMetadata;
+    private final Item item;
     private final SpawnRule spawnRule;
     private final GameManager gameManager;
     private Integer roomId;
     private int noTicks = 0;
     private final Random random = new Random();
     private final Area spawnArea;
+    private final Interner<String> interner = Interners.newWeakInterner();
 
 
-    public ItemSpawner(ItemMetadata itemMetadata, SpawnRule spawnRule, GameManager gameManager) {
-        this.itemMetadata = itemMetadata;
+    public ItemSpawner(Item item, SpawnRule spawnRule, GameManager gameManager) {
+        this.item = item;
         this.spawnRule = spawnRule;
         this.gameManager = gameManager;
         this.noTicks = spawnRule.getSpawnIntervalTicks();
@@ -68,8 +71,8 @@ public class ItemSpawner extends AetherMudEntity {
                 int numberOfAttempts = spawnRule.getMaxInstances() - counterNumberInArea();
                 for (int i = 0; i < numberOfAttempts; i++) {
                     if (random.nextInt(100) < randomPercentage || randomPercentage == 100) {
-                        if (itemMetadata.getValidTimeOfDays() != null && itemMetadata.getValidTimeOfDays().size() > 0) {
-                            if (itemMetadata.getValidTimeOfDays().contains(gameManager.getTimeTracker().getTimeOfDay())) {
+                        if (item.getValidTimeOfDays() != null && item.getValidTimeOfDays().size() > 0) {
+                            if (item.getValidTimeOfDays().contains(gameManager.getTimeTracker().getTimeOfDay())) {
                                 createAndAddItem();
                             }
                         } else {
@@ -88,10 +91,12 @@ public class ItemSpawner extends AetherMudEntity {
     private void createAndAddItem() {
         ArrayList<Room> rooms = Lists.newArrayList(Iterators.filter(gameManager.getRoomManager().getRoomsByArea(spawnArea).iterator(), getRoomsWithRoom()));
         Room room = rooms.get(random.nextInt(rooms.size()));
-        Item item = new ItemBuilder().from(itemMetadata).create();
-        ItemData itemData = gameManager.getEntityManager().saveItem(item);
-        gameManager.placeItemInRoom(room.getRoomId(), item.getItemId());
-        Main.metrics.counter(MetricRegistry.name(ItemSpawner.class, item.getItemName() + "-spawn")).inc();
+        synchronized (interner.intern(this.item.getInternalItemName())) {
+            ItemInstance itemInstance = new ItemBuilder().from(this.item).create();
+            gameManager.getEntityManager().saveItem(itemInstance);
+            gameManager.placeItemInRoom(room.getRoomId(), itemInstance.getItemId());
+            Main.metrics.counter(MetricRegistry.name(ItemSpawner.class, itemInstance.getItemName() + "-spawn")).inc();
+        }
     }
 
     private int counterNumberInArea() {
@@ -100,12 +105,12 @@ public class ItemSpawner extends AetherMudEntity {
         for (Room room : roomsByArea) {
             if (room.getAreas().contains(spawnArea)) {
                 for (String i : room.getItemIds()) {
-                    Optional<Item> currentItemOptional = gameManager.getEntityManager().getItemEntity(i);
+                    Optional<ItemInstance> currentItemOptional = gameManager.getEntityManager().getItemEntity(i);
                     if (!currentItemOptional.isPresent()) {
                         continue;
                     }
-                    Item currentItem = currentItemOptional.get();
-                    if (currentItem.getInternalItemName().equals(itemMetadata.getInternalItemName())) {
+                    ItemInstance currentItem = currentItemOptional.get();
+                    if (currentItem.getInternalItemName().equals(item.getInternalItemName())) {
                         numberCurrentlyInArea++;
                     }
                 }
@@ -121,12 +126,12 @@ public class ItemSpawner extends AetherMudEntity {
                 int count = 0;
                 Set<String> itemIds = room.getItemIds();
                 for (String itemId : itemIds) {
-                    Optional<Item> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+                    Optional<ItemInstance> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
                     if (!itemOptional.isPresent()) {
                         continue;
                     }
-                    Item item = itemOptional.get();
-                    if (item.getInternalItemName().equals(itemMetadata.getInternalItemName())) {
+                    ItemInstance item = itemOptional.get();
+                    if (item.getInternalItemName().equals(ItemSpawner.this.item.getInternalItemName())) {
                         count++;
                     }
                 }
diff --git a/src/main/java/com/syncleus/aethermud/storage/AetherMudStorage.java b/src/main/java/com/syncleus/aethermud/storage/AetherMudStorage.java
index 316d7fa0f0c90fb59abd06cfdbd39a1b46acb15c..ce33fa66370ca760233d23a7555e7abe246a2310 100644
--- a/src/main/java/com/syncleus/aethermud/storage/AetherMudStorage.java
+++ b/src/main/java/com/syncleus/aethermud/storage/AetherMudStorage.java
@@ -18,8 +18,10 @@ package com.syncleus.aethermud.storage;
 
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.npc.NpcSpawn;
 import com.syncleus.aethermud.storage.graphdb.model.ItemData;
+import com.syncleus.aethermud.storage.graphdb.model.ItemInstanceData;
 import com.syncleus.aethermud.storage.graphdb.model.NpcData;
 import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
 
@@ -35,11 +37,19 @@ public interface AetherMudStorage {
 
     Map<String, PlayerData> getAllPlayerMetadata();
 
+    ItemInstanceData saveItemEntity(ItemInstance item);
+
+    Optional<ItemInstanceData> getItemEntity(String itemId);
+
+    void removeItemEntity(String itemId);
+
     ItemData saveItem(Item item);
 
-    Optional<ItemData> getItemEntity(String itemId);
+    Optional<ItemData> getItem(String internalName);
+
+    void removeItem(String internalName);
 
-    void removeItem(String itemId);
+    public List<? extends ItemData> getAllItems();
 
     List<? extends NpcSpawn> getAllNpcs(GameManager gameManager);
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/ItemStorage.java b/src/main/java/com/syncleus/aethermud/storage/ItemStorage.java
deleted file mode 100644
index 74a1149a77227565d9bcfb3f743484d147044bab..0000000000000000000000000000000000000000
--- a/src/main/java/com/syncleus/aethermud/storage/ItemStorage.java
+++ /dev/null
@@ -1,67 +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.storage;
-
-import com.syncleus.aethermud.common.ColorizedTextTemplate;
-import com.syncleus.aethermud.items.ItemMetadata;
-import org.apache.log4j.Logger;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-public class ItemStorage {
-
-    public final static String LOCAL_ITEM_DIRECTORY = "world/items/";
-
-    private static final Logger log = Logger.getLogger(ItemStorage.class);
-    private final FilebasedJsonStorage filebasedJsonStorage;
-
-    private final List<ItemMetadata> itemMetadatas;
-
-    public ItemStorage(FilebasedJsonStorage filebasedJsonStorage) {
-        this.filebasedJsonStorage = filebasedJsonStorage;
-        this.itemMetadatas = getAllItemMetadata();
-    }
-
-    private List<ItemMetadata> getAllItemMetadata() {
-        return filebasedJsonStorage.readAllMetadatas(LOCAL_ITEM_DIRECTORY, true, new ItemMetadata()).stream()
-                .map(itemMetadata -> {
-                    itemMetadata.setItemDescription(ColorizedTextTemplate.renderFromTemplateLanguage(itemMetadata.getItemDescription()));
-                    itemMetadata.setItemName(ColorizedTextTemplate.renderFromTemplateLanguage(itemMetadata.getItemName()));
-                    itemMetadata.setRestingName(ColorizedTextTemplate.renderFromTemplateLanguage(itemMetadata.getRestingName()));
-                    return itemMetadata;
-                }).collect(Collectors.toList());
-    }
-
-    public List<ItemMetadata> getItemMetadatas() {
-        return itemMetadatas;
-    }
-
-    public void saveItemMetadata(ItemMetadata itemMetadata) throws IOException {
-        itemMetadata.setItemName(ColorizedTextTemplate.renderToTemplateLanguage(itemMetadata.getItemName()));
-        itemMetadata.setItemDescription(ColorizedTextTemplate.renderToTemplateLanguage(itemMetadata.getItemDescription()));
-        itemMetadata.setRestingName(ColorizedTextTemplate.renderToTemplateLanguage(itemMetadata.getRestingName()));
-        filebasedJsonStorage.saveMetadata(itemMetadata.getInternalItemName(), LOCAL_ITEM_DIRECTORY, itemMetadata);
-    }
-
-    public Optional<ItemMetadata> get(String internalItemName) {
-        return itemMetadatas.stream()
-                .filter(itemMetadata -> itemMetadata.getInternalItemName().equals(internalItemName))
-                .findFirst();
-    }
-}
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 3d516ec562896a6841c97b29fefaccda1c008573..1c32b0228ad975cae5a13609132c4a2f7be7ce8a 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphDbAetherMudStorage.java
@@ -15,26 +15,29 @@
  */
 package com.syncleus.aethermud.storage.graphdb;
 
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
 import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 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.ItemInstanceData;
 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.PropertyUtils;
 import org.apache.log4j.Logger;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 
-import java.lang.reflect.InvocationTargetException;
 import java.util.*;
 import java.util.stream.Collectors;
 
 public class GraphDbAetherMudStorage implements AetherMudStorage {
     private static final Logger LOGGER = Logger.getLogger(GraphDbAetherMudStorage.class);
     private final WrappedFramedGraph<? extends Graph> framedGraph;
+    private final Interner<String> interner = Interners.newWeakInterner();
 
 
     public GraphDbAetherMudStorage(WrappedFramedGraph<? extends Graph> framedGraph) {
@@ -63,26 +66,65 @@ public class GraphDbAetherMudStorage implements AetherMudStorage {
     }
 
     @Override
-    public Optional<ItemData> getItemEntity(String itemId) {
-        return Optional.ofNullable(framedGraph.traverse((g) -> framedGraph.getTypeResolver().hasType(g.V(), ItemData.class).has("itemId", itemId)).nextOrDefault(ItemData.class, null));
+    public Optional<ItemInstanceData> getItemEntity(String itemId) {
+        synchronized (interner.intern(itemId)) {
+            return Optional.ofNullable(framedGraph.traverse((g) -> framedGraph.getTypeResolver().hasType(g.V(), ItemInstanceData.class).has("itemId", itemId)).nextOrDefault(ItemInstanceData.class, null));
+        }
     }
 
     @Override
-    public void removeItem(String itemId) {
-        this.getItemEntity(itemId).ifPresent((i) -> i.remove());
+    public void removeItemEntity(String itemId) {
+        synchronized (interner.intern(itemId)) {
+            this.getItemEntity(itemId).ifPresent((i) -> i.remove());
+        }
+    }
 
+    @Override
+    public ItemInstanceData saveItemEntity(ItemInstance itemInstance) {
+        synchronized (interner.intern(itemInstance.getItemId())) {
+            Optional<ItemInstanceData> existing = this.getItemEntity(itemInstance.getItemId());
+            ItemInstanceData itemInstanceData;
+            if (existing.isPresent())
+                itemInstanceData = existing.get();
+            else
+                itemInstanceData = framedGraph.addFramedVertex(ItemInstanceData.class);
+            ItemInstanceData.copyItem(itemInstanceData, itemInstance, this.getItem(itemInstance.getItem().getInternalItemName()).get());
+            return itemInstanceData;
+        }
     }
 
     @Override
     public ItemData saveItem(Item item) {
-        Optional<ItemData> existing = this.getItemEntity(item.getItemId());
-        ItemData itemData;
-        if(existing.isPresent())
-            itemData = existing.get();
-        else
-            itemData = framedGraph.addFramedVertex(ItemData.class);
-        ItemData.copyItem(itemData, item);
-        return itemData;
+        synchronized (interner.intern(item.getInternalItemName())) {
+            Optional<ItemData> existing = this.getItem(item.getInternalItemName());
+            ItemData itemData;
+            if (existing.isPresent())
+                itemData = existing.get();
+            else
+                itemData = framedGraph.addFramedVertex(ItemData.class);
+            ItemData.copyItem(itemData, item);
+            return itemData;
+        }
+    }
+
+    @Override
+    public Optional<ItemData> getItem(String internalName) {
+        synchronized (interner.intern(internalName)) {
+            return Optional.ofNullable(framedGraph.traverse((g) -> framedGraph.getTypeResolver().hasType(g.V(), ItemData.class).has("internalItemName", internalName)).nextOrDefault(ItemData.class, null));
+        }
+    }
+
+    @Override
+    public List<? extends ItemData> getAllItems() {
+        return framedGraph.traverse((g) -> framedGraph.getTypeResolver().hasType(g.V(), ItemData.class)).toList(ItemData.class);
+    }
+
+    @Override
+    public void removeItem(String internalName) {
+        synchronized (interner.intern(internalName)) {
+            // TODO : recursively remove all instances
+            this.getItem(internalName).ifPresent((i) -> i.remove());
+        }
     }
 
     public List<? extends NpcSpawn> getAllNpcs(GameManager gameManager) {
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
index 2c5f7eeaf647cc1aba9b516ddefa2122e18ee76a..c666982e6b22167d4f8f994583fd677b2f48651f 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemData.java
@@ -15,12 +15,13 @@
  */
 package com.syncleus.aethermud.storage.graphdb.model;
 
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.syncleus.aethermud.common.ColorizedTextTemplate;
 import com.syncleus.aethermud.core.service.TimeTracker;
 import com.syncleus.aethermud.items.*;
+import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.storage.graphdb.DataUtils;
-import com.syncleus.ferma.TEdge;
-import com.syncleus.ferma.VertexFrame;
 import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
@@ -48,29 +49,20 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     @Property("maxUses")
     public abstract int getMaxUses();
 
-    @Property("withPlayer")
-    public abstract boolean isWithPlayer();
-
-    @Property("withPlayer")
-    public abstract void setWithPlayer(boolean isWithPlayer);
-
-    @Property("numberOfUses")
-    public abstract int getNumberOfUses();
-
-    @Property("numberOfUses")
-    public abstract void setNumberOfUses(int numberOfUses);
-
-    @Property("itemId")
-    public abstract String getItemId();
-
     @Property("internalItemName")
     public abstract String getInternalItemName();
 
-    @Property("itemName")
-    public abstract String getItemName();
+    public String getItemName() {
+        return ColorizedTextTemplate.renderFromTemplateLanguage(this.getProperty("itemName"));
+    }
+
+    public void setItemName(String itemName) {
+        this.setProperty("itemName", ColorizedTextTemplate.renderToTemplateLanguage(itemName));
+    }
 
-    @Property("itemDescription")
-    public abstract String getItemDescription();
+    public String getItemDescription() {
+        return ColorizedTextTemplate.renderFromTemplateLanguage(this.getProperty("itemDescription"));
+    }
 
     @Property("itemTriggers")
     public abstract List<String> getItemTriggers();
@@ -78,15 +70,13 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     @Property("itemTriggers")
     public abstract void setItemTriggers(List<String> itemTriggers);
 
-    @Property("restingName")
-    public abstract String getRestingName();
+    public String getRestingName() {
+        return ColorizedTextTemplate.renderFromTemplateLanguage(this.getProperty("restingName"));
+    }
 
     @Property("itemHalfLifeTicks")
     public abstract int getItemHalfLifeTicks();
 
-    @Property("hasBeenWithPlayer")
-    public abstract void setHasBeenWithPlayer(boolean hasBeenWithPlayer);
-
     @Property("rarity")
     public abstract Rarity getRarity();
 
@@ -96,20 +86,16 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     @Property("valueInGold")
     public abstract int getValueInGold();
 
-    @Property("itemName")
-    public abstract void setItemName(String itemName);
-
-    @Property("itemDescription")
-    public abstract void setItemDescription(String itemDescription);
+    public void setItemDescription(String itemDescription) {
+        this.setProperty("itemDescription", ColorizedTextTemplate.renderToTemplateLanguage(itemDescription));
+    }
 
     @Property("internalItemName")
     public abstract void setInternalItemName(String internalItemName);
 
-    @Property("restingName")
-    public abstract void setRestingName(String restingName);
-
-    @Property("itemId")
-    public abstract void setItemId(String itemId);
+    public void setRestingName(String restingName) {
+        this.setProperty("restingName", ColorizedTextTemplate.renderToTemplateLanguage(restingName));
+    }
 
     @Property("itemHalfLifeTicks")
     public abstract void setItemHalfLifeTicks(int itemHalfLifeTicks);
@@ -123,8 +109,34 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     @Property("disposable")
     public abstract void setDisposable(boolean disposable);
 
-    @Property("hasBeenWithPlayer")
-    public abstract boolean isHasBeenWithPlayer();
+    @Property("forage")
+    public abstract Set<Forage> getForages();
+
+    @Property("forage")
+    public abstract void setForages(Set<Forage> forages);
+
+    @Adjacency(label = "spawnRule", direction = Direction.OUT)
+    public abstract <N extends SpawnRuleData> Iterator<? extends N> getSpawnRulesDataIterator(Class<? extends N> type);
+
+    public List<SpawnRuleData> getSpawnRuleDatas() {
+        return Collections.unmodifiableList(Lists.newArrayList(this.getSpawnRulesDataIterator(SpawnRuleData.class)));
+    }
+
+    @Adjacency(label = "spawnRule", direction = Direction.OUT)
+    public abstract void addSpawnRuleData(SpawnRuleData spawnRule);
+
+    @Adjacency(label = "spawnRule", direction = Direction.OUT)
+    public abstract void removeSpawnRuleData(SpawnRuleData spawnRule);
+
+    public void setSpawnRulesDatas(List<SpawnRuleData> spawnRules) {
+        DataUtils.setAllElements(spawnRules, () -> this.getSpawnRulesDataIterator(SpawnRuleData.class), ruleData -> this.addSpawnRuleData(ruleData), () -> {} );
+    }
+
+    public SpawnRuleData createSpawnRuleData() {
+        final SpawnRuleData rule = this.getGraph().addFramedVertex(SpawnRuleData.class);
+        this.addSpawnRuleData(rule);
+        return rule;
+    }
 
     @Adjacency(label = "equipment", direction = Direction.OUT)
     public abstract <N extends EquipmentData> Iterator<? extends N> getEquipmentDataIterator(Class<? extends N> type);
@@ -259,6 +271,12 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     public static void copyItem(ItemData dest, Item src) {
         try {
             PropertyUtils.copyProperties(dest, src);
+
+            for(SpawnRuleData data : dest.getSpawnRuleDatas())
+                data.remove();
+            for(SpawnRule spawnRule : src.getSpawnRules())
+                SpawnRuleData.copySpawnRule(dest.createSpawnRuleData(), spawnRule);
+
             if( src.getItemApplyStats() != null )
                 StatData.copyStats((dest.getItemApplyStatData() != null ? dest.getItemApplyStatData() : dest.createItemApplyStatData()), src.getItemApplyStats());
             if(src.getLoot() != null )
@@ -277,10 +295,15 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     }
 
     public static Item copyItem(ItemData src) {
-        Item retVal = new Item();
+        Item retVal = new ItemImpl();
         try {
             PropertyUtils.copyProperties(retVal, src);
 
+            Set<SpawnRule> rules = new HashSet<>();
+            for(SpawnRuleData spawnRuleData : src.getSpawnRuleDatas())
+                rules.add(SpawnRuleData.copySpawnRule(spawnRuleData));
+            retVal.setSpawnRules(Collections.unmodifiableSet(rules));
+
             EquipmentData equipmentData = src.getEquipmentData();
             if(equipmentData != null)
                 retVal.setEquipment(EquipmentData.copyEquipment(equipmentData));
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemInstanceData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemInstanceData.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f78bae0aa679b2602d0560c2fc3462a704755dc
--- /dev/null
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemInstanceData.java
@@ -0,0 +1,243 @@
+/**
+ * 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.model;
+
+import com.syncleus.aethermud.core.service.TimeTracker;
+import com.syncleus.aethermud.items.ItemInstance;
+import com.syncleus.aethermud.items.ItemInstanceImpl;
+import com.syncleus.aethermud.items.Rarity;
+import com.syncleus.aethermud.storage.graphdb.DataUtils;
+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.tinkerpop.gremlin.structure.Direction;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+@GraphElement
+public abstract class ItemInstanceData extends AbstractInterceptingVertexFrame {
+    @Property("withPlayer")
+    public abstract boolean isWithPlayer();
+
+    @Property("withPlayer")
+    public abstract void setWithPlayer(boolean isWithPlayer);
+
+    @Property("numberOfUses")
+    public abstract int getNumberOfUses();
+
+    @Property("numberOfUses")
+    public abstract void setNumberOfUses(int numberOfUses);
+
+    @Property("itemId")
+    public abstract String getItemId();
+
+    @Property("hasBeenWithPlayer")
+    public abstract void setHasBeenWithPlayer(boolean hasBeenWithPlayer);
+
+    @Property("itemId")
+    public abstract void setItemId(String itemId);
+
+    @Property("hasBeenWithPlayer")
+    public abstract boolean isHasBeenWithPlayer();
+
+    @Adjacency(label = "item", direction = Direction.OUT)
+    public abstract <N extends ItemData> Iterator<? extends N> getItemDatasIterator(Class<? extends N> type);
+
+    public ItemData getItemData() {
+        Iterator<? extends ItemData> allItems = this.getItemDatasIterator(ItemData.class);
+        if (allItems.hasNext())
+            return allItems.next();
+        else
+            return null;
+    }
+
+    @Adjacency(label = "item", direction = Direction.OUT)
+    public abstract ItemData addItemData(ItemData item);
+
+    @Adjacency(label = "item", direction = Direction.OUT)
+    public abstract void removeItemData(ItemData item);
+
+    public void setItemData(ItemData item) {
+        DataUtils.setAllElements(Collections.singletonList(item), () -> this.getItemDatasIterator(ItemData.class), itemData -> this.addItemData(itemData), () -> {});
+    }
+
+    public List<TimeTracker.TimeOfDay> getValidTimeOfDays() {
+        return getItemData().getValidTimeOfDays();
+    }
+
+    public void setValidTimeOfDays(List<TimeTracker.TimeOfDay> validTimeOfDays) {
+        getItemData().setValidTimeOfDays(validTimeOfDays);
+    }
+
+    public void setValidTimeOfDays(Set<TimeTracker.TimeOfDay> validTimeOfDays) {
+        getItemData().setValidTimeOfDays(validTimeOfDays);
+    }
+
+    public boolean isDisposable() {
+        return getItemData().isDisposable();
+    }
+
+    public int getMaxUses() {
+        return getItemData().getMaxUses();
+    }
+
+    public String getInternalItemName() {
+        return getItemData().getInternalItemName();
+    }
+
+    public String getItemName() {
+        return getItemData().getItemName();
+    }
+
+    public String getItemDescription() {
+        return getItemData().getItemDescription();
+    }
+
+    public List<String> getItemTriggers() {
+        return getItemData().getItemTriggers();
+    }
+
+    public void setItemTriggers(List<String> itemTriggers) {
+        getItemData().setItemTriggers(itemTriggers);
+    }
+
+    public String getRestingName() {
+        return getItemData().getRestingName();
+    }
+
+    public int getItemHalfLifeTicks() {
+        return getItemData().getItemHalfLifeTicks();
+    }
+
+    public Rarity getRarity() {
+        return getItemData().getRarity();
+    }
+
+    public void setRarity(Rarity rarity) {
+        getItemData().setRarity(rarity);
+    }
+
+    public int getValueInGold() {
+        return getItemData().getValueInGold();
+    }
+
+    public void setItemName(String itemName) {
+        getItemData().setItemName(itemName);
+    }
+
+    public void setItemDescription(String itemDescription) {
+        getItemData().setItemDescription(itemDescription);
+    }
+
+    public void setInternalItemName(String internalItemName) {
+        getItemData().setInternalItemName(internalItemName);
+    }
+
+    public void setRestingName(String restingName) {
+        getItemData().setRestingName(restingName);
+    }
+
+    public void setItemHalfLifeTicks(int itemHalfLifeTicks) {
+        getItemData().setItemHalfLifeTicks(itemHalfLifeTicks);
+    }
+
+    public void setValueInGold(int valueInGold) {
+        getItemData().setValueInGold(valueInGold);
+    }
+
+    public void setMaxUses(int maxUses) {
+        getItemData().setMaxUses(maxUses);
+    }
+
+    public void setDisposable(boolean disposable) {
+        getItemData().setDisposable(disposable);
+    }
+
+    public EquipmentData getEquipmentData() {
+        return getItemData().getEquipmentData();
+    }
+
+    public void setEquipmentData(EquipmentData equipment) {
+        getItemData().setEquipmentData(equipment);
+    }
+
+    public EquipmentData createEquipmentData() {
+        return getItemData().createEquipmentData();
+    }
+
+    public Set<EffectData> getEffectDatas() {
+        return getItemData().getEffectDatas();
+    }
+
+    public void setEffectDatas(Set<EffectData> effects) {
+        getItemData().setEffectDatas(effects);
+    }
+
+    public EffectData createEffectData() {
+        return getItemData().createEffectData();
+    }
+
+    public StatData getItemApplyStatData() {
+        return getItemData().getItemApplyStatData();
+    }
+
+    public void setItemApplyStatData(StatData stats) {
+        getItemData().setItemApplyStatData(stats);
+    }
+
+    public StatData createItemApplyStatData() {
+        return getItemData().createItemApplyStatData();
+    }
+
+    public LootData getLootData() {
+        return getItemData().getLootData();
+    }
+
+    public void setLootData(LootData loot) {
+        getItemData().setLootData(loot);
+    }
+
+    public LootData createLootData() {
+        return getItemData().createLootData();
+    }
+
+    public static void copyItem(ItemInstanceData dest, ItemInstance src, ItemData itemData) {
+        dest.setWithPlayer(src.isWithPlayer());
+        dest.setNumberOfUses(src.getNumberOfUses());
+        dest.setHasBeenWithPlayer(src.isHasBeenWithPlayer());
+        dest.setItemId(src.getItemId());
+        if(dest.getItemData() == null )
+            dest.setItemData(itemData);
+        else if(!dest.getItemData().getInternalItemName().equals(itemData.getInternalItemName())) {
+            dest.removeItemData(dest.getItemData());
+            dest.setItemData(itemData);
+        }
+    }
+
+    public static ItemInstance copyItem(ItemInstanceData src) {
+        ItemInstance itemInstance = new ItemInstanceImpl();
+        itemInstance.setWithPlayer(src.isWithPlayer());
+        itemInstance.setNumberOfUses(src.getNumberOfUses());
+        itemInstance.setHasBeenWithPlayer(src.isHasBeenWithPlayer());
+        itemInstance.setItemId(src.getItemId());
+        itemInstance.setItem(ItemData.copyItem(src.getItemData()));
+        return itemInstance;
+    }
+}
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
index 71f12f732bd09a2f874d89d7a7d7cfe7e94520ed..759ea8698e21a08400ea61469fca1cd4011dcea7 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/LootData.java
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 package com.syncleus.aethermud.storage.graphdb.model;
+import com.google.common.collect.Lists;
 
-import com.syncleus.aethermud.items.Item;
 import com.syncleus.aethermud.items.Loot;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
@@ -55,6 +55,7 @@ public abstract class LootData extends AbstractInterceptingVertexFrame {
 
     public static Loot copyLoot(LootData src) {
         Loot retVal = new Loot();
+
         try {
             PropertyUtils.copyProperties(retVal, src);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
diff --git a/src/main/java/com/syncleus/aethermud/world/model/Room.java b/src/main/java/com/syncleus/aethermud/world/model/Room.java
index 54e6f8c16f78dd6b6bf72bb2e369576acf22ff35..f1cef4663754563029617ae90ffd938bc4d0cc92 100644
--- a/src/main/java/com/syncleus/aethermud/world/model/Room.java
+++ b/src/main/java/com/syncleus/aethermud/world/model/Room.java
@@ -19,7 +19,7 @@ import com.syncleus.aethermud.core.GameManager;
 import com.syncleus.aethermud.core.service.TimeTracker;
 import com.syncleus.aethermud.entity.AetherMudEntity;
 import com.syncleus.aethermud.items.Forage;
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.merchant.Merchant;
 import com.syncleus.aethermud.npc.NpcSpawn;
 import com.syncleus.aethermud.player.Player;
@@ -290,12 +290,12 @@ public abstract class Room extends AetherMudEntity {
     @Override
     public void run() {
         for (String itemId : itemIds) {
-            Optional<Item> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
+            Optional<ItemInstance> itemOptional = gameManager.getEntityManager().getItemEntity(itemId);
             if (!itemOptional.isPresent()) {
                 removePresentItem(itemId);
                 continue;
             }
-            Item itemEntity = itemOptional.get();
+            ItemInstance itemEntity = itemOptional.get();
             if (itemEntity.isHasBeenWithPlayer()) {
                 continue;
             }
diff --git a/src/test/java/com/syncleus/aethermud/player/combatsimuation/CombatSimulationDetails.java b/src/test/java/com/syncleus/aethermud/player/combatsimuation/CombatSimulationDetails.java
index 194fdeee94980bd5ee105e3e5b364f52503671a5..8349c94a514cc4871bab79158dbd659a3b94387e 100644
--- a/src/test/java/com/syncleus/aethermud/player/combatsimuation/CombatSimulationDetails.java
+++ b/src/test/java/com/syncleus/aethermud/player/combatsimuation/CombatSimulationDetails.java
@@ -15,7 +15,7 @@
  */
 package com.syncleus.aethermud.player.combatsimuation;
 
-import com.syncleus.aethermud.items.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.npc.NpcSpawn;
 
 import java.util.Set;
@@ -24,10 +24,10 @@ public class CombatSimulationDetails {
 
     private final int level;
     private final int totalIterations;
-    private final Set<Item> equipmentSet;
+    private final Set<ItemInstance> equipmentSet;
     private final NpcSpawn npcSpawn;
 
-    public CombatSimulationDetails(int level, Set<Item> equipmentSet, NpcSpawn npcSpawn) {
+    public CombatSimulationDetails(int level, Set<ItemInstance> equipmentSet, NpcSpawn npcSpawn) {
         this.level = level;
         this.totalIterations = 1000;
         this.equipmentSet = equipmentSet;
@@ -46,7 +46,7 @@ public class CombatSimulationDetails {
         return npcSpawn;
     }
 
-    public Set<Item> getEquipmentSet() {
+    public Set<ItemInstance> getEquipmentSet() {
         return equipmentSet;
     }
 }
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 df6d0d85e4e67c7d4ca1a531aaca31dbf4e1ae86..af786999618b43ba8bf471637d528fdd981b8d76 100644
--- a/src/test/java/com/syncleus/aethermud/player/combatsimuation/NpcTestHarness.java
+++ b/src/test/java/com/syncleus/aethermud/player/combatsimuation/NpcTestHarness.java
@@ -21,7 +21,7 @@ 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.Item;
+import com.syncleus.aethermud.items.ItemInstance;
 import com.syncleus.aethermud.npc.NpcSpawn;
 import com.syncleus.aethermud.npc.NpcBuilder;
 import com.syncleus.aethermud.player.*;
@@ -137,18 +137,18 @@ public class NpcTestHarness {
         }
     }
 
-    private Set<Item> getEarlyLevelArmorSet() {
+    private Set<ItemInstance> getEarlyLevelArmorSet() {
         //  return Sets.newHashSet(ItemType.BERSERKER_BATON.create(), ItemType.BERSEKER_BOOTS.create(), ItemType.BERSEKER_SHORTS.create());
         return Sets.newConcurrentHashSet();
     }
 
-    private Set<Item> getMidLevelArmorSet() {
-        Set<Item> armorSet = getEarlyLevelArmorSet();
+    private Set<ItemInstance> getMidLevelArmorSet() {
+        Set<ItemInstance> armorSet = getEarlyLevelArmorSet();
         //  armorSet.addAll(Sets.newHashSet(ItemType.BERSERKER_BRACERS.create(), ItemType.BERSERKER_CHEST.create()));
         return armorSet;
     }
 
-    private void processRunAndVerify(NpcSpawn testNpcSpawn, int desiredLevel, Set<Item> equipment, float winPctMax, float winPctMin, int maxRounds, int minRounds, int maxAvgGold, int minAvgGold) throws Exception {
+    private void processRunAndVerify(NpcSpawn testNpcSpawn, int desiredLevel, Set<ItemInstance> equipment, float winPctMax, float winPctMin, int maxRounds, int minRounds, int maxAvgGold, int minAvgGold) throws Exception {
         CombatSimulationDetails combatSimulationDetailsLevel = new CombatSimulationDetails(desiredLevel, equipment, testNpcSpawn);
         CombatSimulationResult combatSimulationResultLevel = executeCombat(combatSimulationDetailsLevel);
         printCombatResults(combatSimulationDetailsLevel, combatSimulationResultLevel);
@@ -182,7 +182,7 @@ public class NpcTestHarness {
                 playerWins++;
                 int gold = (int) gameManager.getLootManager().lootGoldAmountReturn(npcSpawn.getLoot());
                 totalGold += gold;
-                Set<Item> items = gameManager.getLootManager().lootItemsReturn(npcSpawn.getLoot());
+                Set<ItemInstance> items = gameManager.getLootManager().lootItemsReturn(npcSpawn.getLoot());
                 items.forEach(item -> {
                     if (!drops.containsKey(item.getItemName())) {
                         drops.put(item.getItemName(), new AtomicInteger(1));
@@ -254,7 +254,7 @@ public class NpcTestHarness {
         return player;
     }
 
-    private void equipArmor(Player player, Set<Item> equipment) {
+    private void equipArmor(Player player, Set<ItemInstance> equipment) {
         equipment.forEach(item -> {
             entityManager.saveItem(item);
             gameManager.acquireItem(player, item.getItemId());