RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Item Save Option

A_Li_N

Knight
Item Save Option

This is kinda a wierd request, but could there be a bool in Item that the Save method checks before it does the item.Serialize( bin ) and changing of the SaveItems method that checks this bool?

This would allow us to make an item that does not save, but is still part of the world. I have my reasons for wanting this, (as does at least one other person) and decided I'd ask if it could be a modification of the next core.
 

Talrol

Wanderer
You could add that yourself actually....in your serialization method you could have it check the boolean value of the class the item belongs to, and if that value is set to false exits without serializing it, you'll probably have to serialize that variable to use in the deserialize method (not sure I'm about half drunk, but I know what you're wanting can be done in the individual item script).
 

A_Li_N

Knight
It actually cannot. At least, I've had no success in doing it. The World.Save method calls item.Serialize(), which serializes the item. I've tried leaving the override blank for my test item, it still serializes the base item, still counts towards the item count, still increases the save times. The only way I can see to be able to stop the serialization of an item is to stop it at the source, which means it needs a core change to add the bool value (or whatever) to the Item.cs, and the check to the World.cs
 

Greystar

Wanderer
A_Li_N said:
It actually cannot. At least, I've had no success in doing it. The World.Save method calls item.Serialize(), which serializes the item. I've tried leaving the override blank for my test item, it still serializes the base item, still counts towards the item count, still increases the save times. The only way I can see to be able to stop the serialization of an item is to stop it at the source, which means it needs a core change to add the bool value (or whatever) to the Item.cs, and the check to the World.cs

What like when its going through the items to save the world have it do a quick check for lets call it m_Save which is a settable variable and if m_Save == false it skips it in the world save? Wouldnt that still make worldsaves just as long since it would have to check a variable first before it saves to a file? Yes this variable would have to be in the core Item file or maybe an ISave or something if an Item or mobile has the ISave virtual thing added on after like ImATestMob: BaseCreature, ISave

which if it sees the ISave it means it actually doesn't save... dunno, I think I know where you're going with this, but im not sure if it would actually save on worldsave times or not.
 

Dipset

Wanderer
Code:
public override void Deserialize( GenericReader reader )
		{
			base.Deserialize( reader );
			int version = reader.ReadInt();
			base.Delete();
		}
 

A_Li_N

Knight
Dipset said:
Code:
public override void Deserialize( GenericReader reader )
		{
			base.Deserialize( reader );
			int version = reader.ReadInt();
			base.Delete();
		}
That would just delete the item when the world loaded...that's not what I'm talking about...that would have no effect on save times.


Greystar said:
What like when its going through the items to save the world have it do a quick check for lets call it m_Save which is a settable variable and if m_Save == false it skips it in the world save? Wouldnt that still make worldsaves just as long since it would have to check a variable first before it saves to a file? Yes this variable would have to be in the core Item file or maybe an ISave or something if an Item or mobile has the ISave virtual thing added on after like ImATestMob: BaseCreature, ISave

which if it sees the ISave it means it actually doesn't save... dunno, I think I know where you're going with this, but im not sure if it would actually save on worldsave times or not.
It would increase the save time becuase it would check the variable, and if it was false, it would NOT save the item, so it would NOT take the time to write all the variables of the Item class to the HDD Save files, so it would decrease the save times.
 

Phantom

Knight
Not saving an item won't effect save times.

It still would be counted as an item, and would be checked, and thus during the process would take time to do this.

There would be no difference.
 

A_Li_N

Knight
Doesn't the actual save times mostly depend on writing the information to the disc? And still, if you skip the serialization, you would be skipping quite a few commands, replacing it with 1 or 2, which would reduce them none the less.

Cancle that...think I figured out how to compile it. I'll do some tests tomorrow and get back with any results I find.
 

A_Li_N

Knight
*Update*
Here is what I have tried and the results. It doesn't load the world, but I havn't had enough time to fix that, which should happen tonight.

World.cs
Code:
		private static void SaveItems()
		{
			ArrayList decaying = new ArrayList();

			GenericWriter idx;
			GenericWriter tdb;
			GenericWriter bin;

			if ( SaveType == SaveOption.Normal )
			{
				idx = new BinaryFileWriter( itemIdxPath, false );
				tdb = new BinaryFileWriter( itemTdbPath, false );
				bin = new BinaryFileWriter( itemBinPath, true );
			} 
			else
			{
				idx = new AsyncWriter( itemIdxPath, false );
				tdb = new AsyncWriter( itemTdbPath, false );
				bin = new AsyncWriter( itemBinPath, true );
			}

			idx.Write( (int) m_Items.Count );
			foreach ( Item item in m_Items.Values )
			{
				[color=blue]if ( !item.CheckSaveItem )
					continue;[/color]
				
				if ( item.Decays && item.Parent == null && item.Map != Map.Internal && (item.LastMoved + item.DecayTime) <= DateTime.Now )
					decaying.Add( item );

				long start = bin.Position;

				idx.Write( (int) item.m_TypeRef );
				idx.Write( (int) item.Serial );
				idx.Write( (long) start );

				item.Serialize( bin );

				idx.Write( (int) (bin.Position - start) );

				item.FreeCache();
			}

			tdb.Write( (int) m_ItemTypes.Count );
			for ( int i = 0; i < m_ItemTypes.Count; ++i )
				tdb.Write( ((Type)m_ItemTypes[i]).FullName );

			idx.Close();
			tdb.Close();
			bin.Close();

			for ( int i = 0; i < decaying.Count; ++i )
			{
				Item item = (Item)decaying[i];

				if ( item.OnDecay() )
					item.Delete();
			}
		}

Item.cs
Code:
		private static bool m_CheckSave = true;
		public virtual bool CheckSaveItem{ get{ return m_CheckSave; } }


As you can probably see, it will cause a loading crash, becuase it saves the count of the m_Items hashtable, then checks the items. Then when it loads, it tries to load the whole m_Items amount, which isn't whaat was saved. It DOES however, reduce the save times. I had over 2 million 'TestItems' that had the CheckSave as false, and the server saved at 1.1 seconds every time. (Going from 0-2million in 100,000 intervals)

So I know it'll reduce the save times, I just have to do some more testing and re-aranging of things to get it to load right.
 

Phantom

Knight
A_Li_N said:
Doesn't the actual save times mostly depend on writing the information to the disc? And still, if you skip the serialization, you would be skipping quite a few commands, replacing it with 1 or 2, which would reduce them none the less.

Cancle that...think I figured out how to compile it. I'll do some tests tomorrow and get back with any results I find.

Its the amount of objects within the Hasthtable that takes the most tme, saving is nothing.
 

A_Li_N

Knight
Phantom said:
Its the amount of objects within the Hasthtable that takes the most tme, saving is nothing.
I respect your ideas Phantom, but if that were the case, then why would my tests show different?

I added as I stated before, 2 million items. These were put in the hashtable like all other items. I then saved, and it 'looped' through the m_Items.Values and checked to see if the variable CheckSaveItem was true or false. If false, it went to the next item, otherwise, it did normal save. If I put 2 million of the exact same items (minus the override CheckSaveItem) in my normal server and saved, it would take around 20 seconds to save.

Here is what I am using:
(Control)
Code:
using System;
using System.Collections;
using Server.Items;

namespace Server.FeudalSystem
{
	public class TestControl : Item
	{
		ArrayList TestArray;
		[Constructable]
		public TestControl() : base( 3804 )
		{
			TestArray = new ArrayList();
		}

		public override void OnDoubleClick( Mobile from )
		{
			if( TestArray == null )
				TestArray = new ArrayList();

			int xx = Location.X;
			int yy = Location.Y;
			int zz = Location.Z;

			for( int x = 0; x < 1000; x++ )
			{
				for( int y = 0; y < 1000; y++ )
				{
					TestTest ti = new TestTest( 147 );
					ti.MoveToWorld( new Point3D( xx+x, yy+y, zz ), Map );
					TestArray.Add( ti );
				}
			}
		}

		public TestControl( Serial serial ) : base( serial )
		{
		}

		public override void Serialize( GenericWriter writer )
		{
			writer.Write( (int) 0 ); // version
			base.Serialize( writer );

			writer.Write( TestArray.Count );
			for( int i = 0; i < TestArray.Count; i++ )
			{
				writer.Write( ((TestTest)TestArray[i]).Location );
			}
			Console.WriteLine( "Number of Items : {0}", World.Items.Count );
		}

		public override void Deserialize( GenericReader reader )
		{
			int version = reader.ReadInt();
			base.Deserialize( reader );

			int count = reader.ReadInt();
			for( int i = 0; i < count; i++ )
			{
				TestTest tt = new TestTest( 147 );
				tt.MoveToWorld( reader.ReadPoint3D(), Map );
			}
		}
	}
}

(TestItem)
Code:
using System;
using System.Collections;
using Server.Items;

namespace Server.FeudalSystem
{
	public class TestTest : Item
	{
		//public override bool CheckSaveItem{ get{ return false; } }

		[Constructable]
		public TestTest( int id ) : base( id )
		{
		}

		public TestTest( Serial serial ) : base( serial )
		{
		}

		public override void Serialize( GenericWriter writer )
		{
			//Console.WriteLine( "Serialize Test" );
			base.Serialize( writer );
			writer.Write( (int) 0 ); // version
		}

		public override void Deserialize( GenericReader reader )
		{
			//Console.WriteLine( "DeSerialize Test" );
			base.Deserialize( reader );
			int version = reader.ReadInt();
		}
	}
}

With 1 dclick of the Control on a normal server, a save is 10.9 seconds. (World.Items.Count = 1000037 )
With 1 dclick of the Control on my modified server, a save is .9 seconds. (World.Items.Count = 1000037 )

So, I therefore have to completely disagree with you on your point. The size of the Hashtable has very little to do with the time of the saves. It effects it, yes, but not to the degree of noticeability. HDD's are a LOT slower than processors, so the saving to the HDD is the slow part. (Or all of the calculations after my CheckSaveItem termination, but I don't think even those calcs are going to effect the time of saves, but I could be wrong)

*Works on figuring out how to get the world to load*
 

Kamron

Knight
Phantom said:
Not saving an item won't effect save times.

It still would be counted as an item, and would be checked, and thus during the process would take time to do this.

There would be no difference.

Phantom, I agree however that this would not make much difference, but for a different reason. If you saved 2 million items, and 500 of them did not need to be serialized, and your save time took 10 seconds, then the difference would be probably a fraction of a second.

BUT, I would have to say Phantom, that your argument that in a technical sense, it would take the same amount of time to serialize (the WHOLE process) items as apposed to not needing to, is obviously incorrect. Just because its counted as an item, doesn't discern the fact that it takes more time to write something, than not to. To emphasize my point further, I would have to say that it would be common sense, that to do something requires more effort than to not to in any respect.
 

A_Li_N

Knight
OK, so I've done a quick hack/slash to get it to load. Here are my changes and results.

World.cs - SaveItems()
Code:
			int count = 0;
			foreach ( Item item in m_Items.Values )
			{
				if( !item.CheckSaveItem )
					continue;
				count++;
			}

			idx.Write( count );
			foreach ( Item item in m_Items.Values )
			{
				if ( !item.CheckSaveItem )
					continue;

Item.cs
Code:
		private static bool m_CheckSave = true;
		public virtual bool CheckSaveItem{ get{ return m_CheckSave; } }

TestControl.cs
Code:
using System;
using System.Collections;
using Server.Items;

namespace Server.FeudalSystem
{
	public class TestControl : Item
	{
		ArrayList TestArray;
		[Constructable]
		public TestControl() : base( 3804 )
		{
			TestArray = new ArrayList();
		}

		public override void OnDoubleClick( Mobile from )
		{
			if( TestArray == null )
				TestArray = new ArrayList();

			int xx = Location.X;
			int yy = Location.Y;
			int zz = Location.Z;

			for( int x = 0; x < 1000; x++ )
			{
				for( int y = 0; y < 1000; y++ )
				{
					TestTest ti = new TestTest( 147 );
					ti.MoveToWorld( new Point3D( xx+x, yy+y, zz ), Map );
					TestArray.Add( ti );
				}
			}
		}

		public TestControl( Serial serial ) : base( serial )
		{
		}

		public override void Serialize( GenericWriter writer )
		{
			writer.Write( (int) 0 ); // version
			base.Serialize( writer );

			writer.Write( TestArray.Count );
			for( int i = 0; i < TestArray.Count; i++ )
			{
				writer.Write( ((TestTest)TestArray[i]).Location );
			}
			Console.WriteLine( "Number of Items : {0}", World.Items.Count );
		}

		public override void Deserialize( GenericReader reader )
		{
			int version = reader.ReadInt();
			base.Deserialize( reader );

			int count = reader.ReadInt();
			for( int i = 0; i < count; i++ )
			{
				TestTest tt = new TestTest( 147 );
				tt.MoveToWorld( reader.ReadPoint3D(), Map );
			}
		}
	}
}


TestTest.cs
Code:
using System;
using System.Collections;
using Server.Items;

namespace Server.FeudalSystem
{
	public class TestTest : Item
	{
		public override bool CheckSaveItem{ get{ return false; } }

		[Constructable]
		public TestTest( int id ) : base( id )
		{
		}

		public TestTest( Serial serial ) : base( serial )
		{
		}

		public override void Serialize( GenericWriter writer )
		{
			Console.WriteLine( "Serialize Test" );
			base.Serialize( writer );
			writer.Write( (int) 0 ); // version
		}

		public override void Deserialize( GenericReader reader )
		{
			Console.WriteLine( "DeSerialize Test" );
			base.Deserialize( reader );
			int version = reader.ReadInt();
		}
	}
}

Results
Code:
Clear World of all items
Add TestControl
Save:
World: Saving...Number of Items : 1
done in 0.1 seconds.

Double click TestControl
Save x3:
World: Saving...Number of Items : 1000001
done in 1.0 seconds.
World: Saving...Number of Items : 1000001
done in 1.8 seconds.
World: Saving...Number of Items : 1000001
done in 1.0 seconds.

Average save time = ~1.3 seconds


Double click TestControl
Save x3:
World: Saving...Number of Items : 2000001
done in 6.6 seconds.
World: Saving...Number of Items : 2000001
done in 2.3 seconds.
World: Saving...Number of Items : 2000001
done in 2.6 seconds.
(rest of saves were 2.4-2.8, about 4 in a row)

Average save time = ~4.5 seconds
Ram usage of server.exe 792megs

Restart server:
World: Loading...done (2000001 items, 1 mobiles) (23.0 seconds)

Like I said, it's a hack/slash test, and the ram usage would have to resolved, but I'm pretty sure that problem is with my TestControl.cs having the arraylist of the items, which is my problem, and nothing that would effect the Core modifications.

I need to look at the World.Load to see why it says it's loading 2million items, but I think it's cause it checks the size of World.Items AFTER it loads and CWL's that amount.

So, in summery, at this current point, I find that having the check would allow save times to decrease (in reference to what I'm talking about) This feature would have no effect on normal items and normal save times of course. But it would be nice to have the option of not saving an item, and this would seem to be one of the better ways to do it. I'm sure the Dev's can find a better/more efficient way of implementing it than looping through the World.Items twice.
 
Top