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!

Making a packethandler for a variable length packet?

Afr0man

Wanderer
Making a packethandler for a variable length packet?

Ok, so I'm making a WoW emu using a stripped down version of the RunUO core. So far, everything's fine, however I need to figure out how on earth to read variable length packets?

This is the description for the packet I'm trying to receive:

Code:
'Client Side Description: 

Data Type  Field Name  Details  
uint8  OpCode  The Opcode that identifies the current packet. (CMD_AUTH_LOGON_CHALLENGE)  
uint8  Error  Ignored. (0x02)  
uint16  Size  Size of the rest of the packet excluding previous fields.  
uint8[4]  GameName  4 bytes containing the name of the game. ("WoW\0")  
uint8[3]  GameVersion  3 bytes containing the version of the game. (0x01,0x0C,0x00)  
uint16  GameBuild  Build Number of the Client  
uint8[4]  Platform  Client Platform Name ("x86\0")  
uint8[4]  OperatingSystem  Client OS Name ("Win\0")  
uint8[4]  Area  Client Area/Localization ("enUS")  
uint32  TimeZone  Client Time Zone  
uint8[4]  ClientIP  Client IP (got locally)  
uint8  AccountNameLen  Length of Account name. (\0) terminator excluded.  
uint8[x]  AccountName  Account name. (\0) terminator excluded.'

Now, my brother and I (my mathskills sucks -_-) have figured out that this packet should be roughly (^^) 35 bytes in size if it HAD NOT BEEN FOR the last packet "parameter" that takes is of variable length. My problem is that when registering a packet handler, I have to specify it's length, otherwise the packet won't get read properly. So, how do I modify the code for reading a packet (I'm assuming this is located in PacketHandler.cs?) so that I can register a packethandler for a variable length packet OR inspect the packet when it is being read so that I can determine the size of the AccountName 'parameter' by looking at AccountNameLen?

I don't know if it's of any importance, but here is my PacketHandlers.cs file so far:

Code:
using System;
using System.Collections.Generic;
using System.Text;

namespace LoginServer.Network
{
    class PacketHandlers
    {
        private static PacketHandler[] m_Handlers;

        static PacketHandlers()
        {
            m_Handlers = new PacketHandler[0x100];

            //This packet seems to take 35 bytes + Accountname,
            //which is of variable length. I.E I HAVE A PROBLEM! o.O
            Register(0x00, 35, false, new OnPacketReceive(AuthLogon));
        }

        public static PacketHandler GetHandler(int PacketID)
        {
            return m_Handlers[PacketID];
        }

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

        public static void AuthLogon(NetState state, PacketReader pvSrc)
        {

        }
    }
}

Thanks in advance for any replies! :)

Edit: D'oh! The Packethandler events are where you actually read the packet data - the size of the packet registered for a particular Packethandler doesn't actually matter when you receive the packet, because the Bytequeue class seems to take care of aqcuiring the full packet anyway, as seen in Messagepump.cs here:

Code:
                    Console.WriteLine("Length: {0}, PacketLength: {1}", length, packetLength);
                    if (length >= packetLength)
                    {
                        byte[] packetBuffer;

                        if (BufferSize >= packetLength)
                            packetBuffer = m_Buffers.AcquireBuffer();
                        else
                            packetBuffer = new byte[packetLength];

                        packetLength = buffer.Dequeue(packetBuffer, 0, packetLength);

                        PacketReader r = new PacketReader(packetBuffer, packetLength, handler.Length != 0);

                        handler.OnReceive(ns, r);
                        length = buffer.Length;

                        if (BufferSize >= packetLength)
                            m_Buffers.ReleaseBuffer(packetBuffer);
                    }

If I am at a loss in my assumption here, please correct me. :)

BTW: Kudos to the devs for making the best core I have ever seen so far. The quality of it never ceases to amaze me, even when I am at a loss in times like these!
 

Beyonder

Wanderer
You should set length to 0 for custom length packets.
Code:
Register( 0x12,   0,  true, new OnPacketReceive( TextCommand ) );

And here is the packet itself:
Code:
		public static void TextCommand( NetState state, PacketReader pvSrc )
		{
			int type = pvSrc.ReadByte();
			string command = pvSrc.ReadString();

			Mobile m = state.Mobile;

			switch ( type )
			{
				case 0x00: // Go
				{
					if ( VerifyGC( state ) )
					{
						try
						{
							string[] split = command.Split( ' ' );

							int x = Utility.ToInt32( split[0] );
							int y = Utility.ToInt32( split[1] );

							int z;

							if ( split.Length >= 3 )
								z = Utility.ToInt32( split[2] );
							else if ( m.Map != null )
								z = m.Map.GetAverageZ( x, y );
							else
								z = 0;

							m.Location = new Point3D( x, y, z );
						}
						catch
						{
						}
					}

					break;
				}
				case 0xC7: // Animate
				{
					EventSink.InvokeAnimateRequest( new AnimateRequestEventArgs( m, command ) );

					break;
				}
				case 0x24: // Use skill
				{
					int skillIndex;

					try{ skillIndex = Convert.ToInt32( command.Split( ' ' )[0] ); }
					catch{ break; }

					Skills.UseSkill( m, skillIndex );

					break;
				}
				case 0x43: // Open spellbook
				{
					int booktype;

					try{ booktype = Convert.ToInt32( command ); }
					catch{ booktype = 1; }

					EventSink.InvokeOpenSpellbookRequest( new OpenSpellbookRequestEventArgs( m, booktype ) );

					break;
				}
				case 0x27: // Cast spell from book
				{
					string[] split = command.Split( ' ' );

					if ( split.Length > 0 )
					{
						int spellID = Utility.ToInt32( split[0] ) - 1;
						int serial = split.Length > 1 ? Utility.ToInt32( split[1] ) : -1;

						EventSink.InvokeCastSpellRequest( new CastSpellRequestEventArgs( m, spellID, World.FindItem( serial ) ) );
					}

					break;
				}
				case 0x58: // Open door
				{
					EventSink.InvokeOpenDoorMacroUsed( new OpenDoorMacroEventArgs( m ) );

					break;
				}
				case 0x56: // Cast spell from macro
				{
					int spellID = Utility.ToInt32( command ) - 1;

					EventSink.InvokeCastSpellRequest( new CastSpellRequestEventArgs( m, spellID, null ) );

					break;
				}
				case 0xF4: // Invoke virtues from macro
				{
					int virtueID = Utility.ToInt32( command ) - 1;

					EventSink.InvokeVirtueMacroRequest( new VirtueMacroRequestEventArgs( m, virtueID ) );

					break;
				}
				default:
				{
					Console.WriteLine( "Client: {0}: Unknown text-command type 0x{1:X2}: {2}", state, type, command );
					break;
				}
			}
		}

As you can see, if you set length to 0, RunUO just reads the first byte (with packet number), and then starts your function, where you should read all the variable packet manually, and do whatever you wish.
 
Top