static void Main( string[] args ) { if( args.Length != 2 ) { Console.WriteLine( "Please specify the filename of an ImpExporter database config file and a player login name." ); return; } // Load up the config. ImpExporterConfig cfg = XmlPersistence.Load<ImpExporterConfig>( args[0] ); // Create a world, sans realtime save and pulse. Assembly asm = Assembly.Load( cfg.DatabaseAssembly ); Type dbType = asm.GetType( cfg.DatabaseClass ); IDatabase db = (IDatabase)Activator.CreateInstance( dbType ); db.setup( cfg.ConnectionString, new TableInfo() ); var coredb = new CoreDatabase( db ); var worlddb = new WorldDatabase( coredb ); var world = CanonWorld.FromWorldDatabase( worlddb, true, true ); world.attributeUrlGenerator = ( obj, name ) => CultureFree.Format( "<attr:{0}.{1}>", obj.name, name ); // Look up the player. If they passed "anon", then we use an anonymous, pre-login player. int mobid; if( args[1] == "anon" ) { mobid = Mob.Anon.id; } else { using( var token = coredb.token() ) { var users = coredb.select( token, new DBUser() { login = args[1] }, new string[] { "login" } ); if( users.Count() != 1 ) { Console.WriteLine( "Couldn't match exactly one user with the login '{0}'", args[1] ); return; } mobid = users.First().@object; } } // Make a shadow world for us to use. var shadowWorld = new ShadowWorld( world ); Mob playerMob = mobid > 0 ? Mob.Wrap( shadowWorld.findObject( mobid ) ) : null; // Make a player object. Player player = new Player( mobid ); player.NewOutput = (o) => { Console.WriteLine( "{0}", o ); }; player.NewSound = (o) => { Console.WriteLine( "Would play sound: {0}", o ); }; player.NewErrorOutput = (o) => { Console.WriteLine( "[error] {0}", o ); }; player.world = World.Wrap( shadowWorld ); if( playerMob != null ) playerMob.player = player; while( true ) { Console.Write( "climoo> " ); string command = Console.ReadLine(); if( command == "exit" ) break; string result = MooCore.InputParser.ProcessInput( command, player ); if( !result.IsNullOrEmpty() ) Console.WriteLine( result ); shadowWorld.waitForMerge(); } // Clear out any changes and timers. shadowWorld.Dispose(); world.Dispose(); }
void createBasicWorld() { createBasicDatabase(); _cw = CanonWorld.FromWorldDatabase( _wdb, false, false ); _sw = new ShadowWorld( _cw ); _w = new World( _sw ); // We want to replace these with actual world copies now. _god = _w.findObject( 1 ); _templates = _w.findObject( 2 ); _playerTemplate = _w.findObject( 3 ); _player = _w.findObject( 4 ); _testObj = _w.findObject( 5 ); }
public void ConflictMerge() { createBasicWorld(); StringBuilder sb = new StringBuilder(); // Make a second shadow world to work with, to simluate two threads. ShadowWorld sw2 = new ShadowWorld( _cw ); World w2 = new World( sw2 ); Mob _test2 = Mob.Wrap( sw2.findObject( _testObj.id ) ); // Disable immediate updates. using( var token = _cw.getMergeToken() ) { // Do some interleaved writes. _testObj.attrSet( "testA", "Value A1" ); _test2.attrSet( "testA", "Value A2" ); _test2.attrSet( "testB", "Value B2" ); _testObj.attrSet( "testB", "Value B1" ); _testObj.verbSet( "testA", new Verb() { name = "testA", code = "//Value A1" } ); _test2.verbSet( "testA", new Verb() { name = "testA", code = "//Value A2" } ); _test2.verbSet( "testB", new Verb() { name = "testB", code = "//Value B2" } ); _testObj.verbSet( "testB", new Verb() { name = "testB", code = "//Value B1" } ); } _sw.waitForMerge(); sw2.waitForMerge(); sb.AppendLine( "Simultaneous merge:" ); sb.AppendLine( "Canon:" ); sb.AppendLine( printCanon() ); sb.AppendLine( "Shadow A:" ); sb.AppendLine( printWorld( _w ) ); sb.AppendLine( "Shadow B:" ); sb.AppendLine( printWorld( w2 ) ); // The second one will test one object getting a chance to update, and a second coming in later. _testObj.attrSet( "testC", "Value C1" ); using( var token = _cw.getMergeToken() ) { _test2.attrSet( "testC", "Value C2" ); _test2.attrSet( "testD", "Value D2" ); } _testObj.attrSet( "testD", "Value D1" ); using( var token = _cw.getMergeToken() ) { _testObj.verbSet( "testC", new Verb() { name = "testC", code = "//Value C1" } ); _test2.verbSet( "testC", new Verb() { name = "testC", code = "//Value C2" } ); } _test2.verbSet( "testD", new Verb() { name = "testD", code = "//Value D2" } ); _testObj.verbSet( "testD", new Verb() { name = "testD", code = "//Value D1" } ); _sw.waitForMerge(); sw2.waitForMerge(); sb.AppendLine( "Interleaved merge:" ); sb.AppendLine( "Canon:" ); sb.AppendLine( printCanon() ); sb.AppendLine( "Shadow A:" ); sb.AppendLine( printWorld( _w ) ); sb.AppendLine( "Shadow B:" ); sb.AppendLine( printWorld( w2 ) ); string results = sb.ToString(); TestCommon.CompareRef( Path.Combine( "WorldTest", "ConflictMerge" ), results ); }
public void DeleteMerge() { createBasicWorld(); StringBuilder sb = new StringBuilder(); // Make a second shadow world to work with, to simluate two threads. ShadowWorld sw2 = new ShadowWorld( _cw ); World w2 = new World( sw2 ); Mob _test2 = Mob.Wrap( sw2.findObject( _testObj.id ) ); // Insert some values and make sure they show up for everyone. _testObj.attrSet( "test1", "Test 1" ); _testObj.attrSet( "test2", "Test 1" ); sb.AppendLine( "Pre-test:" ); sb.AppendLine( "Canon:" ); sb.AppendLine( printCanon() ); sb.AppendLine( "Shadow A:" ); sb.AppendLine( printWorld( _w ) ); sb.AppendLine( "Shadow B:" ); sb.AppendLine( printWorld( w2 ) ); // Now make some delete changes under conflict. using( var token = _cw.getMergeToken() ) { _testObj.attrSet( "test1", "Test 2" ); _test2.attrDel( "test1" ); _test2.attrDel( "test2" ); _testObj.attrSet( "test2", "Test 2" ); } _sw.waitForMerge(); sw2.waitForMerge(); sb.AppendLine( "Post-test:" ); sb.AppendLine( "Canon:" ); sb.AppendLine( printCanon() ); sb.AppendLine( "Shadow A:" ); sb.AppendLine( printWorld( _w ) ); sb.AppendLine( "Shadow B:" ); sb.AppendLine( printWorld( w2 ) ); string results = sb.ToString(); TestCommon.CompareRef( Path.Combine( "WorldTest", "DeleteMerge" ), results ); }