PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/World.cs

https://bitbucket.org/j2718c/shard
C# | 877 lines | 649 code | 206 blank | 22 comment | 113 complexity | 251de209089be04b2945c2a426c0bb5b MD5 | raw file
Possible License(s): AGPL-1.0
  1. /***************************************************************************
  2. * World.cs
  3. * -------------------
  4. * begin : May 1, 2002
  5. * copyright : (C) The UOShard.net Team
  6. * email : info@runuo.com
  7. *
  8. * $Id: World.cs 652 2010-12-28 09:24:30Z asayre $
  9. *
  10. ***************************************************************************/
  11. /***************************************************************************
  12. *
  13. * This program is free software; you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation; either version 2 of the License, or
  16. * (at your option) any later version.
  17. *
  18. ***************************************************************************/
  19. using System;
  20. using System.IO;
  21. using System.Collections;
  22. using System.Collections.Generic;
  23. using System.Reflection;
  24. using System.Threading;
  25. using System.Diagnostics;
  26. using Server;
  27. using Server.Mobiles;
  28. using Server.Accounting;
  29. using Server.Network;
  30. using Server.Guilds;
  31. namespace Server {
  32. public static class World {
  33. public enum SaveOption {
  34. Normal,
  35. Threaded
  36. }
  37. public static SaveOption SaveType = SaveOption.Normal;
  38. private static Dictionary<Serial, Mobile> m_Mobiles;
  39. private static Dictionary<Serial, Item> m_Items;
  40. private static bool m_Loading;
  41. private static bool m_Loaded;
  42. private static bool m_Saving;
  43. private static ManualResetEvent m_DiskWriteHandle = new ManualResetEvent(true);
  44. private static Queue<IEntity> _addQueue, _deleteQueue;
  45. public static bool Saving { get { return m_Saving; } }
  46. public static bool Loaded { get { return m_Loaded; } }
  47. public static bool Loading { get { return m_Loading; } }
  48. public readonly static string MobileIndexPath = Path.Combine( "Saves/Mobiles/", "Mobiles.idx" );
  49. public readonly static string MobileTypesPath = Path.Combine( "Saves/Mobiles/", "Mobiles.tdb" );
  50. public readonly static string MobileDataPath = Path.Combine( "Saves/Mobiles/", "Mobiles.bin" );
  51. public readonly static string ItemIndexPath = Path.Combine( "Saves/Items/", "Items.idx" );
  52. public readonly static string ItemTypesPath = Path.Combine( "Saves/Items/", "Items.tdb" );
  53. public readonly static string ItemDataPath = Path.Combine( "Saves/Items/", "Items.bin" );
  54. public readonly static string GuildIndexPath = Path.Combine( "Saves/Guilds/", "Guilds.idx" );
  55. public readonly static string GuildDataPath = Path.Combine( "Saves/Guilds/", "Guilds.bin" );
  56. public static void NotifyDiskWriteComplete()
  57. {
  58. if( m_DiskWriteHandle.Set())
  59. {
  60. Console.WriteLine("Closing Save Files...");
  61. }
  62. }
  63. public static void WaitForWriteCompletion()
  64. {
  65. m_DiskWriteHandle.WaitOne();
  66. }
  67. public static Dictionary<Serial, Mobile> Mobiles {
  68. get { return m_Mobiles; }
  69. }
  70. public static Dictionary<Serial, Item> Items {
  71. get { return m_Items; }
  72. }
  73. public static bool OnDelete( IEntity entity ) {
  74. if ( m_Saving || m_Loading ) {
  75. if ( m_Saving ) {
  76. AppendSafetyLog( "delete", entity );
  77. }
  78. _deleteQueue.Enqueue( entity );
  79. return false;
  80. }
  81. return true;
  82. }
  83. public static void Broadcast( int hue, bool ascii, string text ) {
  84. Packet p;
  85. if ( ascii )
  86. p = new AsciiMessage( Serial.MinusOne, -1, MessageType.Regular, hue, 3, "System", text );
  87. else
  88. p = new UnicodeMessage( Serial.MinusOne, -1, MessageType.Regular, hue, 3, "ENU", "System", text );
  89. List<NetState> list = NetState.Instances;
  90. p.Acquire();
  91. for ( int i = 0; i < list.Count; ++i ) {
  92. if ( list[i].Mobile != null )
  93. list[i].Send( p );
  94. }
  95. p.Release();
  96. NetState.FlushAll();
  97. }
  98. public static void Broadcast( int hue, bool ascii, string format, params object[] args ) {
  99. Broadcast( hue, ascii, String.Format( format, args ) );
  100. }
  101. private interface IEntityEntry {
  102. Serial Serial { get; }
  103. int TypeID { get; }
  104. long Position { get; }
  105. int Length { get; }
  106. }
  107. private sealed class GuildEntry : IEntityEntry {
  108. private BaseGuild m_Guild;
  109. private long m_Position;
  110. private int m_Length;
  111. public BaseGuild Guild {
  112. get {
  113. return m_Guild;
  114. }
  115. }
  116. public Serial Serial {
  117. get {
  118. return m_Guild == null ? 0 : m_Guild.Id;
  119. }
  120. }
  121. public int TypeID {
  122. get {
  123. return 0;
  124. }
  125. }
  126. public long Position {
  127. get {
  128. return m_Position;
  129. }
  130. }
  131. public int Length {
  132. get {
  133. return m_Length;
  134. }
  135. }
  136. public GuildEntry( BaseGuild g, long pos, int length ) {
  137. m_Guild = g;
  138. m_Position = pos;
  139. m_Length = length;
  140. }
  141. }
  142. private sealed class ItemEntry : IEntityEntry {
  143. private Item m_Item;
  144. private int m_TypeID;
  145. private string m_TypeName;
  146. private long m_Position;
  147. private int m_Length;
  148. public Item Item {
  149. get {
  150. return m_Item;
  151. }
  152. }
  153. public Serial Serial {
  154. get {
  155. return m_Item == null ? Serial.MinusOne : m_Item.Serial;
  156. }
  157. }
  158. public int TypeID {
  159. get {
  160. return m_TypeID;
  161. }
  162. }
  163. public string TypeName {
  164. get {
  165. return m_TypeName;
  166. }
  167. }
  168. public long Position {
  169. get {
  170. return m_Position;
  171. }
  172. }
  173. public int Length {
  174. get {
  175. return m_Length;
  176. }
  177. }
  178. public ItemEntry( Item item, int typeID, string typeName, long pos, int length ) {
  179. m_Item = item;
  180. m_TypeID = typeID;
  181. m_TypeName = typeName;
  182. m_Position = pos;
  183. m_Length = length;
  184. }
  185. }
  186. private sealed class MobileEntry : IEntityEntry {
  187. private Mobile m_Mobile;
  188. private int m_TypeID;
  189. private string m_TypeName;
  190. private long m_Position;
  191. private int m_Length;
  192. public Mobile Mobile {
  193. get {
  194. return m_Mobile;
  195. }
  196. }
  197. public Serial Serial {
  198. get {
  199. return m_Mobile == null ? Serial.MinusOne : m_Mobile.Serial;
  200. }
  201. }
  202. public int TypeID {
  203. get {
  204. return m_TypeID;
  205. }
  206. }
  207. public string TypeName {
  208. get {
  209. return m_TypeName;
  210. }
  211. }
  212. public long Position {
  213. get {
  214. return m_Position;
  215. }
  216. }
  217. public int Length {
  218. get {
  219. return m_Length;
  220. }
  221. }
  222. public MobileEntry( Mobile mobile, int typeID, string typeName, long pos, int length ) {
  223. m_Mobile = mobile;
  224. m_TypeID = typeID;
  225. m_TypeName = typeName;
  226. m_Position = pos;
  227. m_Length = length;
  228. }
  229. }
  230. private static string m_LoadingType;
  231. public static string LoadingType {
  232. get { return m_LoadingType; }
  233. }
  234. private static readonly Type[] m_SerialTypeArray = new Type[1] { typeof(Serial) };
  235. //TODO, when fully migrated to .NET 4.0:
  236. //private static List<Tuple<ConstructorInfo, string>> ReadTypes( BinaryReader tdbReader )
  237. private static List<object[]> ReadTypes( BinaryReader tdbReader )
  238. {
  239. int count = tdbReader.ReadInt32();
  240. List<object[]> types = new List<object[]>(count);
  241. for (int i = 0; i < count; ++i)
  242. {
  243. string typeName = tdbReader.ReadString();
  244. Type t = ScriptCompiler.FindTypeByFullName(typeName);
  245. if (t == null)
  246. {
  247. Console.WriteLine("failed");
  248. if (!Core.Service)
  249. {
  250. Console.WriteLine("Error: Type '{0}' was not found. Delete all of those types? (y/n)", typeName);
  251. if (Console.ReadKey(true).Key == ConsoleKey.Y)
  252. {
  253. types.Add(null);
  254. Console.Write("World: Loading...");
  255. continue;
  256. }
  257. Console.WriteLine("Types will not be deleted. An exception will be thrown.");
  258. }
  259. else
  260. {
  261. Console.WriteLine("Error: Type '{0}' was not found.", typeName);
  262. }
  263. throw new Exception(String.Format("Bad type '{0}'", typeName));
  264. }
  265. ConstructorInfo ctor = t.GetConstructor(m_SerialTypeArray);
  266. if (ctor != null)
  267. {
  268. types.Add(new object[] { ctor, typeName });
  269. }
  270. else
  271. {
  272. throw new Exception(String.Format("Type '{0}' does not have a serialization constructor", t));
  273. }
  274. }
  275. return types;
  276. }
  277. public static void Load() {
  278. if ( m_Loaded )
  279. return;
  280. m_Loaded = true;
  281. m_LoadingType = null;
  282. Console.Write( "World: Loading..." );
  283. Stopwatch watch = Stopwatch.StartNew();
  284. m_Loading = true;
  285. _addQueue = new Queue<IEntity>();
  286. _deleteQueue = new Queue<IEntity>();
  287. int mobileCount = 0, itemCount = 0, guildCount = 0;
  288. object[] ctorArgs = new object[1];
  289. List<ItemEntry> items = new List<ItemEntry>();
  290. List<MobileEntry> mobiles = new List<MobileEntry>();
  291. List<GuildEntry> guilds = new List<GuildEntry>();
  292. if ( File.Exists( MobileIndexPath ) && File.Exists( MobileTypesPath ) ) {
  293. using ( FileStream idx = new FileStream( MobileIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
  294. BinaryReader idxReader = new BinaryReader( idx );
  295. using ( FileStream tdb = new FileStream( MobileTypesPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
  296. BinaryReader tdbReader = new BinaryReader( tdb );
  297. List<object[]> types = ReadTypes( tdbReader );
  298. mobileCount = idxReader.ReadInt32();
  299. m_Mobiles = new Dictionary<Serial, Mobile>( mobileCount );
  300. for ( int i = 0; i < mobileCount; ++i ) {
  301. int typeID = idxReader.ReadInt32();
  302. int serial = idxReader.ReadInt32();
  303. long pos = idxReader.ReadInt64();
  304. int length = idxReader.ReadInt32();
  305. object[] objs = types[typeID];
  306. if ( objs == null )
  307. continue;
  308. Mobile m = null;
  309. ConstructorInfo ctor = ( ConstructorInfo ) objs[0];
  310. string typeName = ( string ) objs[1];
  311. try {
  312. ctorArgs[0] = ( Serial ) serial;
  313. m = ( Mobile ) ( ctor.Invoke( ctorArgs ) );
  314. } catch {
  315. }
  316. if ( m != null ) {
  317. mobiles.Add( new MobileEntry( m, typeID, typeName, pos, length ) );
  318. AddMobile( m );
  319. }
  320. }
  321. tdbReader.Close();
  322. }
  323. idxReader.Close();
  324. }
  325. } else {
  326. m_Mobiles = new Dictionary<Serial, Mobile>();
  327. }
  328. if ( File.Exists( ItemIndexPath ) && File.Exists( ItemTypesPath ) ) {
  329. using ( FileStream idx = new FileStream( ItemIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
  330. BinaryReader idxReader = new BinaryReader( idx );
  331. using ( FileStream tdb = new FileStream( ItemTypesPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
  332. BinaryReader tdbReader = new BinaryReader( tdb );
  333. List<object[]> types = ReadTypes( tdbReader );
  334. itemCount = idxReader.ReadInt32();
  335. m_Items = new Dictionary<Serial, Item>( itemCount );
  336. for ( int i = 0; i < itemCount; ++i ) {
  337. int typeID = idxReader.ReadInt32();
  338. int serial = idxReader.ReadInt32();
  339. long pos = idxReader.ReadInt64();
  340. int length = idxReader.ReadInt32();
  341. object[] objs = types[typeID];
  342. if ( objs == null )
  343. continue;
  344. Item item = null;
  345. ConstructorInfo ctor = ( ConstructorInfo ) objs[0];
  346. string typeName = ( string ) objs[1];
  347. try {
  348. ctorArgs[0] = ( Serial ) serial;
  349. item = ( Item ) ( ctor.Invoke( ctorArgs ) );
  350. } catch {
  351. }
  352. if ( item != null ) {
  353. items.Add( new ItemEntry( item, typeID, typeName, pos, length ) );
  354. AddItem( item );
  355. }
  356. }
  357. tdbReader.Close();
  358. }
  359. idxReader.Close();
  360. }
  361. } else {
  362. m_Items = new Dictionary<Serial, Item>();
  363. }
  364. if ( File.Exists( GuildIndexPath ) ) {
  365. using ( FileStream idx = new FileStream( GuildIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
  366. BinaryReader idxReader = new BinaryReader( idx );
  367. guildCount = idxReader.ReadInt32();
  368. CreateGuildEventArgs createEventArgs = new CreateGuildEventArgs( -1 );
  369. for ( int i = 0; i < guildCount; ++i ) {
  370. idxReader.ReadInt32();//no typeid for guilds
  371. int id = idxReader.ReadInt32();
  372. long pos = idxReader.ReadInt64();
  373. int length = idxReader.ReadInt32();
  374. createEventArgs.Id = id;
  375. BaseGuild guild = EventSink.InvokeCreateGuild( createEventArgs );
  376. if ( guild != null )
  377. guilds.Add( new GuildEntry( guild, pos, length ) );
  378. }
  379. idxReader.Close();
  380. }
  381. }
  382. bool failedMobiles = false, failedItems = false, failedGuilds = false;
  383. Type failedType = null;
  384. Serial failedSerial = Serial.Zero;
  385. Exception failed = null;
  386. int failedTypeID = 0;
  387. if ( File.Exists( MobileDataPath ) ) {
  388. using ( FileStream bin = new FileStream( MobileDataPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
  389. BinaryFileReader reader = new BinaryFileReader( new BinaryReader( bin ) );
  390. for ( int i = 0; i < mobiles.Count; ++i ) {
  391. MobileEntry entry = mobiles[i];
  392. Mobile m = entry.Mobile;
  393. if ( m != null ) {
  394. reader.Seek( entry.Position, SeekOrigin.Begin );
  395. try {
  396. m_LoadingType = entry.TypeName;
  397. m.Deserialize( reader );
  398. if ( reader.Position != ( entry.Position + entry.Length ) )
  399. throw new Exception( String.Format( "***** Bad serialize on {0} *****", m.GetType() ) );
  400. } catch ( Exception e ) {
  401. mobiles.RemoveAt( i );
  402. failed = e;
  403. failedMobiles = true;
  404. failedType = m.GetType();
  405. failedTypeID = entry.TypeID;
  406. failedSerial = m.Serial;
  407. break;
  408. }
  409. }
  410. }
  411. reader.Close();
  412. }
  413. }
  414. if ( !failedMobiles && File.Exists( ItemDataPath ) ) {
  415. using ( FileStream bin = new FileStream( ItemDataPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
  416. BinaryFileReader reader = new BinaryFileReader( new BinaryReader( bin ) );
  417. for ( int i = 0; i < items.Count; ++i ) {
  418. ItemEntry entry = items[i];
  419. Item item = entry.Item;
  420. if ( item != null ) {
  421. reader.Seek( entry.Position, SeekOrigin.Begin );
  422. try {
  423. m_LoadingType = entry.TypeName;
  424. item.Deserialize( reader );
  425. if ( reader.Position != ( entry.Position + entry.Length ) )
  426. throw new Exception( String.Format( "***** Bad serialize on {0} *****", item.GetType() ) );
  427. } catch ( Exception e ) {
  428. items.RemoveAt( i );
  429. failed = e;
  430. failedItems = true;
  431. failedType = item.GetType();
  432. failedTypeID = entry.TypeID;
  433. failedSerial = item.Serial;
  434. break;
  435. }
  436. }
  437. }
  438. reader.Close();
  439. }
  440. }
  441. m_LoadingType = null;
  442. if ( !failedMobiles && !failedItems && File.Exists( GuildDataPath ) ) {
  443. using ( FileStream bin = new FileStream( GuildDataPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
  444. BinaryFileReader reader = new BinaryFileReader( new BinaryReader( bin ) );
  445. for ( int i = 0; i < guilds.Count; ++i ) {
  446. GuildEntry entry = guilds[i];
  447. BaseGuild g = entry.Guild;
  448. if ( g != null ) {
  449. reader.Seek( entry.Position, SeekOrigin.Begin );
  450. try {
  451. g.Deserialize( reader );
  452. if ( reader.Position != ( entry.Position + entry.Length ) )
  453. throw new Exception( String.Format( "***** Bad serialize on Guild {0} *****", g.Id ) );
  454. } catch ( Exception e ) {
  455. guilds.RemoveAt( i );
  456. failed = e;
  457. failedGuilds = true;
  458. failedType = typeof( BaseGuild );
  459. failedTypeID = g.Id;
  460. failedSerial = g.Id;
  461. break;
  462. }
  463. }
  464. }
  465. reader.Close();
  466. }
  467. }
  468. if ( failedItems || failedMobiles || failedGuilds ) {
  469. Console.WriteLine( "An error was encountered while loading a saved object" );
  470. Console.WriteLine( " - Type: {0}", failedType );
  471. Console.WriteLine( " - Serial: {0}", failedSerial );
  472. if ( !Core.Service ) {
  473. Console.WriteLine( "Delete the object? (y/n)" );
  474. if ( Console.ReadKey( true ).Key == ConsoleKey.Y ) {
  475. if ( failedType != typeof( BaseGuild ) ) {
  476. Console.WriteLine( "Delete all objects of that type? (y/n)" );
  477. if ( Console.ReadKey( true ).Key == ConsoleKey.Y ) {
  478. if ( failedMobiles ) {
  479. for ( int i = 0; i < mobiles.Count; ) {
  480. if ( mobiles[i].TypeID == failedTypeID )
  481. mobiles.RemoveAt( i );
  482. else
  483. ++i;
  484. }
  485. } else if ( failedItems ) {
  486. for ( int i = 0; i < items.Count; ) {
  487. if ( items[i].TypeID == failedTypeID )
  488. items.RemoveAt( i );
  489. else
  490. ++i;
  491. }
  492. }
  493. }
  494. }
  495. SaveIndex<MobileEntry>( mobiles, MobileIndexPath );
  496. SaveIndex<ItemEntry>( items, ItemIndexPath );
  497. SaveIndex<GuildEntry>( guilds, GuildIndexPath );
  498. }
  499. Console.WriteLine( "After pressing return an exception will be thrown and the server will terminate." );
  500. Console.ReadLine();
  501. } else {
  502. Console.WriteLine( "An exception will be thrown and the server will terminate." );
  503. }
  504. throw new Exception( String.Format( "Load failed (items={0}, mobiles={1}, guilds={2}, type={3}, serial={4})", failedItems, failedMobiles, failedGuilds, failedType, failedSerial ), failed );
  505. }
  506. EventSink.InvokeWorldLoad();
  507. m_Loading = false;
  508. ProcessSafetyQueues();
  509. foreach ( Item item in m_Items.Values ) {
  510. if ( item.Parent == null )
  511. item.UpdateTotals();
  512. item.Network.ClearProperties();
  513. }
  514. foreach ( Mobile m in m_Mobiles.Values ) {
  515. m.UpdateRegion(); // Is this really needed?
  516. m.UpdateTotals();
  517. m.Network.ClearProperties();
  518. }
  519. watch.Stop();
  520. Console.WriteLine( "done ({1} items, {2} mobiles) ({0:F2} seconds)", watch.Elapsed.TotalSeconds, m_Items.Count, m_Mobiles.Count );
  521. }
  522. private static void ProcessSafetyQueues() {
  523. while ( _addQueue.Count > 0 ) {
  524. IEntity entity = _addQueue.Dequeue();
  525. Item item = entity as Item;
  526. if ( item != null ) {
  527. AddItem( item );
  528. } else {
  529. Mobile mob = entity as Mobile;
  530. if ( mob != null ) {
  531. AddMobile( mob );
  532. }
  533. }
  534. }
  535. while ( _deleteQueue.Count > 0 ) {
  536. IEntity entity = _deleteQueue.Dequeue();
  537. Item item = entity as Item;
  538. if ( item != null ) {
  539. item.Delete();
  540. } else {
  541. Mobile mob = entity as Mobile;
  542. if ( mob != null ) {
  543. mob.Delete();
  544. }
  545. }
  546. }
  547. }
  548. private static void AppendSafetyLog( string action, IEntity entity ) {
  549. string message = String.Format( "Warning: Attempted to {1} {2} during world save." +
  550. "{0}This action could cause inconsistent state." +
  551. "{0}It is strongly advised that the offending scripts be corrected.",
  552. Environment.NewLine,
  553. action, entity
  554. );
  555. Console.WriteLine( message );
  556. try {
  557. using ( StreamWriter op = new StreamWriter( "world-save-errors.log", true ) ) {
  558. op.WriteLine( "{0}\t{1}", DateTime.Now, message );
  559. op.WriteLine( new StackTrace( 2 ).ToString() );
  560. op.WriteLine();
  561. }
  562. } catch {
  563. }
  564. }
  565. private static void SaveIndex<T>( List<T> list, string path ) where T : IEntityEntry {
  566. if ( !Directory.Exists( "Saves/Mobiles/" ) )
  567. Directory.CreateDirectory( "Saves/Mobiles/" );
  568. if ( !Directory.Exists( "Saves/Items/" ) )
  569. Directory.CreateDirectory( "Saves/Items/" );
  570. if ( !Directory.Exists( "Saves/Guilds/" ) )
  571. Directory.CreateDirectory( "Saves/Guilds/" );
  572. using ( FileStream idx = new FileStream( path, FileMode.Create, FileAccess.Write, FileShare.None ) ) {
  573. BinaryWriter idxWriter = new BinaryWriter( idx );
  574. idxWriter.Write( list.Count );
  575. for ( int i = 0; i < list.Count; ++i ) {
  576. T e = list[i];
  577. idxWriter.Write( e.TypeID );
  578. idxWriter.Write( e.Serial );
  579. idxWriter.Write( e.Position );
  580. idxWriter.Write( e.Length );
  581. }
  582. idxWriter.Close();
  583. }
  584. }
  585. internal static int m_Saves;
  586. public static void Save() {
  587. Save( true, false );
  588. }
  589. public static void Save( bool message, bool permitBackgroundWrite ) {
  590. if ( m_Saving )
  591. return;
  592. ++m_Saves;
  593. NetState.FlushAll();
  594. NetState.Pause();
  595. World.WaitForWriteCompletion();//Blocks Save until current disk flush is done.
  596. m_Saving = true;
  597. m_DiskWriteHandle.Reset();
  598. if ( message )
  599. Broadcast( 0x35, true, "The world is saving, please wait." );
  600. SaveStrategy strategy = SaveStrategy.Acquire();
  601. Console.WriteLine( "Core: Using {0} save strategy", strategy.Name.ToLowerInvariant() );
  602. Console.Write( "World: Saving..." );
  603. Stopwatch watch = Stopwatch.StartNew();
  604. if ( !Directory.Exists( "Saves/Mobiles/" ) )
  605. Directory.CreateDirectory( "Saves/Mobiles/" );
  606. if ( !Directory.Exists( "Saves/Items/" ) )
  607. Directory.CreateDirectory( "Saves/Items/" );
  608. if ( !Directory.Exists( "Saves/Guilds/" ) )
  609. Directory.CreateDirectory( "Saves/Guilds/" );
  610. /*using ( SaveMetrics metrics = new SaveMetrics() ) {*/
  611. strategy.Save( null, permitBackgroundWrite );
  612. /*}*/
  613. try {
  614. EventSink.InvokeWorldSave( new WorldSaveEventArgs( message ) );
  615. } catch ( Exception e ) {
  616. throw new Exception( "World Save event threw an exception. Save failed!", e );
  617. }
  618. watch.Stop();
  619. m_Saving = false;
  620. if (!permitBackgroundWrite)
  621. World.NotifyDiskWriteComplete(); //Sets the DiskWriteHandle. If we allow background writes, we leave this upto the individual save strategies.
  622. ProcessSafetyQueues();
  623. strategy.ProcessDecay();
  624. Console.WriteLine( "Save done in {0:F2} seconds.", watch.Elapsed.TotalSeconds );
  625. if ( message )
  626. Broadcast( 0x35, true, "World save complete. The entire process took {0:F1} seconds.", watch.Elapsed.TotalSeconds );
  627. NetState.Resume();
  628. }
  629. internal static List<Type> m_ItemTypes = new List<Type>();
  630. internal static List<Type> m_MobileTypes = new List<Type>();
  631. public static IEntity FindEntity( Serial serial ) {
  632. if ( serial.IsItem )
  633. return FindItem( serial );
  634. else if ( serial.IsMobile )
  635. return FindMobile( serial );
  636. return null;
  637. }
  638. public static Mobile FindMobile( Serial serial ) {
  639. Mobile mob;
  640. m_Mobiles.TryGetValue( serial, out mob );
  641. return mob;
  642. }
  643. public static void AddMobile( Mobile m ) {
  644. if ( m_Saving ) {
  645. AppendSafetyLog( "add", m );
  646. _addQueue.Enqueue( m );
  647. } else {
  648. m_Mobiles[m.Serial] = m;
  649. }
  650. }
  651. public static Item FindItem( Serial serial ) {
  652. Item item;
  653. m_Items.TryGetValue( serial, out item );
  654. return item;
  655. }
  656. public static void AddItem( Item item ) {
  657. if ( m_Saving ) {
  658. AppendSafetyLog( "add", item );
  659. _addQueue.Enqueue( item );
  660. } else {
  661. m_Items[item.Serial] = item;
  662. }
  663. }
  664. public static void RemoveMobile( Mobile m ) {
  665. m_Mobiles.Remove( m.Serial );
  666. }
  667. public static void RemoveItem( Item item ) {
  668. m_Items.Remove( item.Serial );
  669. }
  670. }
  671. }