Broken Code

Diskussion und Informationen über UO:KR
Nachricht
Autor
Link_of_Hyrule

Broken Code

#1 Beitrag von Link_of_Hyrule » 17 Jul 2007 05:46

The latest osi patch broke some code so far ive seen that walking if you stop you get stuck and paperdoll and other gumps wont pop up i dont know if there is an easy fix or not i would look more into it but tommorow the movers are coming and i dont know when ill have internet at the new house so i might be online again for like a month or so

Mitos47

Re: Broken Code

#2 Beitrag von Mitos47 » 17 Jul 2007 16:06

Link_of_Hyrule hat geschrieben:The latest osi patch broke some code so far ive seen that walking if you stop you get stuck and paperdoll and other gumps wont pop up i dont know if there is an easy fix or not i would look more into it but tommorow the movers are coming and i dont know when ill have internet at the new house so i might be online again for like a month or so
LOL, it's so funny when one byte can creates "holy cries" :)

P.S. 6.0.1.7 patch does not break paperdolls, movers and a lot of gumps :) Analyse carefully packets with SpyUO for 2d client and you will see that only one byte was added to several packets with items lists :) And really this byte can be set in 0 for 2D client, because it was created for KR client :) I don't understand why all freeshards are crying about this patch and warning their players do not patch client :)

Link_of_Hyrule

#3 Beitrag von Link_of_Hyrule » 18 Jul 2007 08:38

well then how do you fix it?

EDIT: ok well i dont know what was up with walking and stuff last night but it seems to work on my dads pc anyways from what i can tell inventory is broken on both client and paperdolls are broken on kr other then that i think everything is fine im sure its an easy fix that we can fix quickly

and the latest patch for 2d says

2D Client vers. 1.45.5.1


Backend compatibility changes

EDIT:

ok well i just logged off and closed the client then logged back in and now nothing works again im guessing there must be a packet the client is getting that it doesnt like that causes it to freeze up i dont have any clue why if someone can packet log it i bet we can figure it out

Sabresite

#4 Beitrag von Sabresite » 21 Jul 2007 00:57

For 6.0.1.7 - Have a list of added/modified packets? I know of only a few

Drop Request, Container Content, Update Container Content, and possibly Secure Trade Equip

I do not have an OSI account, so I cannot find out.

Mitos47

#5 Beitrag von Mitos47 » 21 Jul 2007 06:14

Sabresite hat geschrieben:For 6.0.1.7 - Have a list of added/modified packets? I know of only a few

Drop Request, Container Content, Update Container Content, and possibly Secure Trade Equip

I do not have an OSI account, so I cannot find out.
And VendorBuyList too. Really, only two server-side packets: 0x3C and 0x25 and one client-side packet 0x08 changed.

Sabresite

#6 Beitrag von Sabresite » 21 Jul 2007 09:04

Then why does it still give me problems after updating those packets... The game still sends packets every second (0xBF, 0x06, 0x00.....) (according to SpyUO), no status information displays, paperdoll, etc.

pmouse

#7 Beitrag von pmouse » 21 Jul 2007 20:31

It seems that a few packets are sent with 0xFF prepend to them, when I drop an item to world, for example, the object property request 0xD6 is now 0xFF 0xD6 etc. Immediately following the 0xFF packet i have the item's serial being sent in another packet.

I'm wondering if 0xFF actually means a two part packet?

UPDATE: nvm, I'm stupid

Sabresite

#8 Beitrag von Sabresite » 22 Jul 2007 00:53

Well, it works for players... but it doesn't work for administrators on RunUO emulated servers.

Do you have a list of files that use the 0xFF prepend?

pmouse

#9 Beitrag von pmouse » 22 Jul 2007 03:42

The "0xff" prepending is due to a mistake made by the packet handlers and packets. Do use what's posted on the runuo forum, it's not going to work.

I'll post a summary here later today.

Sabresite

#10 Beitrag von Sabresite » 22 Jul 2007 19:59

Okay, a summary is good..

pmouse

#11 Beitrag von pmouse » 22 Jul 2007 22:00

forgot to turn off HTML in this post...repost
Zuletzt geändert von pmouse am 22 Jul 2007 22:02, insgesamt 1-mal geändert.

pmouse

#12 Beitrag von pmouse » 22 Jul 2007 22:01

Here's my list of modifications (with KR support)

in NetState, declare a boolean property indicating whether the client version is the new client

Code: Alles auswählen

        private static ClientVersion PostUOKRClient = new ClientVersion("6.0.1.7");

        public bool UseKRPacket
        {
            get
            {
                return (m_Version != null && m_Version >= PostUOKRClient) || m_KRClient;
            }
        }
First change is to the 0x8 packet, as indicated by many sources, there is now an extra one byte added to the packet. That byte is used to tell the server which slot the item was COMING from (not dropping into). The KR slot management is now done on the server side (maybe possible client exploit?), so, here goes:

Code: Alles auswählen

		public static void DropReq( NetState state, PacketReader pvSrc )
		{
			pvSrc.ReadInt32(); // serial, ignored
			int x = pvSrc.ReadInt16();
			int y = pvSrc.ReadInt16();
			int z = pvSrc.ReadSByte();

            if (state.UseKRPacket)
            {
                pvSrc.ReadByte();
            }

			Serial dest = pvSrc.ReadInt32();
			Point3D loc = new Point3D( x, y, z );
			Mobile from = state.Mobile;

            
			if ( dest.IsMobile )
				from.Drop( World.FindMobile( dest ), loc );
			else if ( dest.IsItem )
				from.Drop( World.FindItem( dest ), loc );
			else
				from.Drop( loc );
		}
Now, the problem is with the packet size. This handler has a registered size of 14 bytes (runuo handlers must register it's size, or set to zero for dynamic size packets). Depending on the client version, you want this to have either 14 bytes or 15 bytes, and you can't simply set it to zero because this packet contains no size information...OSI doesn't need to handle this because they will only support the new protocol. If you want to support the old clients also, you must modify MessagePump to "dirty hack" the issue away:

My entire MessagePump::HandleReceive(NetState), with KR seeding support. (I don't use the 0xFF packet for KR seeding, rather, KR sends 0xFFFFFFFF = -1 for the seed, that's enough to tell it's a KR client, much cleaner this way.
You may also notice there may be a few lines different from the 2.0 SVN version, that's because I don't use 2.0, the core was modified from V1.0.

Code: Alles auswählen


		public bool HandleReceive( NetState ns )
		{
            ByteQueue buffer = ns.Buffer;

            if (buffer == null || buffer.Length <= 0)
                return true;

            lock (buffer)
			{
				int length = buffer.Length;

                if (!ns.Seeded)
                {
                    if (buffer.Length >= 4)
                    {
                        buffer.Dequeue(m_Peek, 0, 4);

                        int seed = (m_Peek[0] << 24) | (m_Peek[1] << 16) | (m_Peek[2] << 8) | m_Peek[3];

                        if (seed == 0)
                        {
                            Console.WriteLine("Login: {0}: Invalid client detected, disconnecting", ns);
                            ns.Dispose();
                            return false;
                        }
                        else if (seed == -1)
                        {
                            Console.WriteLine("Login: {0} is KR Client", ns);
                            ns.Send(new KRVerifier());
                            ns.IsKRClient = true;
                        }

                        ns.m_Seed = seed;
                        ns.Seeded = true;

                        length = buffer.Length;
                    }
                    else
                    {
                        return true;
                    }
                }


				//Console.WriteLine( "{" );

				while ( length > 0 && ns.Running )
				{
					int packetID = buffer.GetPacketID();

                    if (!ns.SentFirstPacket && packetID != 0xF1 && packetID != 0xCF && packetID != 0x80 && packetID != 0x91 && packetID != 0xA4 && packetID!=0xE4)
					{
						Console.WriteLine( "Client: {0}: Encrypted client detected, disconnecting", ns );
						ns.Dispose();
						break;
					}

					PacketHandler handler = PacketHandlers.GetHandler( packetID );

					if ( handler == null )
					{
						byte[] data = new byte[length];
						length = buffer.Dequeue( data, 0, length );

						new PacketReader( data, length, false ).Trace( ns );

						break;
					}


					int packetLength = handler.Length;

                    // TODO: remove this dirty hack later for full 6.0.1.7 support
                    if (packetID == 0x8)
                    {
                        if (ns.UseKRPacket)
                            packetLength = 15;
                        else
                            packetLength = 14;
                    }

					if ( packetLength <= 0 )
					{
						if ( length >= 3 )
						{
							packetLength = buffer.GetPacketLength();

							if ( packetLength < 3 )
							{
								ns.Dispose();
								break;
							}
						}
						else
						{
							break;
						}
					}

					if ( length >= packetLength )
					{
						if ( handler.Ingame && ns.Mobile == null )
						{
							Console.WriteLine( "Client: {0}: Sent ingame packet (0x{1:X2}) before having been attached to a mobile", ns, packetID );
							ns.Dispose();
							break;
						}
						else if ( handler.Ingame && ns.Mobile.Deleted )
						{
							ns.Dispose();
							break;
						}
						else
						{
							ThrottlePacketCallback throttler = handler.ThrottleCallback;

							if ( throttler != null && !throttler( ns ) )
							{
								m_Throttled.Enqueue( ns );
								//Console.WriteLine( "}" );
								return false;
							}

							//Console.WriteLine( handler.OnReceive.Method.Name );

							PacketProfile profile = PacketProfile.GetIncomingProfile( packetID );
							DateTime start = ( profile == null ? DateTime.MinValue : DateTime.Now );

							byte[] packetBuffer;

							if ( BufferSize >= packetLength )
								packetBuffer = m_Buffers.AquireBuffer();
							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 ( profile != null )
								profile.Record( packetLength, DateTime.Now - start );

							//Console.WriteLine( "Client: {0}: Unhandled packet 0x{1:X2}", ns, packetID );
							//Utility.FormatBuffer( Console.Out, new System.IO.MemoryStream( r.Buffer ), r.Buffer.Length );
						}
					}
					else
					{
						break;
					}
				}

				//Console.WriteLine( "}" );
			}

			return true;
		}
Now, the "0xFF" problem, which is the server thinking it received 0xFF packets sent from the clients after dropping to the ground. That problem is caused by the size mismatch in the 0x08 handler. After dropping to the ground, the client sends 0xFFFFFFFF as the target UID. But since we are missing one byte from the handler (14 vs. 15), it only sees 0xFFFFFF, the next part of the packet is trimmed and prepended to the next packet - which is often the 0xD6 property list require. This is why we always see 0xFF 0xD6 after client drops to the ground.

Now we have this issue straight, the rest is simple:

the 0x25 Packet also have one byte added

Code: Alles auswählen

public sealed class ContainerContentUpdate : Packet
	{
        public ContainerContentUpdate(Item item, bool uokr)
            : base(0x25, uokr ? 21 : 20)
		{
			Serial parentSerial;

			if ( item.Parent is Item )
			{
				parentSerial = ((Item)item.Parent).Serial;
			}
			else
			{
				Console.WriteLine( "Warning: ContainerContentUpdate on item with !(parent is Item)" );
				parentSerial = Serial.Zero;
			}

			ushort cid = (ushort) item.ItemID;

			if ( cid > 0x3FFF )
				cid = 0x9D7;

			m_Stream.Write( (int) item.Serial );        // 4
			m_Stream.Write( (short) cid );              // 6
			m_Stream.Write( (byte) 0 );                 // 7 signed, itemID offset
			m_Stream.Write( (ushort) item.Amount );     // 9
			m_Stream.Write( (short) item.X );           // 11
            m_Stream.Write((short)item.Y);              // 13
            if (uokr)
                m_Stream.Write((byte)item.GridLocation); // 14 / 13 container grid location for UOKR client
			
			m_Stream.Write( (int) parentSerial );       // 18 / 17
			m_Stream.Write( (ushort) item.Hue );        // 20 / 19
		}
	}

Code: Alles auswählen

public sealed class SecureTradeEquip : Packet
	{
		public SecureTradeEquip( Item item, Mobile m, bool uokr ) : base( 0x25, uokr? 21:20 )
        {
			m_Stream.Write( (int) item.Serial );
			m_Stream.Write( (short) item.ItemID );
			m_Stream.Write( (byte) 0 );
			m_Stream.Write( (short) item.Amount );
			m_Stream.Write( (short) item.X );
            m_Stream.Write((short)item.Y); 
            
            if (uokr)
                m_Stream.Write((byte)item.GridLocation);

			m_Stream.Write( (int) m.Serial );
			m_Stream.Write( (short) item.Hue );
		}
	}
The problem with these two packets above, is that they are fixed size packets. You cannot call EnsureCapacity on them, otherwise they become dynamic size and RunUO is confused.

but you can do it with the 0x3c packet

Code: Alles auswählen

public sealed class ContainerContent : Packet
	{
		public ContainerContent( Mobile beholder, Item beheld, bool uokr ) : base( 0x3C )
		{
			ArrayList items = beheld.Items;
			int count = items.Count;
            this.EnsureCapacity(5 + (count * (uokr ? 20 : 19)));

			long pos = m_Stream.Position;

			int written = 0;

			m_Stream.Write( (ushort) 0 );

			for ( int i = 0; i < count; ++i )
			{
				Item child = (Item)items[i];

				if ( !child.Deleted && beholder.CanSee( child ) )
				{
					Point3D loc = child.Location;

					ushort cid = (ushort) child.ItemID;

					if ( cid > 0x3FFF )
						cid = 0x9D7;

					m_Stream.Write( (int) child.Serial );
					m_Stream.Write( (ushort) cid );
					m_Stream.Write( (byte) 0 ); // signed, itemID offset
					m_Stream.Write( (ushort) child.Amount );
					m_Stream.Write( (short) loc.m_X );
                    m_Stream.Write((short)loc.m_Y); 
                    if (uokr)
                        m_Stream.Write((byte)child.GridLocation);
					
					m_Stream.Write( (int) beheld.Serial );
					m_Stream.Write( (ushort) child.Hue );

					++written;
				}
			}

			m_Stream.Seek( pos, SeekOrigin.Begin );
			m_Stream.Write( (ushort) written );
		}
	}

Code: Alles auswählen

public sealed class VendorBuyContent : Packet
	{
		public VendorBuyContent( ArrayList list , bool uokr ) : base( 0x3c )
		{
            this.EnsureCapacity(list.Count * (uokr ? 20 : 19) + 5);

			m_Stream.Write( (short)list.Count );

			//The client sorts these by their X/Y value.
			//OSI sends these in wierd order.  X/Y highest to lowest and serial loest to highest
			//These are already sorted by serial (done by the vendor class) but we have to send them by x/y
			//(the x74 packet is sent in 'correct' order.)
			for ( int i = list.Count - 1; i >= 0; --i )
			{
				BuyItemState bis = (BuyItemState)list[i];
		
				m_Stream.Write( (int)bis.MySerial );
				m_Stream.Write( (ushort)(bis.ItemID & 0x3FFF) );
				m_Stream.Write( (byte)0 );//itemid offset
				m_Stream.Write( (ushort)bis.Amount );
				m_Stream.Write( (short)(i+1) );//x
                m_Stream.Write((short)1);//y
                
                if ( uokr )
                    m_Stream.Write((byte)0); //grid location

				m_Stream.Write( (int)bis.ContainerSerial );
				m_Stream.Write( (ushort)bis.Hue );
			}
		}
	}
Now, the biggest challenge of all: The login process
Many have reported that their "GM" char cannot login, but their "Player" char can. Well, the problem is not with your GM char, it's because you've probably used the GM char most often, and upon login the client requests to open the backpack automatically. This causes a significant problem, because by the time the server sends the content update packet (0x25), the client version information has not arrived yet. Therefore the server will have no clue if the new packet is to be used.

To get around this problem, you must use another dirty hack: wait until the client version arrives before sending the login packets. I haven't got the time to write this portion up. Right now I simply sends a client version request upon login, and use a timer delay of 1 second on the DoLogin method. This allows everyone with a fast connection to use the new client. But if the client version packet fails to arrive within 1 second, you cannot login to the server (very poorly handled...but the goal is to get everyone to use the new client so I can remove these dirty hacks later).

Hope these helps...

Regards,

PQ

pmouse

#13 Beitrag von pmouse » 23 Jul 2007 06:48

The exact reason for "no paperdoll", etc., is this:

upon login, client sends 0x5D packet, at this time, RunUO trys to connect the NetState object with the Mobile.
The problem occurs when the Mobile object gets moved out of the internal map, since a map update automatically puts all items on the mobile (and everything in range) to the Delta queue. The next core tick will result in a contentupdate sent to the netstate - at this time the client version information hasn't arrived yet, even the DoLogin() might not gets called if you are using a multicore processor.

The solution to this problem is to hold off setting the netstate object of the mobile until a client version has been received. This requires the server actively sending out a client version request and put everything into a timer delay loop, checking back on regular interval to see if client has replied. This also requires setting the 0xBD packet's "in game" property to false, and modify it's handler to accept null Mobile cases.

(Note that the BuffIcon support resets the 0xBD handler, you might want to modify that as well).

In the end, you want to achieve something like this:

Code: Alles auswählen

BEFORE:

23:56:09.9687: Client -> Server 0x5D (Length: 73)
        0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
       -- -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
0000   5D ED ED ED ED ...


23:56:10.5312: Server -> Client 0x1B (Length: 37)
        0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
       -- -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
0000   1B 00 00 00 3A ....


23:56:10.5468: Server -> Client 0x25 (Length: 21)
        0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
       -- -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
0000   25 40 01 12 21 0E F0 00  

23:56:10.5468: Server -> Client 0x40 (Length: 201)
        0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
       -- -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
0000   40 01 12 21 41 5C 27 24  ...
23:56:10.5468 is where the problem starts

AFTER:

Code: Alles auswählen

00:37:19.6562: Client -> Server 0x5D (Length: 73)
        0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
       -- -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
0000   5D ED ED ED ED ....


00:37:19.6562: Server -> Client 0xBD (Length: 3)
        0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
       -- -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
0000   BD 00 03                                           ...


00:37:19.6562: Client -> Server 0xBD (Length: 11)
        0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
       -- -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
0000   BD 00 0B 36 2E 30 2E 31  2E 37 00                  ...6.0.1.7.

RadstaR
Junior Mitglied
Beiträge: 70
Registriert: 29 Dez 2006 06:22
Kontaktdaten:

#14 Beitrag von RadstaR » 23 Jul 2007 09:24

pmouse > Thank you very very much for your help.

pmouse

#15 Beitrag von pmouse » 23 Jul 2007 18:56

My pleasure :)

Antworten