Full Version : Question About Attachment Data
xmlspawner >>XMLSpawner - Attachments >>Question About Attachment Data


<< Prev | Next >>

HellRazor- 05-04-2007
(Edited to change the question!)

Arte,

Is there an example of an attachment to store custom properties on a player?


ArteGordon- 05-04-2007
If you are scripting your own attachments, you can define any type of property that you want.

So, for example, here is the script for the XmlData attachment that is included in the default xmlspawner package.

CODE

using System;
using Server;
using Server.Items;
using Server.Network;
using Server.Mobiles;

namespace Server.Engines.XmlSpawner2
{
public class XmlData : XmlAttachment
{
 private string m_DataValue = null;    // default data

 [CommandProperty( AccessLevel.GameMaster )]
 public string Data { get{ return m_DataValue; } set { m_DataValue = value; } }

 // These are the various ways in which the message attachment can be constructed.  
 // These can be called via the [addatt interface, via scripts, via the spawner ATTACH keyword.
 // Other overloads could be defined to handle other types of arguments
     
 // a serial constructor is REQUIRED
 public XmlData(ASerial serial) : base(serial)
 {
 }

 [Attachable]
 public XmlData(string name)
 {
  Name = name;
  Data = String.Empty;
 }

 [Attachable]
 public XmlData(string name, string data)
 {
  Name = name;
  Data = data;
 }

 [Attachable]
 public XmlData(string name, string data, double expiresin)
 {
  Name = name;
  Data = data;
  Expiration = TimeSpan.FromMinutes(expiresin);

 }

 public override void Serialize( GenericWriter writer )
 {
  base.Serialize(writer);

  writer.Write( (int) 0 );
  // version 0
  writer.Write((string)m_DataValue);

 }

 public override void Deserialize(GenericReader reader)
 {
  base.Deserialize(reader);

  int version = reader.ReadInt();
  // version 0
  m_DataValue = reader.ReadString();
 }

 public override string OnIdentify(Mobile from)
 {
  if(from == null || from.AccessLevel == AccessLevel.Player) return null;

  if(Expiration > TimeSpan.Zero)
  {
   return String.Format("{2}: Data {0} expires in {1} mins",Data,Expiration.TotalMinutes, Name);
  }
  else
  {
   return String.Format("{1}: Data {0}",Data, Name);
  }
 }
}
}


If you wanted to make your own custom attachment that stored an int instead, you could mod it like this...

CODE

using System;
using Server;
using Server.Items;
using Server.Network;
using Server.Mobiles;

namespace Server.Engines.XmlSpawner2
{
public class XmlIntData : XmlAttachment
{
 private int m_DataValue = 0;    // default data

 [CommandProperty( AccessLevel.GameMaster )]
 public int Data { get{ return m_DataValue; } set { m_DataValue = value; } }

 // These are the various ways in which the message attachment can be constructed.  
 // These can be called via the [addatt interface, via scripts, via the spawner ATTACH keyword.
 // Other overloads could be defined to handle other types of arguments
     
 // a serial constructor is REQUIRED
 public XmlIntData(ASerial serial) : base(serial)
 {
 }

 [Attachable]
 public XmlIntData(string name)
 {
  Name = name;
 }

 [Attachable]
 public XmlIntData(string name, int data)
 {
  Name = name;
  Data = data;
 }

 [Attachable]
       public XmlIntData(string name, int data, double expiresin)
 {
  Name = name;
  Data = data;
  Expiration = TimeSpan.FromMinutes(expiresin);

 }

 public override void Serialize( GenericWriter writer )
 {
  base.Serialize(writer);

  writer.Write( (int) 0 );
  // version 0
  writer.Write((int)m_DataValue);

 }

 public override void Deserialize(GenericReader reader)
 {
  base.Deserialize(reader);

  int version = reader.ReadInt();
  // version 0
  m_DataValue = reader.ReadInt();
 }

 public override string OnIdentify(Mobile from)
 {
  if(from == null || from.AccessLevel == AccessLevel.Player) return null;

  if(Expiration > TimeSpan.Zero)
  {
   return String.Format("{2}: Data {0} expires in {1} mins",Data,Expiration.TotalMinutes, Name);
  }
  else
  {
   return String.Format("{1}: Data {0}",Data, Name);
  }
 }
}
}


note that you dont actually have to have some of that code in there, such as the multiple constructors, or the OnIdentify override. Here would be a stripped down version that would have the minimum code required for a useable attachment

CODE

using System;
using Server;

namespace Server.Engines.XmlSpawner2
{
   public class XmlIntData : XmlAttachment
   {
       private int m_DataValue = 0;    // default data

       [CommandProperty(AccessLevel.GameMaster)]
       public int Data { get { return m_DataValue; } set { m_DataValue = value; } }

       public XmlIntData(ASerial serial)
           : base(serial)
       {
       }

       [Attachable]
       public XmlIntData()
       {
       }

       public override void Serialize(GenericWriter writer)
       {
           base.Serialize(writer);

           writer.Write((int)0);
           // version 0
           writer.Write((int)m_DataValue);
       }

       public override void Deserialize(GenericReader reader)
       {
           base.Deserialize(reader);

           int version = reader.ReadInt();
           // version 0
           m_DataValue = reader.ReadInt();
       }
   }
}


Now if you wanted to store an array of ints, for example, you could do it like

CODE

using System;
using Server;

namespace Server.Engines.XmlSpawner2
{
   public class XmlIntArrayData : XmlAttachment
   {
       private int[] m_DataValue;    // default data

       [CommandProperty(AccessLevel.GameMaster)]
       public int[] Data { get { return m_DataValue; } set { m_DataValue = value; } }

       public XmlIntArrayData(ASerial serial)
           : base(serial)
       {
       }

       [Attachable]
       public XmlIntArrayData()
       {
       }

       public override void Serialize(GenericWriter writer)
       {
           base.Serialize(writer);

           writer.Write((int)0);
           // version 0
           if (Data == null)
           {
               writer.Write((int)0);
           }
           else
           {
               writer.Write((int)Data.Length);
               for (int i = 0; i < Data.Length; i++)
               {
                   writer.Write((int)Data[i]);
               }
           }
       }

       public override void Deserialize(GenericReader reader)
       {
           base.Deserialize(reader);

           int version = reader.ReadInt();
           // version 0
           int count = reader.ReadInt();

           Data = new int[count];
           for (int i = 0; i < count; i++)
           {
               Data[i] = reader.ReadInt();
           }
       }
   }
}


ArteGordon- 05-04-2007
those examples could be used to create and store custom properties on a player. You would create the attachment with your custom properties, and then when you wanted to access them you could look up the properties from the attachment.

METHOD 1--------------------------------------------------------------
CODE

using System;
using Server;

namespace Server.Engines.XmlSpawner2
{
   public class CustomPlayerProperties : XmlAttachment
   {
       private DateTime m_LastBathed;

       [CommandProperty(AccessLevel.GameMaster)]
       public DateTime LastBathed { get { return m_LastBathed; } set { m_LastBathed= value; } }

       public CustomPlayerProperties(ASerial serial)
           : base(serial)
       {
       }

       [Attachable]
       public CustomPlayerProperties()
       {
       }

       public override void Serialize(GenericWriter writer)
       {
           base.Serialize(writer);

           writer.Write((int)0);
           // version 0
           writer.Write(m_LastBathed);
       }

       public override void Deserialize(GenericReader reader)
       {
           base.Deserialize(reader);

           int version = reader.ReadInt();
           // version 0
           m_LastBathed = reader.ReadDateTime();
       }
   }
}


and then in your scripts where you wanted to access the custom 'LastBathed' property, you would look up the attachment on the player and access the property, like

CODE

CustomPlayerProperties p = (CustomPlayerProperties) XmlAttach.FindAttachment(from, typeof(CustomPlayerProperties));

if(p != null && p.LastBathed + TimeSpan.FromDays(30) < DateTime.Now)
{
from.SendMessage("You really need to bathe at at least once a month!");
}


METHOD 2--------------------------------------------------------------

Now, if you dont like the idea of having to look up the attachment in your script every time you want to access your custom properties, you can also mod your playermobile script to add the custom properties there, and then have the attachment save/restore the playermobile properties. It basically allows you to use the attachment to handled the ser/deser of the custom properties without having to mod the ser/deser of the playermobile class itself.
The code would then look like

CODE

if(from.LastBathed + TimeSpan.FromDays(30) < DateTime.Now)
{
from.SendMessage("You really need to bathe at at least once a month!");
}


where 'from' would refer to your modified playermobile.
Then in your playermobile class you would add your custom properties

CODE

       private DateTime m_LastBathed;

       [CommandProperty(AccessLevel.GameMaster)]
       public DateTime LastBathed { get { return m_LastBathed; } set { m_LastBathed = value; } }


and in your attachment script, you would now set those custom playermobile properties as soon as you put the attachment back on the playermobile (during world loads) with this method in your custom attachment script

CODE

       public override void OnReattach()
       {
           base.OnReattach();

           PlayerMobile from = AttachedTo as PlayerMobile;

           if (from != null)
           {
               from.LastBathed = m_LastBathed;
           }
       }


this would be the full custom attachment that would automatically set/save/restore your custom playermobile property

CODE

using System;
using Server;

namespace Server.Engines.XmlSpawner2
{
   public class CustomPlayerProperties : XmlAttachment
   {
       private DateTime m_LastBathed;

       [CommandProperty(AccessLevel.GameMaster)]
       public DateTime LastBathed {
           get
           {
               // get it from the attached player if available
               PlayerMobile from = AttachedTo as PlayerMobile;
               if (from != null)
               {
                   return from.LastBathed;
               } else
                   return m_LastBathed;
           }
           set
           {
               m_LastBathed = value;
               // set it on the attached player if available
               PlayerMobile from = AttachedTo as PlayerMobile;
               if (from != null)
               {
                   from.LastBathed = m_LastBathed;
               }
           }
       }

       public CustomPlayerProperties(ASerial serial)
           : base(serial)
       {
       }

       [Attachable]
       public CustomPlayerProperties()
       {
       }

       public override void OnReattach()
       {
           base.OnReattach();

           PlayerMobile from = AttachedTo as PlayerMobile;

           // restore the custom property values on the modified playermobile
           if (from != null)
           {
               from.LastBathed = m_LastBathed;
           }
       }

       public override void Serialize(GenericWriter writer)
       {
           base.Serialize(writer);

           writer.Write((int)0);
           // version 0

           writer.Write(LastBathed);

       }

       public override void Deserialize(GenericReader reader)
       {
           base.Deserialize(reader);

           int version = reader.ReadInt();
           // version 0

           m_LastBathed = reader.ReadDateTime();
       }
   }
}


I tend not to use the second approach just because it involves more modifications and locks the custom attachment to a particular custom modded playermobile, but it is more transparent to the scripts that have to access those custom properties since scripts that refer to your custom playermobile properties dont have to know anything about attachments at all.

HellRazor- 05-04-2007
Wow. As usual Arte, you are the MAN. You always exceed all expectations.

As you can probably tell I am easing my way into learning and understanding the XML attachment system and this is all REALLY helpful. Thank you!!!

P.S. Hopefully now I will be able to update that "Last Bathed" property! smile.gif

ArteGordon- 05-04-2007
QUOTE (HellRazor @ May 04, 2007 07:50 am)
Wow.  As usual Arte, you are the MAN.  You always exceed all expectations.

As you can probably tell I am easing my way into learning and understanding the XML attachment system and this is all REALLY helpful.  Thank you!!!

P.S.  Hopefully now I will be able to update that "Last Bathed" property!  smile.gif

or you could just change the test from 30 days to 60 wink.gif

Also, once you have your CustomPlayerProperties attachment there, you can just keep piling new custom properties into that one attachment. You dont need to make new attachments for new properties (although you can if you want).