From 8880ac8513bbc9c1da4a160da317949ff2e26015 Mon Sep 17 00:00:00 2001
From: Jeffrey Phillips Freeman <jeffrey.freeman@syncleus.com>
Date: Thu, 21 Sep 2017 15:58:00 -0400
Subject: [PATCH] fix: fixing titan specific issues.

---
 pom.xml                                       |  26 +++-
 .../storage/graphdb/GraphStorageFactory.java  |  82 ++++++++-----
 .../storage/graphdb/model/CoolDownData.java   |   4 +-
 .../storage/graphdb/model/EffectData.java     |   4 +-
 .../storage/graphdb/model/ItemData.java       |  54 +++++----
 .../graphdb/model/ItemInstanceData.java       |  11 +-
 .../storage/graphdb/model/LootData.java       |   5 +-
 .../storage/graphdb/model/NpcData.java        |  57 ++++-----
 .../storage/graphdb/model/PlayerData.java     |  85 ++++++++++----
 .../storage/graphdb/model/SpawnRuleData.java  |   7 +-
 .../storage/graphdb/model/StatData.java       |   4 +-
 .../resources/titan-cassandra-es.properties   |  10 ++
 .../common/ColorizedTextTemplateTest.java     |  14 +--
 vagrant/Vagrantfile                           |  16 +++
 vagrant/bootstrap.sh                          | 111 ++++++++++++++++++
 15 files changed, 344 insertions(+), 146 deletions(-)
 create mode 100644 src/main/resources/titan-cassandra-es.properties
 create mode 100644 vagrant/Vagrantfile
 create mode 100755 vagrant/bootstrap.sh

diff --git a/pom.xml b/pom.xml
index 86042d1b..709ce171 100644
--- a/pom.xml
+++ b/pom.xml
@@ -187,12 +187,23 @@
         <dependency>
             <groupId>com.syncleus.ferma</groupId>
             <artifactId>ferma</artifactId>
-            <version>3.1.0</version>
+            <version>3.1.1-SNAPSHOT</version>
+            <exclusions>
+                <exclusion>
+                        <groupId>org.apache.tinkerpop</groupId>
+                        <artifactId>gremlin-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tinkerpop</groupId>
+            <artifactId>gremlin-core</artifactId>
+            <version>3.1.8</version>
         </dependency>
         <dependency>
             <groupId>org.apache.tinkerpop</groupId>
             <artifactId>tinkergraph-gremlin</artifactId>
-            <version>3.2.4</version>
+            <version>3.1.8</version>
         </dependency>
         <dependency>
             <groupId>org.reflections</groupId>
@@ -205,9 +216,14 @@
             <version>1.9.3</version>
         </dependency>
         <dependency>
-            <groupId>com.syncleus.ferma</groupId>
-            <artifactId>ferma-orientdb</artifactId>
-            <version>3.0.0</version>
+            <groupId>com.thinkaurelius.titan</groupId>
+            <artifactId>titan-cassandra</artifactId>
+            <version>1.1.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.thinkaurelius.titan</groupId>
+            <artifactId>titan-es</artifactId>
+            <version>1.1.0-SNAPSHOT</version>
         </dependency>
     </dependencies>
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphStorageFactory.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphStorageFactory.java
index de0dc9bc..68c93f82 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphStorageFactory.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/GraphStorageFactory.java
@@ -15,33 +15,52 @@
  */
 package com.syncleus.aethermud.storage.graphdb;
 
-import com.google.common.base.Function;
-import com.syncleus.aethermud.storage.graphdb.model.PlayerData;
-import com.syncleus.ferma.ext.orientdb.OrientTransactionFactory;
-import com.syncleus.ferma.ext.orientdb.impl.OrientTransactionFactoryImpl;
-import com.syncleus.ferma.tx.Tx;
-import com.syncleus.ferma.tx.TxFactory;
-import org.apache.tinkerpop.gremlin.orientdb.OrientGraphFactory;
-
-import java.util.Optional;
-import java.util.function.Consumer;
+import com.google.common.collect.Sets;
+import com.syncleus.aethermud.items.ItemInstance;
+import com.syncleus.aethermud.storage.graphdb.model.*;
+import com.syncleus.ferma.DelegatingFramedGraph;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.tinkerpop.gremlin.structure.Transaction;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
 
 public class GraphStorageFactory {
-    OrientGraphFactory graphFactory;
-    OrientTransactionFactory txFactory;
+
+    private static final Set<? extends Class<?>> MODEL_CLASSES = Sets.newHashSet(Arrays.asList(
+        AetherMudMessageData.class,
+        CoolDownData.class,
+        EffectData.class,
+        EquipmentData.class,
+        ItemData.class,
+        ItemInstance.class,
+        LootData.class,
+        NpcData.class,
+        PlayerData.class,
+        SpawnRuleData.class,
+        StatData.class
+    ));
+
+    private static final String PROPS_PATH = "titan-cassandra-es.properties";
+
+    private TitanGraph titanGraph;
 
     public GraphStorageFactory(String connectUrl, String username, String password) {
-        graphFactory = new OrientGraphFactory(connectUrl, username, password);
-        txFactory = new OrientTransactionFactoryImpl(graphFactory, true, "com.syncleus.aethermud.storage.graphdb.model");
-        txFactory.setupElementClasses();
-        txFactory.addEdgeClass("itemApplyStats");
+        try {
+            Configuration conf = new PropertiesConfiguration(PROPS_PATH);
+            titanGraph = TitanFactory.open(conf);
+        } catch (ConfigurationException e) {
+            throw new IllegalStateException(e);
+        }
     }
 
     public GraphStorageFactory(String connectUrl) {
-        graphFactory = new OrientGraphFactory(connectUrl);
-        txFactory = new OrientTransactionFactoryImpl(graphFactory, true, "com.syncleus.aethermud.storage.graphdb.model");
-        txFactory.setupElementClasses();
-        txFactory.addEdgeClass("itemApplyStats");
+        this(connectUrl, null, null);
     }
 
     public GraphStorageFactory() {
@@ -49,18 +68,17 @@ public class GraphStorageFactory {
     }
 
     public GraphStorageFactory(boolean onDisk) {
-        this(onDisk ? "plocal:./aethermud-graphdb-orientdb" : "memory:tinkerpop");
+        this(null, null, null);
     }
 
     public AetherMudTx beginTransaction() {
-        return new AetherMudTx(txFactory.tx());
+        return new AetherMudTx(this.titanGraph);
     }
 
     public void close() {
-        if( this.graphFactory != null ) {
-            this.graphFactory.close();
-            this.graphFactory = null;
-            this.txFactory = null;
+        if( this.titanGraph != null ) {
+            this.titanGraph.close();
+            this.titanGraph = null;
         }
     }
 
@@ -71,20 +89,20 @@ public class GraphStorageFactory {
     }
 
     public static class AetherMudTx implements AutoCloseable {
-        private final Tx tx;
         private final GraphDbAetherMudStorage storage;
+        private final Transaction tx;
 
-        public AetherMudTx(Tx tx) {
-            this.tx = tx;
-            this.storage = new GraphDbAetherMudStorage(tx.getGraph());
+        public AetherMudTx(final TitanGraph titanGraph) {
+            this.tx = titanGraph.tx();
+            this.storage = new GraphDbAetherMudStorage(new DelegatingFramedGraph<TitanGraph>(this.tx.createThreadedTx(), true, MODEL_CLASSES));
         }
 
         public void success() {
-            tx.success();
+            tx.commit();
         }
 
         public void failure() {
-            tx.failure();
+            tx.rollback();
         }
 
         public void close() {
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/CoolDownData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/CoolDownData.java
index b7927aaa..f507e2ba 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/CoolDownData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/CoolDownData.java
@@ -17,12 +17,12 @@ package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.aethermud.player.CoolDown;
 import com.syncleus.aethermud.player.CoolDownType;
+import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 
 @GraphElement
-public abstract class CoolDownData extends AbstractInterceptingVertexFrame {
+public abstract class CoolDownData extends AbstractVertexFrame {
     @Property("type")
     public abstract CoolDownType getCoolDownType();
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
index 4638afb9..4f4fb3f2 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/EffectData.java
@@ -18,10 +18,10 @@ package com.syncleus.aethermud.storage.graphdb.model;
 import com.syncleus.aethermud.items.Effect;
 import com.syncleus.aethermud.stats.Stats;
 import com.syncleus.aethermud.storage.graphdb.DataUtils;
+import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
@@ -31,7 +31,7 @@ import java.util.Iterator;
 import java.util.List;
 
 @GraphElement
-public abstract class EffectData extends AbstractInterceptingVertexFrame {
+public abstract class EffectData extends AbstractVertexFrame {
     @Property("effectName")
     public abstract String getEffectName();
 
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 c666982e..d2d1cf99 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
@@ -22,10 +22,10 @@ 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.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
@@ -33,7 +33,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.util.*;
 
 @GraphElement
-public abstract class ItemData extends AbstractInterceptingVertexFrame {
+public abstract class ItemData extends AbstractVertexFrame {
     @Property("validTimeOfDays")
     public abstract List<TimeTracker.TimeOfDay> getValidTimeOfDays();
 
@@ -129,7 +129,8 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     public abstract void removeSpawnRuleData(SpawnRuleData spawnRule);
 
     public void setSpawnRulesDatas(List<SpawnRuleData> spawnRules) {
-        DataUtils.setAllElements(spawnRules, () -> this.getSpawnRulesDataIterator(SpawnRuleData.class), ruleData -> this.addSpawnRuleData(ruleData), () -> {} );
+        DataUtils.setAllElements(spawnRules, () -> this.getSpawnRulesDataIterator(SpawnRuleData.class), ruleData -> this.addSpawnRuleData(ruleData), () -> {
+        });
     }
 
     public SpawnRuleData createSpawnRuleData() {
@@ -143,7 +144,7 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
 
     public EquipmentData getEquipmentData() {
         Iterator<? extends EquipmentData> allEquipment = this.getEquipmentDataIterator(EquipmentData.class);
-        if( allEquipment.hasNext() )
+        if (allEquipment.hasNext())
             return allEquipment.next();
         else
             return null;
@@ -156,11 +157,11 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     public abstract void removeEquipmentData(EquipmentData equipment);
 
     public void setEquipmentData(EquipmentData equipment) {
-        DataUtils.setAllElements(Collections.singletonList(equipment), () -> this.getEquipmentDataIterator(EquipmentData.class), equipmentData -> this.addEquipmentData(equipmentData), () -> createEquipmentData() );
+        DataUtils.setAllElements(Collections.singletonList(equipment), () -> this.getEquipmentDataIterator(EquipmentData.class), equipmentData -> this.addEquipmentData(equipmentData), () -> createEquipmentData());
     }
 
     public EquipmentData createEquipmentData() {
-        if( this.getEquipmentData() != null )
+        if (this.getEquipmentData() != null)
             throw new IllegalStateException("Already has stats, can't create another");
         final EquipmentData equipment = this.getGraph().addFramedVertex(EquipmentData.class);
         this.addEquipmentData(equipment);
@@ -181,7 +182,8 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     }
 
     public void setEffectDatas(Set<EffectData> effects) {
-        DataUtils.setAllElements(effects, () -> this.getEffectDatasIterator(EffectData.class), effectData -> this.addEffectData(effectData), () -> {} );
+        DataUtils.setAllElements(effects, () -> this.getEffectDatasIterator(EffectData.class), effectData -> this.addEffectData(effectData), () -> {
+        });
     }
 
     public EffectData createEffectData() {
@@ -195,7 +197,7 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
 
     public StatData getItemApplyStatData() {
         Iterator<? extends StatData> allStats = this.getItemApplyStatDatasIterator(StatData.class);
-        if( allStats.hasNext() )
+        if (allStats.hasNext())
             return allStats.next();
         else
             return null;
@@ -208,11 +210,11 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     public abstract void removeStatData(StatData stats);
 
     public void setItemApplyStatData(StatData stats) {
-        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getItemApplyStatDatasIterator(StatData.class), statsData -> this.addStatData(statsData), () -> createItemApplyStatData() );
+        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getItemApplyStatDatasIterator(StatData.class), statsData -> this.addStatData(statsData), () -> createItemApplyStatData());
     }
 
     public StatData createItemApplyStatData() {
-        if( this.getItemApplyStatData() != null )
+        if (this.getItemApplyStatData() != null)
             throw new IllegalStateException("Already has stats, can't create another");
         final StatData stats = this.getGraph().addFramedVertex(StatData.class);
         stats.setAgile(0);
@@ -242,7 +244,7 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
 
     public LootData getLootData() {
         Iterator<? extends LootData> allStats = this.getLootDatasIterator(LootData.class);
-        if( allStats.hasNext() )
+        if (allStats.hasNext())
             return allStats.next();
         else
             return null;
@@ -255,11 +257,11 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
     public abstract void removeLootData(LootData loot);
 
     public void setLootData(LootData loot) {
-        DataUtils.setAllElements(Collections.singletonList(loot), () -> this.getLootDatasIterator(LootData.class), lootData -> this.addLootData(lootData), () -> createLootData() );
+        DataUtils.setAllElements(Collections.singletonList(loot), () -> this.getLootDatasIterator(LootData.class), lootData -> this.addLootData(lootData), () -> createLootData());
     }
 
     public LootData createLootData() {
-        if( this.getLootData() != null )
+        if (this.getLootData() != null)
             throw new IllegalStateException("Already has loot, can't create another");
         final LootData loot = this.getGraph().addFramedVertex(LootData.class);
         loot.setLootGoldMax(0);
@@ -272,19 +274,19 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
         try {
             PropertyUtils.copyProperties(dest, src);
 
-            for(SpawnRuleData data : dest.getSpawnRuleDatas())
+            for (SpawnRuleData data : dest.getSpawnRuleDatas())
                 data.remove();
-            for(SpawnRule spawnRule : src.getSpawnRules())
+            for (SpawnRule spawnRule : src.getSpawnRules())
                 SpawnRuleData.copySpawnRule(dest.createSpawnRuleData(), spawnRule);
 
-            if( src.getItemApplyStats() != null )
+            if (src.getItemApplyStats() != null)
                 StatData.copyStats((dest.getItemApplyStatData() != null ? dest.getItemApplyStatData() : dest.createItemApplyStatData()), src.getItemApplyStats());
-            if(src.getLoot() != null )
+            if (src.getLoot() != null)
                 LootData.copyLoot((dest.getLootData() != null ? dest.getLootData() : dest.createLootData()), src.getLoot());
-            if( src.getEquipment() != null )
-                EquipmentData.copyEquipment((dest.getEquipmentData() != null ? dest.getEquipmentData() :dest.createEquipmentData()), src.getEquipment());
-            if( src.getEffects() != null ) {
-                for(EffectData data : dest.getEffectDatas())
+            if (src.getEquipment() != null)
+                EquipmentData.copyEquipment((dest.getEquipmentData() != null ? dest.getEquipmentData() : dest.createEquipmentData()), src.getEquipment());
+            if (src.getEffects() != null) {
+                for (EffectData data : dest.getEffectDatas())
                     data.remove();
                 for (Effect effect : src.getEffects())
                     EffectData.copyEffect(dest.createEffectData(), effect);
@@ -300,23 +302,23 @@ public abstract class ItemData extends AbstractInterceptingVertexFrame {
             PropertyUtils.copyProperties(retVal, src);
 
             Set<SpawnRule> rules = new HashSet<>();
-            for(SpawnRuleData spawnRuleData : src.getSpawnRuleDatas())
+            for (SpawnRuleData spawnRuleData : src.getSpawnRuleDatas())
                 rules.add(SpawnRuleData.copySpawnRule(spawnRuleData));
             retVal.setSpawnRules(Collections.unmodifiableSet(rules));
 
             EquipmentData equipmentData = src.getEquipmentData();
-            if(equipmentData != null)
+            if (equipmentData != null)
                 retVal.setEquipment(EquipmentData.copyEquipment(equipmentData));
 
             LootData lootData = src.getLootData();
-            if(lootData != null)
+            if (lootData != null)
                 retVal.setLoot(LootData.copyLoot(lootData));
 
             StatData applyStats = src.getItemApplyStatData();
-            if( applyStats != null )
+            if (applyStats != null)
                 retVal.setItemApplyStats(StatData.copyStats(applyStats));
 
-            if( src.getEffectDatas() != null ) {
+            if (src.getEffectDatas() != null) {
                 Set<Effect> effects = new HashSet<>();
                 for (EffectData effect : src.getEffectDatas())
                     effects.add(EffectData.copyEffect(effect));
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
index 9f78bae0..71ae07fc 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemInstanceData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/ItemInstanceData.java
@@ -20,10 +20,10 @@ 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.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
 import java.util.Collections;
@@ -32,7 +32,7 @@ import java.util.List;
 import java.util.Set;
 
 @GraphElement
-public abstract class ItemInstanceData extends AbstractInterceptingVertexFrame {
+public abstract class ItemInstanceData extends AbstractVertexFrame {
     @Property("withPlayer")
     public abstract boolean isWithPlayer();
 
@@ -75,7 +75,8 @@ public abstract class ItemInstanceData extends AbstractInterceptingVertexFrame {
     public abstract void removeItemData(ItemData item);
 
     public void setItemData(ItemData item) {
-        DataUtils.setAllElements(Collections.singletonList(item), () -> this.getItemDatasIterator(ItemData.class), itemData -> this.addItemData(itemData), () -> {});
+        DataUtils.setAllElements(Collections.singletonList(item), () -> this.getItemDatasIterator(ItemData.class), itemData -> this.addItemData(itemData), () -> {
+        });
     }
 
     public List<TimeTracker.TimeOfDay> getValidTimeOfDays() {
@@ -223,9 +224,9 @@ public abstract class ItemInstanceData extends AbstractInterceptingVertexFrame {
         dest.setNumberOfUses(src.getNumberOfUses());
         dest.setHasBeenWithPlayer(src.isHasBeenWithPlayer());
         dest.setItemId(src.getItemId());
-        if(dest.getItemData() == null )
+        if (dest.getItemData() == null)
             dest.setItemData(itemData);
-        else if(!dest.getItemData().getInternalItemName().equals(itemData.getInternalItemName())) {
+        else if (!dest.getItemData().getInternalItemName().equals(itemData.getInternalItemName())) {
             dest.removeItemData(dest.getItemData());
             dest.setItemData(itemData);
         }
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 759ea869..93da9018 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,19 +14,18 @@
  * limitations under the License.
  */
 package com.syncleus.aethermud.storage.graphdb.model;
-import com.google.common.collect.Lists;
 
 import com.syncleus.aethermud.items.Loot;
+import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 
 @GraphElement
-public abstract class LootData extends AbstractInterceptingVertexFrame {
+public abstract class LootData extends AbstractVertexFrame {
     @Property("internalItemNames")
     public abstract List<String> getInternalItemNames();
 
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
index 278844e7..8eec9ce9 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/NpcData.java
@@ -23,10 +23,10 @@ import com.syncleus.aethermud.npc.Temperament;
 import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.storage.graphdb.DataUtils;
 import com.syncleus.aethermud.world.model.Area;
+import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
@@ -37,7 +37,7 @@ import java.util.Iterator;
 import java.util.List;
 
 @GraphElement
-public abstract class NpcData extends AbstractInterceptingVertexFrame {
+public abstract class NpcData extends AbstractVertexFrame {
     @Property("name")
     public abstract String getName();
 
@@ -76,7 +76,8 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     public abstract void removeSpawnRuleData(SpawnRuleData spawnRule);
 
     public void setSpawnRulesDatas(List<SpawnRuleData> spawnRules) {
-        DataUtils.setAllElements(spawnRules, () -> this.getSpawnRulesDataIterator(SpawnRuleData.class), ruleData -> this.addSpawnRuleData(ruleData), () -> {} );
+        DataUtils.setAllElements(spawnRules, () -> this.getSpawnRulesDataIterator(SpawnRuleData.class), ruleData -> this.addSpawnRuleData(ruleData), () -> {
+        });
     }
 
     public SpawnRuleData createSpawnRuleData() {
@@ -106,7 +107,7 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
 
     public LootData getLootData() {
         Iterator<? extends LootData> allLoots = this.getLootDataIterator(LootData.class);
-        if( allLoots.hasNext() )
+        if (allLoots.hasNext())
             return allLoots.next();
         else
             return null;
@@ -119,11 +120,11 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     public abstract void removeLootData(LootData loot);
 
     public void setLootData(LootData loot) {
-        DataUtils.setAllElements(Collections.singletonList(loot), () -> this.getLootDataIterator(LootData.class), lootData -> this.addLootData(lootData), () -> this.createLootData() );
+        DataUtils.setAllElements(Collections.singletonList(loot), () -> this.getLootDataIterator(LootData.class), lootData -> this.addLootData(lootData), () -> this.createLootData());
     }
 
     public LootData createLootData() {
-        if( this.getLootData() != null )
+        if (this.getLootData() != null)
             throw new IllegalStateException("Already has stats, can't create another");
         final LootData loot = this.getGraph().addFramedVertex(LootData.class);
         loot.setInternalItemNames(Lists.newArrayList());
@@ -138,7 +139,7 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
 
     public StatData getStatsData() {
         Iterator<? extends StatData> allStats = this.getAllStatsData(StatData.class);
-        if( allStats.hasNext() )
+        if (allStats.hasNext())
             return allStats.next();
         else
             return null;
@@ -151,11 +152,11 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     public abstract void removeStatsData(StatData stats);
 
     public void setStatsData(StatData stats) {
-        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getAllStatsData(StatData.class), statsData -> this.addStatsData(statsData), () -> this.createStatsData() );
+        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getAllStatsData(StatData.class), statsData -> this.addStatsData(statsData), () -> this.createStatsData());
     }
 
     public StatData createStatsData() {
-        if( this.getStatsData() != null )
+        if (this.getStatsData() != null)
             throw new IllegalStateException("Already has stats, can't create another");
         final StatData stats = this.getGraph().addFramedVertex(StatData.class);
         stats.setAgile(0);
@@ -194,7 +195,7 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     public abstract void removeAttackMessageData(AetherMudMessageData message);
 
     public void setAttackMessageDatas(List<AetherMudMessageData> messages) {
-        DataUtils.setAllElements(messages, () -> this.getAttackMessageDataIterator(AetherMudMessageData.class), message -> this.addAttackMessageData(message), () -> this.createAttackMessageData() );
+        DataUtils.setAllElements(messages, () -> this.getAttackMessageDataIterator(AetherMudMessageData.class), message -> this.addAttackMessageData(message), () -> this.createAttackMessageData());
     }
 
     public AetherMudMessageData createAttackMessageData() {
@@ -217,7 +218,7 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     public abstract void removeIdleMessageData(AetherMudMessageData message);
 
     public void setIdleMessageDatas(List<AetherMudMessageData> messages) {
-        DataUtils.setAllElements(messages, () -> this.getIdleMessageDataIterator(AetherMudMessageData.class), message -> this.addIdleMessageData(message), () -> this.createIdleMessageData() );
+        DataUtils.setAllElements(messages, () -> this.getIdleMessageDataIterator(AetherMudMessageData.class), message -> this.addIdleMessageData(message), () -> this.createIdleMessageData());
     }
 
     public AetherMudMessageData createIdleMessageData() {
@@ -240,7 +241,7 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     public abstract void removeBattleMessageData(AetherMudMessageData message);
 
     public void setBattleMessageDatas(List<AetherMudMessageData> messages) {
-        DataUtils.setAllElements(messages, () -> this.getBattleMessageDataIterator(AetherMudMessageData.class), message -> this.addBattleMessageData(message), () -> this.createBattleMessageData() );
+        DataUtils.setAllElements(messages, () -> this.getBattleMessageDataIterator(AetherMudMessageData.class), message -> this.addBattleMessageData(message), () -> this.createBattleMessageData());
     }
 
     public AetherMudMessageData createBattleMessageData() {
@@ -263,7 +264,7 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
     public abstract void removeCriticalAttackMessageData(AetherMudMessageData message);
 
     public void setCriticalAttackMessageDatas(List<AetherMudMessageData> messages) {
-        DataUtils.setAllElements(messages, () -> this.getCriticalAttackMessageDataIterator(AetherMudMessageData.class), message -> this.addCriticalAttackMessageData(message), () -> this.createCriticalAttackMessageData() );
+        DataUtils.setAllElements(messages, () -> this.getCriticalAttackMessageDataIterator(AetherMudMessageData.class), message -> this.addCriticalAttackMessageData(message), () -> this.createCriticalAttackMessageData());
     }
 
     public AetherMudMessageData createCriticalAttackMessageData() {
@@ -285,30 +286,30 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
             LootData.copyLoot((dest.getLootData() != null ? dest.getLootData() : dest.createLootData()), src.getLoot());
             StatData.copyStats((dest.getStatsData() != null ? dest.getStatsData() : dest.createStatsData()), src.getStats());
 
-            for(SpawnRuleData data : dest.getSpawnRuleDatas())
+            for (SpawnRuleData data : dest.getSpawnRuleDatas())
                 data.remove();
-            for(SpawnRule spawnRule : src.getSpawnRules())
+            for (SpawnRule spawnRule : src.getSpawnRules())
                 SpawnRuleData.copySpawnRule(dest.createSpawnRuleData(), spawnRule);
 
-            for(AetherMudMessageData data : dest.getAttackMessageDatas())
+            for (AetherMudMessageData data : dest.getAttackMessageDatas())
                 data.remove();
-            for(AetherMudMessage message : src.getAttackMessages())
+            for (AetherMudMessage message : src.getAttackMessages())
                 AetherMudMessageData.copyAetherMudMessage(dest.createAttackMessageData(), message);
 
 
-            for(AetherMudMessageData data : dest.getBattleMessageDatas())
+            for (AetherMudMessageData data : dest.getBattleMessageDatas())
                 data.remove();
-            for(AetherMudMessage message : src.getBattleMessages())
+            for (AetherMudMessage message : src.getBattleMessages())
                 AetherMudMessageData.copyAetherMudMessage(dest.createBattleMessageData(), message);
 
-            for(AetherMudMessageData data : dest.getCriticalAttackMessageDatas())
+            for (AetherMudMessageData data : dest.getCriticalAttackMessageDatas())
                 data.remove();
-            for(AetherMudMessage message : src.getCriticalAttackMessages())
+            for (AetherMudMessage message : src.getCriticalAttackMessages())
                 AetherMudMessageData.copyAetherMudMessage(dest.createCriticalAttackMessageData(), message);
 
-            for(AetherMudMessageData data : dest.getIdleMessageDatas())
+            for (AetherMudMessageData data : dest.getIdleMessageDatas())
                 data.remove();
-            for(AetherMudMessage message : src.getIdleMessages())
+            for (AetherMudMessage message : src.getIdleMessages())
                 AetherMudMessageData.copyAetherMudMessage(dest.createIdleMessageData(), message);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
             throw new IllegalStateException("Could not copy properties", e);
@@ -323,27 +324,27 @@ public abstract class NpcData extends AbstractInterceptingVertexFrame {
             retVal.setStats(StatData.copyStats(src.getStatsData()));
 
             List<SpawnRule> rules = new ArrayList<>();
-            for(SpawnRuleData spawnRuleData : src.getSpawnRuleDatas())
+            for (SpawnRuleData spawnRuleData : src.getSpawnRuleDatas())
                 rules.add(SpawnRuleData.copySpawnRule(spawnRuleData));
             retVal.setSpawnRules(Collections.unmodifiableList(rules));
 
             List<AetherMudMessage> attackMessages = new ArrayList<>();
-            for(AetherMudMessageData message : src.getAttackMessageDatas())
+            for (AetherMudMessageData message : src.getAttackMessageDatas())
                 attackMessages.add(AetherMudMessageData.copyAetherMudMessage(message));
             retVal.setAttackMessages(Collections.unmodifiableList(attackMessages));
 
             List<AetherMudMessage> criticalAttackMessages = new ArrayList<>();
-            for(AetherMudMessageData message : src.getCriticalAttackMessageDatas())
+            for (AetherMudMessageData message : src.getCriticalAttackMessageDatas())
                 criticalAttackMessages.add(AetherMudMessageData.copyAetherMudMessage(message));
             retVal.setCriticalAttackMessages(Collections.unmodifiableList(criticalAttackMessages));
 
             List<AetherMudMessage> battleMessages = new ArrayList<>();
-            for(AetherMudMessageData message : src.getBattleMessageDatas())
+            for (AetherMudMessageData message : src.getBattleMessageDatas())
                 battleMessages.add(AetherMudMessageData.copyAetherMudMessage(message));
             retVal.setBattleMessages(Collections.unmodifiableList(battleMessages));
 
             List<AetherMudMessage> idleMessages = new ArrayList<>();
-            for(AetherMudMessageData message : src.getIdleMessageDatas())
+            for (AetherMudMessageData message : src.getIdleMessageDatas())
                 idleMessages.add(AetherMudMessageData.copyAetherMudMessage(message));
             retVal.setIdleMessages(Collections.unmodifiableList(idleMessages));
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/PlayerData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/PlayerData.java
index 6502b8ef..774a4f79 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/PlayerData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/PlayerData.java
@@ -16,15 +16,14 @@
 package com.syncleus.aethermud.storage.graphdb.model;
 
 
-import com.syncleus.aethermud.player.*;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.syncleus.aethermud.player.*;
 import com.syncleus.aethermud.storage.graphdb.DataUtils;
-import com.syncleus.ferma.ClassInitializer;
-import com.syncleus.ferma.DefaultClassInitializer;
+import com.syncleus.ferma.*;
 import com.syncleus.ferma.annotations.Adjacency;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 
@@ -32,7 +31,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.util.*;
 
 @GraphElement
-public abstract class PlayerData extends AbstractInterceptingVertexFrame {
+public abstract class PlayerData extends AbstractVertexFrame {
 
     static final ClassInitializer<PlayerData> DEFAULT_INITIALIZER = new DefaultClassInitializer(PlayerData.class);
 
@@ -84,14 +83,14 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
     public Set<PlayerRole> getPlayerRoles() {
         HashSet<PlayerRole> roles = new HashSet<>();
         Collection<String> rolesText = getPlayerRoleTextCollection();
-        for(final String roleText : rolesText)
+        for (final String roleText : rolesText)
             roles.add(PlayerRole.valueOf(roleText));
         return Collections.unmodifiableSet(roles);
     }
 
     public void setPlayerRoles(Collection<PlayerRole> playerRoleSet) {
         ArrayList<String> newProperty = new ArrayList<String>();
-        for(PlayerRole role : playerRoleSet) {
+        for (PlayerRole role : playerRoleSet) {
             newProperty.add(role.toString());
         }
         this.setProperty("roleSet", newProperty);
@@ -121,11 +120,45 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
     @Property("learnedSpells")
     public abstract void setLearnedSpells(List<String> learnedSpells);
 
-    @Property("npcKillLog")
-    public abstract Map<String, Long> getNpcKillLog();
+    public Map<String, Long> getNpcKillLog() {
+        final List<? extends TEdge> killEdges = this.traverse((v) -> v.outE().hasLabel("npcKillLog")).toList(TEdge.class);
+        if( killEdges == null || killEdges.isEmpty())
+            return Collections.emptyMap();
+
+        final Map<String, Long> killLog = new HashMap<>(killEdges.size());
+        for(TEdge killEdge : killEdges) {
+            final Long count = killEdge.getProperty("count");
+            final String name = killEdge.traverse((e) -> this.getGraph().getTypeResolver().hasNotType(e.inV(), NpcData.class)).next(NpcData.class).getProperty("name");
+            killLog.put(name, count);
+        }
+        return Collections.unmodifiableMap(killLog);
+    }
 
-    @Property("npcKillLog")
-    public abstract void setNpcKillLog(Map<String, Long> killLog);
+    public void setNpcKillLog(Map<String, Long> killLog) {
+        final Map<String, Long> existingLog = this.getNpcKillLog();
+
+        final Set<String> toDeleteNames = Sets.newHashSet(existingLog.keySet());
+        toDeleteNames.removeAll(killLog.keySet());
+        final Map<String, Long> toAdd = Maps.newHashMap(killLog);
+        toAdd.keySet().removeAll(existingLog.keySet());
+
+        final List<? extends TEdge> killEdges = this.traverse((v) -> v.outE().hasLabel("npcKillLog")).toList(TEdge.class);
+        for(TEdge killEdge : killEdges) {
+            final String name = killEdge.traverse((e) -> this.getGraph().getTypeResolver().hasNotType(e.inV(), NpcData.class)).next(NpcData.class).getProperty("name");
+            if(toDeleteNames.contains(name))
+                killEdge.remove();
+            else
+                killEdge.setProperty("count", killLog.get(name));
+        }
+
+        for(Map.Entry<String, Long> addEntry : toAdd.entrySet()) {
+            final String name = addEntry.getKey();
+            final Long count = addEntry.getValue();
+            final NpcData dest = this.getGraph().traverse((g) -> this.getGraph().getTypeResolver().hasNotType(g.V().has("name", name), NpcData.class)).next(NpcData.class);
+            final TEdge addedEdge = this.getGraph().addFramedEdge(this, dest, "npcKillLog");
+            addedEdge.setProperty("npcKillLog", count);
+        }
+    }
 
     @Property("playerClass")
     public abstract PlayerClass getPlayerClass();
@@ -155,12 +188,12 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
     public void setEffects(Set<EffectData> effects) {
         this.resetEffects();
 
-        if( effects == null || effects.size() == 0 ) {
+        if (effects == null || effects.size() == 0) {
             return;
         }
 
-        for( EffectData effect : effects ) {
-                this.addEffect(effect);
+        for (EffectData effect : effects) {
+            this.addEffect(effect);
         }
     }
 
@@ -170,10 +203,10 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
         return effect;
     }
 
-    public void resetEffects(){
+    public void resetEffects() {
         Iterator<? extends EffectData> existingAll = this.getEffects(EffectData.class);
-        if( existingAll != null ) {
-            while( existingAll.hasNext() ) {
+        if (existingAll != null) {
+            while (existingAll.hasNext()) {
                 EffectData existing = existingAll.next();
                 this.removeEffect(existing);
                 existing.remove();
@@ -192,12 +225,12 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
 
     public void setCoolDowns(Map<CoolDownType, CoolDownData> coolDowns) {
         Iterator<? extends CoolDownData> existingCoolDowns = getCoolDowns(CoolDownData.class);
-        while(existingCoolDowns.hasNext()) {
+        while (existingCoolDowns.hasNext()) {
             CoolDownData existingCoolDown = existingCoolDowns.next();
             this.removeCoolDown(existingCoolDown);
         }
 
-        for(CoolDownData coolDown : coolDowns.values()) {
+        for (CoolDownData coolDown : coolDowns.values()) {
             this.addCoolDown(coolDown);
         }
     }
@@ -208,9 +241,9 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
 
     public CoolDownData createCoolDown(CoolDownType type) {
         Iterator<? extends CoolDownData> coolDowns = getCoolDowns(CoolDownData.class);
-        while(coolDowns.hasNext()) {
+        while (coolDowns.hasNext()) {
             CoolDownData coolDown = coolDowns.next();
-            if(coolDown.getCoolDownType().equals(type)) {
+            if (coolDown.getCoolDownType().equals(type)) {
                 coolDown.remove();
             }
         }
@@ -226,9 +259,9 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
 
     public CoolDownData createCoolDown(CoolDown coolDownSource) {
         Iterator<? extends CoolDownData> coolDowns = getCoolDowns(CoolDownData.class);
-        while(coolDowns.hasNext()) {
+        while (coolDowns.hasNext()) {
             CoolDownData coolDown = coolDowns.next();
-            if(coolDown.getCoolDownType().equals(coolDownSource.getCoolDownType())) {
+            if (coolDown.getCoolDownType().equals(coolDownSource.getCoolDownType())) {
                 coolDown.remove();
             }
         }
@@ -252,7 +285,7 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
 
     public StatData getStatData() {
         Iterator<? extends StatData> allStats = this.getStatDataIterator(StatData.class);
-        if( allStats.hasNext() )
+        if (allStats.hasNext())
             return allStats.next();
         else
             return null;
@@ -265,11 +298,11 @@ public abstract class PlayerData extends AbstractInterceptingVertexFrame {
     public abstract void removeStatData(StatData stats);
 
     public void setStats(StatData stats) {
-        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getStatDataIterator(StatData.class), statsData -> this.addStatData(statsData), () -> createStatData() );
+        DataUtils.setAllElements(Collections.singletonList(stats), () -> this.getStatDataIterator(StatData.class), statsData -> this.addStatData(statsData), () -> createStatData());
     }
 
     public StatData createStatData() {
-        if( this.getStatData() != null )
+        if (this.getStatData() != null)
             throw new IllegalStateException("Already has stats, can't create another");
         final StatData stats = this.getGraph().addFramedVertex(StatData.class);
         stats.setAgile(0);
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
index 8a3fda23..2135449e 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/SpawnRuleData.java
@@ -15,19 +15,17 @@
  */
 package com.syncleus.aethermud.storage.graphdb.model;
 
-import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.spawner.SpawnRule;
 import com.syncleus.aethermud.world.model.Area;
 import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 
 import java.lang.reflect.InvocationTargetException;
 
 @GraphElement
-public abstract class SpawnRuleData extends AbstractInterceptingVertexFrame {
+public abstract class SpawnRuleData extends AbstractVertexFrame {
     @Property("randomChance")
     public abstract int getRandomChance();
 
@@ -69,7 +67,8 @@ public abstract class SpawnRuleData extends AbstractInterceptingVertexFrame {
     public static SpawnRule copySpawnRule(SpawnRuleData src) {
         SpawnRule retVal = new SpawnRule();
         try {
-            PropertyUtils.copyProperties(retVal, src);;
+            PropertyUtils.copyProperties(retVal, src);
+            ;
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
             throw new IllegalStateException("Could not copy properties", e);
         }
diff --git a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatData.java b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatData.java
index 4970f5fb..5db225f6 100644
--- a/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatData.java
+++ b/src/main/java/com/syncleus/aethermud/storage/graphdb/model/StatData.java
@@ -16,9 +16,9 @@
 package com.syncleus.aethermud.storage.graphdb.model;
 
 import com.syncleus.aethermud.stats.Stats;
+import com.syncleus.ferma.AbstractVertexFrame;
 import com.syncleus.ferma.annotations.GraphElement;
 import com.syncleus.ferma.annotations.Property;
-import com.syncleus.ferma.ext.AbstractInterceptingVertexFrame;
 import org.apache.commons.beanutils.PropertyUtils;
 
 import java.lang.reflect.InvocationTargetException;
@@ -26,7 +26,7 @@ import java.lang.reflect.InvocationTargetException;
 import static java.lang.StrictMath.sqrt;
 
 @GraphElement
-public abstract class StatData extends AbstractInterceptingVertexFrame {
+public abstract class StatData extends AbstractVertexFrame {
     @Property("intelligence")
     public abstract Integer getIntelligence();
 
diff --git a/src/main/resources/titan-cassandra-es.properties b/src/main/resources/titan-cassandra-es.properties
new file mode 100644
index 00000000..fb148d3f
--- /dev/null
+++ b/src/main/resources/titan-cassandra-es.properties
@@ -0,0 +1,10 @@
+storage.backend=cassandrathrift
+storage.hostname=10.10.10.10
+cache.db-cache = false
+#cache.db-cache-clean-wait = 20
+#cache.db-cache-time = 1000
+#cache.db-cache-size = 0.25
+
+index.search.backend=elasticsearch
+index.search.hostname=10.10.10.10
+index.search.elasticsearch.client-only=true
diff --git a/src/test/java/com/syncleus/aethermud/common/ColorizedTextTemplateTest.java b/src/test/java/com/syncleus/aethermud/common/ColorizedTextTemplateTest.java
index 5ec476f1..4f84bdc3 100644
--- a/src/test/java/com/syncleus/aethermud/common/ColorizedTextTemplateTest.java
+++ b/src/test/java/com/syncleus/aethermud/common/ColorizedTextTemplateTest.java
@@ -16,21 +16,13 @@
 package com.syncleus.aethermud.common;
 
 import com.google.common.collect.Lists;
-import com.syncleus.aethermud.Main;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 import com.syncleus.aethermud.items.Loot;
 import com.syncleus.aethermud.storage.AetherMudStorage;
-import com.syncleus.aethermud.storage.graphdb.GraphDbAetherMudStorage;
 import com.syncleus.aethermud.storage.graphdb.GraphStorageFactory;
 import com.syncleus.aethermud.storage.graphdb.model.NpcData;
-import com.google.common.collect.Maps;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.syncleus.ferma.DelegatingFramedGraph;
-import com.syncleus.ferma.WrappedFramedGraph;
-import com.syncleus.ferma.ext.orientdb.impl.OrientTransactionFactoryImpl;
-import org.apache.tinkerpop.gremlin.orientdb.OrientGraphFactory;
-import org.apache.tinkerpop.gremlin.structure.Graph;
-import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
 import org.junit.Assert;
 import org.junit.Test;
 
diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile
new file mode 100644
index 00000000..4370233f
--- /dev/null
+++ b/vagrant/Vagrantfile
@@ -0,0 +1,16 @@
+VAGRANTFILE_API_VERSION = "2"
+
+VM_IP = "10.10.10.10"
+CASS_VER = "21x"
+ES_VER = "1.5.2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+  config.vm.box = "ubuntu/trusty64"
+  config.vm.provision :shell, path: "bootstrap.sh", args: [VM_IP, CASS_VER, ES_VER]
+  config.vm.network "private_network", ip: VM_IP
+
+  config.vm.provider "virtualbox" do |vb|
+	    vb.memory = 2048
+		  vb.cpus = 2
+  end
+ end
diff --git a/vagrant/bootstrap.sh b/vagrant/bootstrap.sh
new file mode 100755
index 00000000..ab1ef067
--- /dev/null
+++ b/vagrant/bootstrap.sh
@@ -0,0 +1,111 @@
+#!/usr/bin/env bash
+
+export HOST_IP=$1
+export CASS_VER=$2
+export ES_VER=$3
+
+
+#--------------------
+# Misc 
+#--------------------
+sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A278B781FE4B2BDA
+sudo add-apt-repository -y ppa:openjdk-r/ppa
+sudo apt-get update
+sudo apt-get install unzip curl openjdk-8-jdk -y --allow-unauthenticated --force-yes
+
+#--------------------
+# Titan
+#-------------------
+# Note that this is really only added to so you can use the gremlin shell.
+
+if [ ! -e titan* ]; then
+  echo "Installing Titan"
+  wget https://s3.amazonaws.com/s3.thinkaurelius.com/downloads/titan/titan-1.0.0-hadoop2.zip > /dev/null 2>&1
+  unzip titan-*.zip
+
+	# Clear existing conf file
+	echo "" > titan-*/conf/titan-cassandra-es.properties 
+	# Set conf file
+  sudo tee -a titan-*/conf/titan-cassandra-es.properties <<_EOF_
+storage.backend=cassandrathrift
+storage.hostname=$HOST_IP
+cache.db-cache = false
+
+index.search.backend=elasticsearch
+index.search.hostname=$HOST_IP
+index.search.elasticsearch.client-only=true
+_EOF_
+
+fi
+
+
+#--------------------
+# Elasticsearch
+#--------------------
+if [ ! -d /etc/elasticsearch ]; then
+  echo "Installing Elasticsearch"
+  wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-$ES_VER.deb > /dev/null 2>&1
+  sudo dpkg -i elasticsearch-*.deb
+
+  # set listen host
+  echo "setting elasticsearch host to $HOST_IP"
+  #sudo rm /etc/elasticsearch/elasticsearch.yml
+  echo "network.host: $HOST_IP" | sudo tee /etc/elasticsearch/elasticsearch.yml > /dev/null
+
+  # Install the head plugin
+  sudo JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64" /usr/share/elasticsearch/bin/plugin -install mobz/elasticsearch-head
+fi
+
+sudo service elasticsearch restart
+
+#--------------------
+# Cassandra 
+#--------------------
+if [ ! -d /etc/cassandra ]; then
+  echo "Installing Cassandra"
+  sudo tee /etc/apt/sources.list.d/cassandra.sources.list <<_EOF_
+deb http://www.apache.org/dist/cassandra/debian $CASS_VER main
+deb-src http://www.apache.org/dist/cassandra/debian $CASS_VER main
+_EOF_
+ 
+  # add apache cassandra keys
+  gpg --keyserver keyserver.ubuntu.com --recv-keys 4BD736A82B5C1B00
+  gpg --export --armor 4BD736A82B5C1B00 | sudo apt-key add -
+  gpg --keyserver keyserver.ubuntu.com --recv-keys F758CE318D77295D
+  gpg --export --armor F758CE318D77295D | sudo apt-key add -
+#  gpg --keyserver pgp.mit.edu --recv-keys 749D6EEC0353B12C
+#  gpg --export --armor 749D6EEC0353B12C | sudo apt-key add -
+  sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 0353B12C
+  sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 749D6EEC0353B12C
+  
+  # update repository
+  sudo apt-get update
+ 
+  # install
+  sudo apt-get install -y --allow-unauthenticated --force-yes cassandra
+ 
+  # set listen host
+  sudo sed --in-place "s/localhost/$HOST_IP/g" /etc/cassandra/cassandra.yaml
+  sudo sed --in-place "s/127.0.0.1/$HOST_IP/g" /etc/cassandra/cassandra.yaml
+
+  # More sane default configurations - the default 50% free memory allocation is too much for me
+  sudo tee -a  /etc/default/cassandra <<"_EOF_"
+ 
+# Set the Max Memory used by Cassandra, change to whatever value you like
+MAX_HEAP_SIZE="700M"
+HEAP_NEWSIZE="200M"
+ 
+# Performance Tuning
+# see also : http://wiki.apache.org/cassandra/PerformanceTuning
+JVM_OPTS="$JVM_OPTS -XX:+UseThreadPriorities" # To lower compaction priority
+JVM_OPTS="$JVM_OPTS -XX:ThreadPriorityPolicy=42" # To lower compaction priority
+JVM_OPTS="$JVM_OPTS -Dcassandra.compaction.priority=1" # To lower compaction priority
+JVM_OPTS="$JVM_OPTS -XX:+UseCompressedOops" # enables compressed references, reducing memory overhead on 64bit JVMs
+ 
+_EOF_
+
+fi
+ 
+# restart Cassandra
+sudo service cassandra restart
+
-- 
GitLab