OK, so I'm trying to understand the UO protocol. I've found several pages describing the packets that make up the protocol, amongst others;
-
POLServer.com UO Packets Site - Home
-
Category:UO Protocol - Wolfpack Wiki
-
Ultima Online Protocol
The problem that all these pages have in common is that none of them describe the order in which the packets are sent. If anyone could give me a brief overview, that would be very much appreciated!
I've come as far as to intercept the client's seed packet and login packet (0x80), and now I'm wondering what packet to send in response. It seems that RunUO is sending the Game Server List packet (0xA8) for a successful login, and the Login Failed packet (0x82) for failed login.
Sending the Login Failed packet for a failed login works out perfectly for me - the messagebox is displayed in the client with the correct failure message. However, no matter what I do, I just cannot get the Server List packet to work! :\
If anyone could take a look at my code and spot any obvious flaws in the way I'm building my Server List packet, I'd greatly appreciate it!
Code:
if (Database.DoesAccountPassExist(AccountPwd))
{
using (AuthPacketOut OutPacket = new AuthPacketOut(AuthServerOpCode.ACCOUNT_LOGIN_OK))
{
OutPacket.Write((byte)0x5D); //Unknown
OutPacket.Write((ushort)RealmServerManager.Realms.Count);
for (int i = 0; i < RealmServerManager.Realms.Count; i++)
{
ServerInfo Realm = RealmServerManager.Realms[i];
OutPacket.Write((ushort)i);
OutPacket.WriteAsciiFixed(Realm.Name, 32);
OutPacket.Write((byte)Realm.FullPercent);
OutPacket.Write((sbyte)Realm.TimeZone);
OutPacket.Write((int)BitConverter.ToInt32(Realm.Address.Address.GetAddressBytes(), 0));
}
OutPacket.BaseStream.Position = 1;
OutPacket.Write((ushort)OutPacket.Length);
Client.Send(OutPacket);
if (Globals.IsDebugEnabled)
Console.WriteLine("Login OK - sent packet!\n");
}
}
PacketOut.cs:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using WowServ.Cryptography;
namespace WowServ.Network
{
public class PacketOut : BinaryWriter
{
PacketID m_ID;
public PacketOut(PacketID ID)
: base(new MemoryStream())
{
m_ID = ID;
}
/// <summary>
/// Writes a number of 0x00 byte values to the underlying stream.
/// </summary>
public void Fill(int length)
{
if (Position == Length)
{
this.BaseStream.SetLength(Length + length);
Seek(0, SeekOrigin.End);
}
else
Write(new byte[length], 0, length);
}
/// <summary>
/// Writes a fixed-length ASCII-encoded string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters.
/// </summary>
public void WriteAsciiFixed(string value, int size)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteAsciiFixed() with null value\n");
value = String.Empty;
}
byte[] buffer = Encoding.ASCII.GetBytes(value);
if (buffer.Length >= size)
Write(buffer, 0, size);
else
{
Write(buffer, 0, buffer.Length);
Fill(size - buffer.Length);
}
}
/// <summary>
/// Writes a C-style string to the stream
/// </summary>
/// <param name="str">String to write</param>
public virtual void WriteCString(string str)
{
Write(Encoding.ASCII.GetBytes(str));
Write('\0');
}
/// <summary>
/// Writes a BigInteger to the stream
/// </summary>
/// <param name="bigInt">BigInteger to write</param>
public virtual void WriteBigInt(BigInteger bigInt)
{
byte[] data = bigInt.GetBytes();
base.Write(data);
}
/// <summary>
/// Writes a BigInteger to the stream
/// </summary>
/// <param name="bigInt">BigInteger to write</param>
/// <param name="length">maximum numbers of bytes to write for th BigInteger</param>
public virtual void WriteBigInt(BigInteger bigInt, int length)
{
byte[] data = bigInt.GetBytes(length);
base.Write(data);
}
/// <summary>
/// Writes a BigInteger to the stream, while writing the length before it
/// </summary>
/// <param name="bigInt">BigInteger to write</param>
public virtual void WriteBigIntLength(BigInteger bigInt)
{
byte[] data = bigInt.GetBytes();
base.Write((byte)data.Length);
base.Write(data);
}
/// <summary>
/// Writes a BigInteger to the stream, while writing the length before it
/// </summary>
/// <param name="bigInt">BigInteger to write</param>
/// <param name="length">maximum numbers of bytes to write for th BigInteger</param>
public virtual void WriteBigIntLength(BigInteger bigInt, int length)
{
byte[] data = bigInt.GetBytes(length);
base.Write((byte)length);
base.Write(data);
}
/// <summary>
/// Writes a short to the stream
/// </summary>
/// <param name="val">the value to write</param>
public virtual void WriteShort(int val)
{
Write((short)val);
}
/// <summary>
/// Writes a short to the stream
/// </summary>
/// <param name="val">the value to write</param>
public virtual void WriteShort(uint val)
{
Write((short)val);
}
/// <summary>
/// Writes the supplied value to the stream for a specified number of bytes
/// </summary>
/// <param name="val">Value to write</param>
/// <param name="num">Number of bytes to write</param>
public virtual void Fill(byte val, int num)
{
for (int i = 0; i < num; ++i)
{
Write((byte)val);
}
}
/// <summary>
/// Fetches all the data in this Packet's stream
/// and returns them as a byte array for sending.
/// Should NOT be called before ALL data is written
/// to the packet!!
/// </summary>
/// <returns>A byte array of the packet's data.</returns>
public byte[] GetBytes()
{
MemoryStream MemStream;
MemStream = (MemoryStream)base.BaseStream;
return MemStream.ToArray();
}
public int Position
{
get { return (int)base.BaseStream.Position; }
set { base.BaseStream.Position = value; }
}
public int Length
{
get { return (int)base.BaseStream.Length; }
}
public UInt16 LengthUInt16
{
get { return (UInt16)base.BaseStream.Length; }
}
}
}
AuthPacketOut.cs:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WowServ.Network;
namespace ServeUO.Network
{
public class AuthPacketOut : PacketOut
{
public AuthPacketOut(AuthServerOpCode OpCode)
: base(new PacketID(OpCode))
{
Write((byte)OpCode);
}
public AuthPacketOut(RealmAuthProofErrorCodes OpCode)
: base(new PacketID(IDType.Authentication, (byte)OpCode))
{
}
}
}
My client is UO Gold, version 4.0.6a (Patch 6). Encryption was removed with UO RICE.
Thanks in advance!
PS: Sorry to mods if this was in the wrong forum section. Didn't quite know where to put this...