Full Version : Attachments & Efficiency..
xmlspawner >>XMLSpawner - Attachments >>Attachments & Efficiency..


<< Prev | Next >>

neizarr- 12-27-2006
I am curious about the efficiency of attachments. It looks like such a powerful and useful system that I will be using it alot, and so need to know it will not bog down the system.

I assume they are written out in some sort of XML format in the saves/attachments folder, and read from the same. When and how often are they read from file into memory? For instance, if I have an attachment which is saved to a mobile, will the attachment be loaded into memory on loading the mobile from the save file? Or will the attachment only be loaded into memory once a call to look up an attachment is made on that mobile? Or are all attachments loaded into memory at once at startup?

I ask this because I intend to use it for some pretty intensive things, and while having it in memory and a quick lookup isn't a problem(hopefully), reading from disk every time to look for an attachment would be way too slow for some applications.

For instance, on my POL shard, I have a script that when you type .sa and target someone(sa = say above), it saves the serial of what you are talking above onto yourself(as a sort of attachment for POL), and then there is a packet hook running which hooks the outgoing speech packet, and when you say something, it looks to see if that attachment serial exists on you, if not, normal processing of the packet ensues, if so, then it uses the serial in the attachment to look up the mobile reference and then prints the text you spoke over the mobile's head.

This allows you to be 'speaking' normally as your GM, but instead of the text appearing over your own head it appears over the mobile(or item) you are speaking above.

If attachments are in memory at the time of the search or at least only loaded once for the first search, then this isn't a big problem likely, if it is loaded from disk every time the search is performed, then for this particular application I will probably have to look to some other means of handling it. (ie, probably modifying the playermobile class, which I would really rather not do)

The hope being that eventually I will have a [sa replacement in RunUO for the .sa I have in POL.

Thanks for any insight you can give into the efficiency of attachments!



ArteGordon- 12-27-2006
All attachments are loaded into memory during world loads on server restarts.
They are saved in a binary format that is similar to the way in which items/mobiles are serialized, and are serialized/deserialized using the same binary writer/readers.

Once in memory, they are accessed using hash table lookups keyed on the object, so there is no significant search time overhead and since the hash lookups are O(1) there is no additional overhead as the number of attachments grows.

When comparing the load associated with the use of attachments with that of an actual custom property added to the object class, there is the additional overhead of the hash lookup to find the attachment list on the object, and then if an object has multiple attachments, it has to scan that list, but that is generally short.
I use them all over the place by the tens of thousands with no detectable load effect.

If you take a look in the Members Tips forum, you will find some examples of how attachments can be incorporated into normal server methods to give handy custom functionality to tagged objects of the sort you are describing.

Also note that all of the XmlSpawner addon systems are attachment-based.

You can also find some additional handy attachments in the XmlSpawner Files forum.

neizarr- 12-27-2006

After reading through those, I am getting a sense of it all, but I feel I would be stupid not to ask you for your suggestion on how to approach the problem exactly, since I may not even have to do this by looking into packet hooks and such in RunUO for all I know.

All I really want to do is have a command that you type :

[sa

and it asks you to target who you want to talk above. When you target that person, you speak/emote/whisper/yell over that person(whether you yourself are hidden or not). To get out of it, you just type [sa again and instead of targetting another mobile, you target yourself, and you are back to speaking over yourself.

The idea I had for how to do this was, as I mentioned, storing the serial of what you target to an attachment stored on yourself. If you target yourself, delete the attachment. Then creating some sort of packet hook interception(something I have yet to look into as to how RUO handles this) which does the redirection of where the speech actually goes. Possibly by intercepting the incoming(from client to server) speech packet and doing a lookup for the packet on that player mobile by using the serial given by the packet to find the player, and then a lookup for the speech redirection attachment which would contain the serial of what to speak over. If that exists, handle putting the speech above the mobile, if it doesn't exist, call whatever the typical handling of the packet is.

It is probably abusing you some to ask this, but would you have some hints as to how you think would be the best way to go about it? Should I handle it as I am considering? Or is there some better way of doing it in RUO?

Hopefully you don't mind the questions. They are sort of off topic, since the attachment part of this is pretty simple likely regardless(store the targetted mobile's serial to an attachment on the person doing the targetting, simple enough), but you are just so obviously superior a scripter to most(certainly including me) that I can't help but ask you how you would handle it if you wanted to do such a thing. smile.gif

Thanks for any help in advance and IMO the whole XML system should clearly be part of the distro if you ask me and replace most of the current systems it makes redundant!


ArteGordon- 12-27-2006
I would add a little hook into the PlayerMobile DoSpeech method that checks for an attachment on the speaking player, and if it finds it then it invokes the DoSpeech method on the target mobile.

Your [sa command would add/remove the attachment depending on who was targeted. The DoSpeech mod would be pretty simple.

QUOTE

        public override void DoSpeech(string text, int[] keywords, MessageType type, int hue)
        {
            // ARTEGORDONMOD
            // check for the [sa attachment and if found then redirect speech to them
            XmlMobile x = (XmlMobile)XmlAttach.FindAttachment(this, typeof(XmlMobile), "sa");
            if (x != null)
            {             
                // check the target to see if it has an sa attachment to make sure you dont start pingponging
                Mobile target = x.Value;
                if (target != null && XmlAttach.FindAttachment(target, typeof(XmlMobile), "sa") == null)
                {
                    target.DoSpeech(text, keywords, type, hue);
                    return;
                }
            }

            if (Guilds.Guild.NewGuildSystem && (type == MessageType.Guild || type == MessageType.Alliance))
            {
                Guilds.Guild g = this.Guild as Guilds.Guild;
                if (g == null)
                {
                    SendLocalizedMessage(1063142); // You are not in a guild!
                }
                else if (type == MessageType.Alliance)
                {


I would also make a custom attachment to track mobile values.

XmlMobile.cs
CODE

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

namespace Server.Engines.XmlSpawner2
{
   public class XmlMobile : XmlAttachment
   {
       private Mobile m_DataValue;

       [CommandProperty(AccessLevel.GameMaster)]
       public Mobile Value { 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 XmlMobile(ASerial serial)
           : base(serial)
       {
       }

       [Attachable]
       public XmlMobile(string name, Mobile value)
       {
           Name = name;
           Value = value;
       }

       [Attachable]
       public XmlMobile(string name, Mobile value, double expiresin)
       {
           Name = name;
           Value = value;
           Expiration = TimeSpan.FromMinutes(expiresin);

       }

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

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

       }

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

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

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

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


and then the [sa code would just set up the attachment when you target another mobile or delete it if you target yourself.

SA.cs
CODE

using System;
using System.Text;
using Server;
using Server.Items;
using Server.Network;
using System.Collections;
using System.Reflection;
using Server.Targeting;
using Server.Mobiles;
using Server.Multis;
using Server.Engines.XmlSpawner2;
using Server.Gumps;
using Server.Engines.Help;
using Server.Commands;
using Server.Commands.Generic;
using Server.Accounting;


namespace Server.Scripts.Commands
{
   public class GenericCommand
   {
       public static void Initialize()
       {
           CommandSystem.Register("sa", AccessLevel.GameMaster, new CommandEventHandler(GenericCommand_OnCommand));
       }


       public class GenericTarget : Target
       {

           public GenericTarget()
               : base(30, true, TargetFlags.None)
           {
               CheckLOS = false;
           }
           protected override void OnTarget(Mobile from, object targeted)
           {
               if (from == null || targeted == null) return;

               if (targeted is Mobile)
               {
                   Mobile m = (Mobile)targeted;

                   if (from == m)
                   {
                       // remove the attachment if targeting self
                       XmlMobile x = (XmlMobile)XmlAttach.FindAttachment(from, typeof(XmlMobile), "sa");
                       if (x != null)
                           x.Delete();
                   }
                   else
                   {
                       XmlAttach.AttachTo(from, new XmlMobile("sa", m));
                   }
               }
           }
       }

       [Usage("sa")]
       public static void GenericCommand_OnCommand(CommandEventArgs e)
       {
           if (e != null && e.Mobile != null)
               e.Mobile.Target = new GenericTarget();
       }
   }
}



Note that this is doing more than just displaying the speech over the target mobiles head, it is actually causing them to speak those words just as if they had said it themselves. It will be logged as their speech, it will activate speech triggered items, etc.

If you just want it to display the text overhead instead of actually speaking, just substitute a target.PublicOverheadMessage call for the
target.DoSpeech(text, keywords, type, hue);

in the playermobile mod. If you do that, you can also get rid of the pingponging check.

neizarr- 12-27-2006

Incredible. smile.gif I didn't expect you to actually write it for me, but thanks a ton! I will scour over it line by line and make sure I understand every speck of it so I can learn as much as possible from it.

Thanks again!