// This makes a basic world with 5 objects: // God (#1) / // Templates (#2) /templates // PlayerTemplate (#3) /templates/player // Player (#4) // Test (#5) void createBasicDatabase() { var mdb = new MemoryDatabase(); var cdb = new CoreDatabase( mdb ); _wdb = new WorldDatabase( cdb ); _wdb.setConfigInt( World.ConfigNextId, 6 ); // Create a new checkpoint. using( var token = cdb.token() ) { DBCheckpoint cp = new DBCheckpoint() { name = "initial", time = DateTimeOffset.UtcNow }; cdb.insert( token, cp ); } // This will let our stubs function in a minimal way for saving. _stubworld = new StubWorld(); // Create a god object. _god = makeMob( 1, 0, 0, "God", "The god object!" ); // Create a template room. _templates = makeMob( 2, 1, 1, "Templates", "Template room", (m) => { m.pathId = "templates"; } ); // Create a player template. _playerTemplate = makeMob( 3, 1, 2, "PlayerTemplate", "Playter template", (m) => { m.pathId = "player"; } ); // Create a player. _player = makeMob( 4, 3, 1, "Player", "Test player" ); _testObj = makeMob( 5, 1, 1, "Test", "Test object" ); }
/// <summary> /// Creates a new checkpoint. After the checkpoint, all modifications will operate /// on new copies of the objects in questions. (Checkpoints are independent.) /// </summary> public void checkpoint( string checkpointName ) { lock( _lock ) using( var token = _db.token() ) using( var trans = _db.transaction( token ) ) { // Find the ID of the existing newest one. ulong oldCpId = getLatestCheckpoint( token ); // Create a new checkpoint. DBCheckpoint cp = new DBCheckpoint() { name = checkpointName, time = DateTimeOffset.UtcNow }; _db.insert( token, cp ); // Duplicate the mob table of the previous checkpoint. IEnumerable<DBMobTable> oldmts = _db.select( token, new DBMobTable() { checkpoint = oldCpId }, new string[] { "checkpoint" } ); foreach( DBMobTable mt in oldmts ) _db.insert( token, new DBMobTable() { checkpoint = cp.id, mob = mt.mob, objectId = mt.objectId } ); // Duplicate the config table as well. We don't try to COW these. IEnumerable<DBConfig> oldcfgs = _db.select( token, new DBConfig() { checkpoint = oldCpId }, new string[] { "checkpoint" } ); foreach( DBConfig cfg in oldcfgs ) _db.insert( token, new DBConfig() { checkpoint = cp.id, name = cfg.name, intvalue = cfg.intvalue, strvalue = cfg.strvalue } ); trans.commit(); // The latest checkpoint is now the new one. _latestCheckpoint = cp.id; } }
// Returns the most recent (and current) checkpoint. ulong getLatestCheckpoint( DatabaseToken token ) { if( _latestCheckpoint == ulong.MaxValue ) { var sorted = getCheckpoints( token ).OrderByDescending( c => c.time ); if( !sorted.Any() ) { // If there isn't an existing one, go ahead and make the first one -- empty database. DBCheckpoint dbcheckpoint = new DBCheckpoint() { name = "initial", time = DateTimeOffset.UtcNow }; _db.insert( token, dbcheckpoint ); _latestCheckpoint = dbcheckpoint.id; } else _latestCheckpoint = sorted.First().id; } return _latestCheckpoint; }
static void Import( Info info ) { Console.WriteLine( "Loading exported world database..." ); string baseDir = info.xmlDir; string binDir = Path.Combine( baseDir, "bin" ); string textDir = Path.Combine( baseDir, "text" ); string verbDir = Path.Combine( baseDir, "verb" ); string screenDir = Path.Combine( baseDir, "screen" ); XmlClimoo root = XmlPersistence.Load<XmlClimoo>( Path.Combine( baseDir, "mobs.xml" ) ); // Divine the next id from the existing ones. int nextId = -1; foreach( XmlMob m in root.mobs ) if( m.id > nextId ) nextId = m.id; ++nextId; Console.WriteLine( "Instantiating world objects..." ); var token = info.coredb.token(); // ?FIXME? This maybe should go through WorldDatabase too, but it gets unnecessarily // into the messy guts of World and Mob to do it that way. // Create a checkpoint. This will "overwrite" anything in the database // previously, but it will also allow for recovery. DBCheckpoint cp = new DBCheckpoint() { name = "Imported", time = DateTimeOffset.UtcNow }; info.coredb.insert( token, cp ); info.worlddb.setConfigInt( World.ConfigNextId, nextId ); // Load up the mobs. foreach( XmlMob m in root.mobs ) { DBMob dbmob = new DBMob() { objectId = m.id, location = m.locationId, owner = m.ownerId, parent = m.parentId, pathId = m.pathId, pulse = m.attrs.Any( a => a.name == "pulsefreq" ) }; info.coredb.insert( token, dbmob ); DBMobTable dbmobtable = new DBMobTable() { mob = dbmob.id, objectId = m.id, checkpoint = cp.id }; info.coredb.insert( token, dbmobtable ); foreach( XmlAttr attr in m.attrs ) { // We run attributes through the serializer to give it a chance to // convert it to another type. AttributeSerialized ser = new AttributeSerialized() { mimetype = attr.mimeType, binvalue = !String.IsNullOrEmpty( attr.dataContentName ) ? File.ReadAllBytes( Path.Combine( binDir, attr.dataContentName ) ) : null, strvalue = !String.IsNullOrEmpty( attr.textContentName ) ? File.ReadAllText( Path.Combine( textDir, attr.textContentName ) ) : null }; TypedAttribute ta = TypedAttribute.FromSerialized( ser ); ser = ta.serialize(); DBAttr dbattr = new DBAttr() { mime = ser.mimetype, name = attr.name, mob = dbmob.id, text = ser.strvalue, data = ser.binvalue }; info.coredb.insert( token, dbattr ); } foreach( XmlVerb verb in m.verbs ) { DBVerb dbverb = new DBVerb() { name = verb.name, code = !String.IsNullOrEmpty( verb.codeName ) ? File.ReadAllText( Path.Combine( verbDir, verb.codeName ) ) : null, mob = dbmob.id }; info.coredb.insert( token, dbverb ); } } Console.WriteLine( "Importing web database..." ); XmlClimooWeb web = XmlPersistence.Load<XmlClimooWeb>( Path.Combine( baseDir, "web.xml" ) ); foreach (var s in web.screens) { info.coredb.insert( token, new DBScreen() { name = s.name, text = File.ReadAllText( Path.Combine( screenDir, s.textName ) ) } ); } foreach (var u in web.users) { info.coredb.insert( token, new DBUser() { login = u.login, openId = u.openId, password = u.password, @object = u.objectId, name = u.name } ); } }