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!

Packet Hooking

Jeff

Lord
So, now that we have a way to hook notoriety functions (found here http://www.runuo.com/community/threads/handler-hooking.472803/#post-3677865), we can apply this to any handler in RunUO. For instance, you could apply the same technique to the following Mobile handlers
Code:
	public delegate bool SkillCheckTargetHandler( Mobile from, SkillName skill, object target, double minSkill, double maxSkill );
	public delegate bool SkillCheckLocationHandler( Mobile from, SkillName skill, double minSkill, double maxSkill );

	public delegate bool SkillCheckDirectTargetHandler( Mobile from, SkillName skill, object target, double chance );
	public delegate bool SkillCheckDirectLocationHandler( Mobile from, SkillName skill, double chance );

	public delegate TimeSpan RegenRateHandler( Mobile from );
However, what if a handler doesn't exist? Seems like we are just out of luck and have to do a core/distro edit, doesn't it? Well, not necessarily. One thing I think most people forget about, or are just to timid to try is working with packets. RunUO's packet handler registry provides the ability to retrieve a packet handler by packet id, and register new packet handlers. Using this we can design packet hooking functionality that works almost exact like our Notoriety hook. Now, because we have different types of packet handlers (regular, extended, throttled, 6017, encoded, did i miss any?), we need the ability to create hooks for all 5 (or more). So, we need to start with a base class, something that will contain our base functionality:
Code:
    public abstract class PacketHandlerChainBase
    {
        protected abstract void InvokeSuccessor(NetState state, PacketReader pvSrc);

        protected virtual void OnPacketReceive(NetState state, PacketReader pvSrc)
        {
            InvokeSuccessor(state, pvSrc);
        }
    }

Now, from here we can start implementing our packet handler chains for each type of packet we can receive. Lets start with a regular packet handler, we will call it PacketHandlerChain
Code:
    public class PacketHandlerChain : PacketHandlerChainBase
    {
        private readonly PacketHandler _handler;

        protected PacketHandlerChain(int packetId, int length, bool ingame)
        {
            _handler = PacketHandlers.GetHandler(packetId);

            Asserter.IsNotNull(_handler, string.Format("Unable to find packet handler for packet id 0x{0:X2}", packetId));
            PacketHandlers.Register(packetId, length, ingame, OnPacketReceive);
        }

        protected override void InvokeSuccessor(NetState state, PacketReader pvSrc)
        {
            _handler.OnReceive(state, pvSrc);
        }
    }
Just like our NotorietyHandlerChain we store the original handler to a local variable and register a new handler. The new handler OnPacketReceived can be overridden to provide new functionality, or to just snoop the packet before it is processed by the server. Using our same "Event System" concept as we did in the NotorietyHandlerChain example, we can create a new packet hook for Party packets.
Code:
    public sealed class PartyMessagePacketChain : ExtendedPacketHandlerChain
    {
        public PartyMessagePacketChain()
            : base(0x06, true) { }

        protected override void OnPacketReceive(Server.Network.NetState state, Server.Network.PacketReader pvSrc)
        {
            if (state.Mobile == null)
                return;

            EventContext eventContext = EventController.FindEvent(state.Mobile);

            if (eventContext == null)
            {
                base.OnPacketReceive(state, pvSrc);
                return;
            }

            switch (pvSrc.ReadByte())
            {
                case 0x01: eventContext.OnPartyAdd(state.Mobile); break;
                case 0x02: eventContext.OnPartyRemove(state.Mobile, World.FindMobile(pvSrc.ReadInt32())); break;
                case 0x03: eventContext.OnPartyPrivateMessage(state.Mobile, World.FindMobile(pvSrc.ReadInt32()), pvSrc.ReadUnicodeStringSafe()); break;
                case 0x04: eventContext.OnPartyPublicMessage(state.Mobile, pvSrc.ReadUnicodeStringSafe()); break;
                case 0x06: eventContext.OnPartySetCanLoot(state.Mobile, pvSrc.ReadBoolean()); break;
                case 0x08: eventContext.OnPartyAccept(state.Mobile, World.FindMobile(pvSrc.ReadInt32())); break;
                case 0x09: eventContext.OnPartyDecline(state.Mobile, World.FindMobile(pvSrc.ReadInt32())); break;
                default:
                    //Seek back to 0 so that the original packet handler can read the same byte we used.
                    pvSrc.Seek(-1, System.IO.SeekOrigin.Current);
                    base.OnPacketReceive(state, pvSrc);
                    break;
            }
        }
    }
Remember, this is just an example, there really isn't an event system, just something made up for this sample. The idea here is that if the packet belongs to a mobile that is in an event, we can pass the information in the packet on to the EventContext and let the Event handle the packet as it needs. If the event chooses to push the code on to the original party system, it can do so, but that would be extra code, and completely independent of what we are trying to accomplish here.

To initialize this, we can just do the following:
Code:
public static class EventController
{
    public static void Initialize()
    {
        new PartyMessagePacketChain();
        ...
    }

    ...
}
This same approach can be applied for each of the packet types, for example, Extended packets can be done like this:
Code:
    public class ExtendedPacketHandlerChain : PacketHandlerChainBase
    {
        private readonly PacketHandler _handler;

        protected ExtendedPacketHandlerChain(int packetId, bool ingame)
        {
            _handler = PacketHandlers.GetExtendedHandler(packetId);

            Asserter.IsNotNull(_handler, string.Format("Unable to find extended packet handler for packet id 0x{0:X2}", packetId));
            PacketHandlers.RegisterExtended(packetId, ingame, OnPacketReceive);
        }

        protected override void InvokeSuccessor(NetState state, PacketReader pvSrc)
        {
            _handler.OnReceive(state, pvSrc);
        }
    }
While this won't allow you to hook all functionality in RunUO, it will allow you to have access to a lot more without editing core/distro code, making your custom releases even that much more plug and play.
 

Praxiiz

Sorceror
This is a great tutorial. I was trying to use it to hook some login functionality for map definitions, and found it works perfectly for the older clients. I was looking through the PacketHandlers.cs file trying to figure out why it wouldn't work with the newer clients, and I found this:

Code:
public static void Register( int packetID, int length, bool ingame, OnPacketReceive onReceive )
{
   m_Handlers[packetID] = new PacketHandler( packetID, length, ingame, onReceive );

   if ( m_6017Handlers[packetID] == null )
      m_6017Handlers[packetID] = new PacketHandler( packetID, length, ingame, onReceive );
}

Have you found anyway around that?
 
Top