Serialization
To save and load the world, RunUO uses serialization. Every item or mobile you script is required to have three different methods associated with that.
The first is very simple, a special serialization "constructor" called when the object is being loaded from the save files. Usually you can use a simple prototype:
All this does is call your parent class' serialization constructor.
The next two methods are for serializing and deserializing (saving and loading). These methods have one parameter each, a GenericWriter and GenericReader. They provide methods for reading and writing any data you have.
For the simplest of scripts, where no fields are serialized, that's all you need. You can see a "version" number is serialized; this is for backwards compatibility. If you ever modify the fields in your script, old worldfiles can still work.
Here's an example for two fields, a string and a number.
The "switch ( version )" construct allows us to branch from version to version, allowing you to write less code. Here's how we would add another string field to the same script:
The version number has been changed from 0 to 1, and the field has been added to the Serialize method. If this script loads an old world file (one with the version number of 0), it does not try to read anything for "m_StringField2".
To save and load the world, RunUO uses serialization. Every item or mobile you script is required to have three different methods associated with that.
The first is very simple, a special serialization "constructor" called when the object is being loaded from the save files. Usually you can use a simple prototype:
Code:
public MyClassName( Serial serial ) : base( serial )
{
}
All this does is call your parent class' serialization constructor.
The next two methods are for serializing and deserializing (saving and loading). These methods have one parameter each, a GenericWriter and GenericReader. They provide methods for reading and writing any data you have.
Code:
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 0 ); // version
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
}
For the simplest of scripts, where no fields are serialized, that's all you need. You can see a "version" number is serialized; this is for backwards compatibility. If you ever modify the fields in your script, old worldfiles can still work.
Here's an example for two fields, a string and a number.
Code:
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 0 ); // version
writer.Write( m_StringField );
writer.Write( m_NumberField );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
switch ( version )
{
case 0:
{
m_StringField = reader.ReadString();
m_NumberField = reader.ReadInt();
break;
}
}
}
The "switch ( version )" construct allows us to branch from version to version, allowing you to write less code. Here's how we would add another string field to the same script:
Code:
public override void Serialize( GenericWriter writer )
{
base.Serialize( writer );
writer.Write( (int) 1 ); // version
writer.Write( m_StringField2 );
writer.Write( m_StringField );
writer.Write( m_NumberField );
}
public override void Deserialize( GenericReader reader )
{
base.Deserialize( reader );
int version = reader.ReadInt();
switch ( version )
{
case 1:
{
m_StringField2 = reader.ReadString();
goto case 0;
}
case 0:
{
m_StringField = reader.ReadString();
m_NumberField = reader.ReadInt();
break;
}
}
}
The version number has been changed from 0 to 1, and the field has been added to the Serialize method. If this script loads an old world file (one with the version number of 0), it does not try to read anything for "m_StringField2".