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!

Community Effort to Restore Patching

Simon Omega

Traveler
Praxiiz - Investigation of the handshake
http://www.runuo.com/community/threads/new-uo-patches-repository.533684/page-2#post-3971183

Morgan - Google Gurued the Communication
http://www.runuo.com/community/threads/new-uo-patches-repository.533684/page-2#post-3971231

I'm using the information from the the Rock Stars above, along with my own investigation data (OllyDBG, IDA, Wireshark, RawCap) to write a python wrapper for the UOPatch application and start the patch process.

I know everything around here is .Net and C#. But I am at home in python. And if it becomes functional python is easy to cross platform on servers and tie into web applications.

Anyone that wants to help feel free to step in.
Once I have more than just "Waiting to Download File. Server Refused to Send File.", I'll put the code on github.
 

Morgan

Sorceror
I'm almost done writing multi-threaded server (C# console). I haven't wrote anything (C#) in 6 years so code quality will be shameful tho. :p
 

Simon Omega

Traveler
Morgan, I've been busy with work all day. But thanks to you once I get the time all I need is the file block transfer communication and I'll have a CLI server that can run on any OS hosting the patches. It will need cleaned up, a lot of my work is sequential. So put some threads in the python script. I think to close they out you let the Function End or Class become garbage collected. But C is telling me that just isn't right. But haven't found lose thread yet. I'll read more about python threads when I have the time.

I'm betting that UOPatch.exe may actually not request the files be transfered if it detects the patch exists in 'patch' by simply not sending the request for that file. We may be able to create and interface that users set their version, and specify the desired version. Feed it a repository/archive URL. Download it out side of the UOPatch. This could lead to some interesting patch management interfaces and also multiple client versions... If we can get a front-end/wrapper to create version trees.

I just need to verify it if a 'skip download' type flag is used in the communication, or if UOPatch.exe just skips over the file request (It requests every file by name and size anyway, so I am betting that it would just not request that file).

But first things first. We need to get people to a point they can at least patch.
If you run into something shout. I'm not a C# pro, but I can help you look at it.

I even installed C# Express just for this. :)
 

Simon Omega

Traveler
Ding! Have a little Cleanup to do but it is Alpha Worthy enough for others to fork, or me turn it in to a proper Class when I get the time.

At the end when the UOPatch.exe sends \x00\x00\x00\x03 about 8 times.
If you don't handel it then and send a complete/finish (I think it's \x00\x00\x00\x00)
It will start patching all over again.
LOL, I patched 7.0.11.0 over 7.0.11.0 because I didn't handle it soon enough.

This should really be considered a server replacement. But I'll make it run locally until you finish yours Morgan.

 

Attachments

  • ApplyingPatches.png
    110.1 KB · Views: 304

Simon Omega

Traveler
github for the Python Script.
As you can see it is very quick and hackish for now, but it works.
https://github.com/SimonOmega/UOPatchServer

Lessons Learned:

If the file is located in the patch directory already, and the file size matches the size sent by the server. UOPatch.exe will skip the download request for the file and apply the patch from the local directory. Just as it always did, this requires no extra command from the server.

This means we could load all the patches into patch, have the server serve out of that directory. Server Passes the PatchList to UOPatch.exe and it will start installing the patches in the order they were received.

UOPatch.exe relies on the server to build a list of patches it needs based off the version string UPPatch.exe supplies. My script does not account for this yet and just sends all the files to UOPatch.exe. UOPatch.exe will apply the same patch over it self. So for my script you have to make sure you place only the files you need in the 'archive' folder it hosts.


The communication chain:
<UOPatch.exe Connects>
UOPatch send HelloRequest
byte[4] 0x00 0x00 0x00 0x15​
Server responds with control comands
byte[4] 0x00 0x00 0x00 0x01 (Possibly Protocol?)​
byte[4] 0x00 0x00 0x00 0x01​
0x01 designates self as server.​
I think 0x00 start the PatchServerTransfer and those packets follow.​
(If the above is 0x01)​
byte[4] 0x00 0x00 0x00 0x01​
0x01 designates ready for requests.​
I think 0x02 designates a NoticeBlock and those packets follow.​
UOPatch sends PatchListRequest (It relies on the server to calculate the required patches)
byte[4] 0x00 0x00 0x00 0x01 (I think of this as Step 1)​
byte[4] unsigned int Length_of_Version_String​
Contains the Length of the Version String.​
byte[Length_of_Version_String] ascii string Version​
This is a version string like those seen in the patch file name (minus the numbers) and as in the .pat files (minus) the numbers.​
byte[4] unsigned int Patch_Number​
The (Patch ##) when you run the client.​
Server Sends a PatchListData for each file UOPatch Needs.
byte[4] unsigned int Length_of_File_Name​
byte[Length_of_File_Name] ascii string File_Name​
byte[4] unsigned int Size_in_Bytes_of_File​
Once each PatchListData is sent the Server sends a Complete/Successful
byte[4] 0x00 0x00 0x00 0x00​
UOPatch sends PatchDataRequest (If the patch does not exist in the patch folder with proper file size)
byte[4] 0x00 0x00 0x00 0x02 (I think of this as Step 2)​
byte[4] unsigned int Length_of_File_Name​
byte[Length_of_File_Name] ascii string File_Name​
byte[4] always equal to 0x00 0x00 0x00 0x00 if file is not in patch folder.​
I suspect it is used to resume partially downloaded files, but not tested.​
I would assume it hold a byte count for the partially downloaded file.​
Server Sends a PatchData
byte[4] unsigned int Length_of_File_Name​
byte[Length_of_File_Name] ascii string File_Name​
byte[4] unsigned int Size_in_Bytes_of_File​
Server Open File as Binary and reads X bytes at a time.​
While File is not EOF​
byte[4] unsigned int Number_of_Bytes_Read​
byte[Number_of_Bytes_Read] the bytes that were read​
At File EOF the Server sends a Complete/Successful​
byte[4] 0x00 0x00 0x00 0x00​
If additional patches are needed UOPatch sends PatchDataRequest for each file.
Server responds to each PatchDataRequest with a PatchData.

Once UOPatch has all the patches at the right sizes it iterates over the PatchListData it received installing each IN THE ORDER THEY WERE RECEIVED.

Once UOPatch applies all the patches it sends upto 8 messages.
byte[4] 0x00 0x00 0x00 0x03 (I think of this as Step 3)​
The Server should respond with a Complete/Successful before all messages are sent.
byte[4] 0x00 0x00 0x00 0x00​
If UOPatch does not receive this packet it starts the whole process over again but reports the new version.

And thats how an RTPatch application communicates.
I need some sleep.
 

Praxiiz

Sorceror
Good job on this.


Before I saw your post, I spent some of today working on replicating the patcher's functionality. What I learned was that the patcher just basically transfers patch files from the server and then calls the Patchw32.dll.

I coded up a quick console app that calls the patcher on a specific patch file and lists the operations it would perform.

Aside from the patching functionality which you've already accomplished, this simple utility can tell you what files will be altered during a given patch. There are a few params it can be given, the /l just tells it to list what it would do.

This could be a standalone app that would be run from your client folder without a server/client setup.

Here's a sample run:


Calling RT Patch with: "aos2d_win32_4-0-10a.rtp" "C:\games\Electronic Arts" /l

ID: 12 Param: 268630664
ID: 12 Param: 268607680
ID: 12 Param: 268607680
ID: 6 Param: 268629056
Number of PatchFile Entries: 24
ID: 9 Param: 268607680
Progress Message: Directory Information
---------------------

ID: 9 Param: 268607680
Progress Message: Update Directory: C:\games\Electronic Arts

ID: 9 Param: 268607680
Progress Message:

ID: 9 Param: 268607680
Progress Message:

ID: 9 Param: 268607680
Progress Message: Patch Program Flag Settings
---------------------------

ID: 9 Param: 268607680
Progress Message: Backup Affected Files : OFF

ID: 9 Param: 268607680
Progress Message: Ignore Missing Files : OFF

ID: 9 Param: 268607680
Progress Message: Path Searching : ON

ID: 9 Param: 268607680
Progress Message: Sub Directory Searching : ON

ID: 9 Param: 268607680
Progress Message: Print Error Messages : ON

ID: 9 Param: 268607680
Progress Message: Global Patch Undo on Error : OFF

ID: 9 Param: 268607680
Progress Message: Information Display Level : VERBOSE

ID: 9 Param: 268607680
Progress Message: Action Files
------ ------

ID: 9 Param: 268607680
Progress Message: MODIFY art.mul

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY artidx.mul

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY client.exe

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY Cliloc.cht

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY Cliloc.enu

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY Cliloc.jpn

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY Cliloc.kor

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY gumpart.mul

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY gumpidx.mul

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY options.cht

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY options.deu

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY options.enu

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY options.esp

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY options.fra

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY options.JPN

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY options.kor

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY OPTNUOTD.CHT

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY OPTNUOTD.DEU

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY OPTNUOTD.ENU

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY OPTNUOTD.ESP

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY OPTNUOTD.FRA

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY OPTNUOTD.JPN

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY OPTNUOTD.KOR

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
ID: 9 Param: 268607680
Progress Message: MODIFY tiledata.mul

ID: 7 Param: 268615200
ID: 9 Param: 268607680
Progress Message:

ID: 8 Param: 0
RT Patch Returned! 0
 

Simon Omega

Traveler
I looked at Patchw32.dll
I ripped all the function names out of it... But I can't find them.
Would it help if I got the function names for you again, or by the looks of things do you already have them?
 

Praxiiz

Sorceror
The patcher only uses one function. The patch method hasn't changed since the original patcher was released, which is where I started. Interestingly enough, the original patchw32.dll only had one function. Newer versions of the dll have added more functions, but the UO patcher doesn't use them. I have a handler for the method including the callbacks. My next step is applying a series of patches and verify them using the client repository.

The interesting thing is that the UOPatcher doesn't have much in the way of error handling if the patcher fails.
 

Simon Omega

Traveler
Just received a message from @Obsidian-Fire saying the Patcher was sending over an empty byte packet. I was hoping I would not, but I might have to adjust for multiple client versions. I haven't seen one of those since I was trying to figure out the communication sequence.

I only tested on 7 series clients so far.
 

Simon Omega

Traveler
While I had a moment here I tested the follwoing:
  1. UOPatch I confirmed only applies patches who names it receives from the server. I placed a patch in the patch dir that was not in the list from the server and it never touched it.
  2. It actually does bit/byte/block level updates sometimes. I deleted client.exe and it could not apply a patch. It could not create it from the data in the patch. It was looking for the existing client.exe to alter it.
Not sure if that helps anyone, but I was curious.

And like you said, the only error I got was Patch failed.
 

Simon Omega

Traveler
When the patch failed I got a file in the client directory. It's binary, but I can make out some code, comments and text. It seems related to the client.exe.

The file was RTA04320

Not sure if that helps you. Not sure what all is in it...
 

Morgan

Sorceror
Compile, Throw patch files into Patches folder. Start the server.
Tested on 7.X.X.X and it worked nicely.

It automaticly serves correct patch files for given UO version and should deal nicely with double version patch files (not tested).

This is for home use only. Its in no way production code...

Code:
using System;
using System.Collections;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
 
namespace UOPatchServer
{
 
    public class SynchronousSocketListener
    {
        private const int PortNum = 8888;
 
        public static int Main(String[] args)
        {
            StartListening();
            return 0;
        }
 
        public static void StartListening()
        {
 
            var patchList = PatchData.Table;
 
            var connectionPool = new ClientConnectionPool();
 
            var clientTask = new ClientService(connectionPool);
 
            clientTask.Start();
            IPAddress ip = IPAddress.Parse("127.0.0.1");
            var listener = new TcpListener(ip, PortNum);
            try
            {
                listener.Start();
 
                int clientNbr = 0;
 
                Console.WriteLine("Waiting for a connection...");
                while (true)
                {
                    TcpClient handler = listener.AcceptTcpClient();
 
                    Console.WriteLine("Client#{0} accepted!", ++clientNbr);
 
                    connectionPool.Enqueue(new ClientHandler(handler, patchList));
 
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
 
            Console.WriteLine("\nHit enter to continue...");
            Console.Read();
        }
    }
 
    public class ClientConnectionPool
    {
        private readonly Queue _syncdQ = Queue.Synchronized(new Queue());
 
        public int Count
        {
            get { return _syncdQ.Count; }
        }
 
        public object SyncRoot
        {
            get { return _syncdQ.SyncRoot; }
        }
 
        public void Enqueue(ClientHandler client)
        {
            _syncdQ.Enqueue(client);
        }
 
        public ClientHandler Dequeue()
        {
            return (ClientHandler)(_syncdQ.Dequeue());
        }
    }
 
    public class ClientService
    {
        private const int NumOfThread = 10;
 
        private readonly ClientConnectionPool _connectionPool;
        private readonly Thread[] _threadTask = new Thread[NumOfThread];
        private bool _continueProcess;
 
        public ClientService(ClientConnectionPool connectionPool)
        {
            _connectionPool = connectionPool;
        }
 
        public void Start()
        {
            _continueProcess = true;
            for (int i = 0; i < _threadTask.Length; i++)
            {
                _threadTask[i] = new Thread(Process);
                _threadTask[i].Start();
            }
        }
 
        private void Process()
        {
            while (_continueProcess)
            {
                ClientHandler client = null;
                lock (_connectionPool.SyncRoot)
                {
                    if (_connectionPool.Count > 0)
                        client = _connectionPool.Dequeue();
                }
                if (client != null)
                {
                    client.Process();
                    if (client.Alive)
                        _connectionPool.Enqueue(client);
                }
 
                Thread.Sleep(100);
            }
        }
 
        public void Stop()
        {
            _continueProcess = false;
            foreach (Thread t in _threadTask)
            {
                if (t != null && t.IsAlive)
                    t.Join();
            }
 
            while (_connectionPool.Count > 0)
            {
                ClientHandler client = _connectionPool.Dequeue();
                client.Close();
                Console.WriteLine("Client connection is closed!");
            }
        }
    }
 
    public class ClientHandler
    {
        private readonly byte[] _bytes;
        private readonly TcpClient _clientSocket;
        private readonly DataTable _table;
        private readonly NetworkStream _networkStream;
        private readonly StringBuilder _sb = new StringBuilder();
 
        public ClientHandler(TcpClient clientSocket, DataTable table)
        {
            clientSocket.ReceiveTimeout = 100;
            _clientSocket = clientSocket;
            _table = table;
            _networkStream = clientSocket.GetStream();
            _bytes = new byte[clientSocket.ReceiveBufferSize];
            Alive = true;
            _table = table;
        }
 
        public bool Alive { get; private set; }
 
        public void Process()
        {
            try
            {
                int bytesRead = _networkStream.Read(_bytes, 0, _bytes.Length);
                if (bytesRead > 0)
                    _sb.Append(Encoding.ASCII.GetString(_bytes, 0, bytesRead));
                else
                    ProcessDataReceived();
            }
            catch (IOException)
            {
                ProcessDataReceived();
            }
            catch (SocketException)
            {
                _networkStream.Close();
                _clientSocket.Close();
                Alive = false;
                Console.WriteLine("Conection is broken!");
            }
        }
 
        // Process()
 
        private void ProcessDataReceived()
        {
            if (_sb.Length > 0)
            {
                switch (IdentifyRequest())
                {
                    case 1:
 
                        #region NoticeReply
                        var header = new byte[] { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02 };
                        var website = Encoding.ASCII.GetBytes("http://www.moongatereborn.pl" + "\0");
                        var websitelenght = BitConverter.GetBytes(website.Length).Reverse().ToArray();
 
                        try
                        {
                            Send(header);
                            Send(websitelenght);
                            Send(website);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
                        _sb.Clear();
                        break;
 
                        #endregion
 
                    case 2:
 
                        #region PatchListReply
 
                        Match match = Regex.Match(_sb.ToString(), @"(Win32_[A-Za-z]+)", RegexOptions.IgnoreCase);
                        string version = match.Groups[1].Value;
                        int id = _sb[_sb.Length - 1];
                        DataTable avaliablePatches = _table.Clone();
                        string query = "Version = '" + version + "' AND PatchID > " + id.ToString(CultureInfo.InvariantCulture);
                        DataRow[] result = _table.Select(query);
                        var endBytes = new byte[] { 0x00, 0x00, 0x00, 0x00 };
                        foreach (DataRow row in result)
                        {
                            avaliablePatches.ImportRow(row);
                        }
 
                        for (int i = 0; i < avaliablePatches.Rows.Count; i++)
                        {
                            var fileName = Encoding.ASCII.GetBytes(Path.GetFileName(avaliablePatches.Rows[i].ItemArray[2].ToString()));
                            var fileNameLenght = BitConverter.GetBytes(fileName.Length).Reverse().ToArray();
                            var fileSize = BitConverter.GetBytes((int)avaliablePatches.Rows[i].ItemArray[3]).Reverse().ToArray();
                            try
                            {
                                Send(fileNameLenght);
                                Send(fileName);
                                Send(fileSize);
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }
                        }
 
                        try
                        {
                            Send(endBytes);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
                        _sb.Clear();
                        break;
 
                        #endregion
 
                    case 3:
 
                        #region PatchDataReply
 
                        int filenameLength = _sb[7];
                        var requestedPatchFile = _sb.ToString().Substring(8, filenameLength);
                        Match identifyFileName = Regex.Match(_sb.ToString(), @"([A-Za-z0-9_\-.]+win32[A-Za-z0-9_\-.]+)");
                        if (identifyFileName.Success)
                        {
                            requestedPatchFile = identifyFileName.Groups[1].Value;
                        }
 
                        try
                        {
                            for (int i = 0; i < _table.Rows.Count; i++)
                            {
                                if (Path.GetFileName(_table.Rows[i].ItemArray[2].ToString()) == requestedPatchFile)
                                {
                                    byte[] file =Encoding.ASCII.GetBytes(Path.GetFileName(_table.Rows[i].ItemArray[2].ToString()));
                                    byte[] fileNameLenght = BitConverter.GetBytes(requestedPatchFile.Length).Reverse().ToArray();
                                    byte[] fileSize = BitConverter.GetBytes((int) _table.Rows[i].ItemArray[3]).Reverse().ToArray();
                                    Send(fileNameLenght);
                                    Send(file);
                                    Send(fileSize);
                                    break;
                                }
                            }
 
 
                            string path = @".\Patches\" + requestedPatchFile;
                            using (
                                var br =
                                    new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)))
                            {
                                byte[] buffer = br.ReadBytes(1500);
                                while (buffer.Length > 0)
                                {
                                    Send(BitConverter.GetBytes(buffer.Length).Reverse().ToArray());
                                    Send(buffer, false);
                                    buffer = br.ReadBytes(2048);
 
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
                        _sb.Clear();
 
                        try
                        {
                            var sendBytes = new byte[] {0x00, 0x00, 0x00, 0x00};
                            Send(sendBytes);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
 
                        _sb.Clear();
                        break;
 
                        #endregion
 
                    default:
                        try
                        {
                            var sendBytes = new byte[] { 0x00, 0x00, 0x00, 0x00 };
                            Send(sendBytes);
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }
                        _sb.Clear();
                        break;
                }
            }
        }
 
        public void Close()
        {
            _networkStream.Close();
            _clientSocket.Close();
        }
 
        private int IdentifyRequest()
        {
 
            var hello = new byte[] { 0x00, 0x00, 0x00, 0x15 };
            var zeroone = new byte[] { 0x00, 0x00, 0x00, 0x01 };
            var zerotwo = new byte[] { 0x00, 0x00, 0x00, 0x02 };
            byte[] packet = Encoding.ASCII.GetBytes(_sb.ToString());
            var packetHeader = packet.Take(4).ToArray();
            if (packet.Length == 4 && packet.SequenceEqual(hello)) //Hello Packet
            {
                return 1;
            }
 
            if (packet.Length > 4)
            {
                if (packetHeader.SequenceEqual(zeroone))//PatchListRequest Packet
                    return 2;
                if (packetHeader.SequenceEqual(zerotwo))//PatchDataRequest Packet
                    return 3;
            }
            return 0;
        }
 
        private void Send(byte[] bytes, bool log = true)
        {
            _networkStream.Write(bytes, 0, bytes.Length);
        }
    }
 
    public class PatchData
    {
        public static DataTable Table = GetTable();
 
        private static DataTable GetTable()
        {
            var unsortedPatchTable = new DataTable();
            unsortedPatchTable.Columns.Add("Version", typeof(string));
            unsortedPatchTable.Columns.Add("PatchID", typeof(int));
            unsortedPatchTable.Columns.Add("Filename", typeof(string));
            unsortedPatchTable.Columns.Add("Size", typeof(int));
            string path = Directory.GetCurrentDirectory() + @"\Patches";
 
            if (Directory.Exists(path))
            {
                string[] fileEntries = Directory.GetFiles(path, "*.pat");
                foreach (string fileName in fileEntries)
                {
                    long sizeRpt = new FileInfo(fileName.Replace(".pat", ".rtp")).Length;
                    long sizePat = new FileInfo(fileName).Length;
                    try
                    {
                        using (var sr = new StreamReader(fileName))
                        {
                            string line;
                            while ((line = sr.ReadLine()) != null)
                            {
                                string[] version = line.Split(Convert.ToChar(" "));
                                unsortedPatchTable.Rows.Add(version[0], version[1], fileName.Replace(".pat", ".rtp"), sizeRpt);
                                unsortedPatchTable.Rows.Add(version[0], version[1], fileName, sizePat);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
 
            }
            else
            {
                Console.WriteLine("{0} is not a valid file or directory.", path);
            }
 
            var sortedPatchTable = unsortedPatchTable.AsEnumerable().OrderBy(r => r.Field<string>("Version")).ThenBy(r => r.Field<int>("PatchID")).ThenBy(r => r.Field<string>("Filename")).CopyToDataTable();
 
            for (int i = 0; i < unsortedPatchTable.Rows.Count; i++)
            {
                string test = sortedPatchTable.Rows[i].ItemArray[0] + " | " + sortedPatchTable.Rows[i].ItemArray[1] + " | " +
                              sortedPatchTable.Rows[i].ItemArray[2] + " | " + sortedPatchTable.Rows[i].ItemArray[3];
                Console.WriteLine(test);
            }
            return sortedPatchTable;
        }
    }
}

EDIT: Simon Omega. That text field in NoticePacket is website address that UOPatch.exe displays (it will write it to config file for l8r use)
 

Simon Omega

Traveler
I've had to be away. Problems here. Morgan if you need me I messaged you a contact information for me. But I have to duck out for a while. I'll see if my brother can contribute some patches.
 

ivhazu84

Traveler
Hi,

many compliment for the work but unfortunately I'm not so expert and I'm not able to set-up it.
I install the UO Classic Client 7_0_15_1.

I create an archive folder in C:\ with all patch ut to 7_0_24_2 and in the C:\ I have the UOPatchServer.py script.

I run the script and here the first problem:

Code:
127.0.0.1 listening on 8888
Socket: Waiting on Connections.
PatchHandler for %s is using directory %s ('127.0.0.1', '/home\\simonomega\\archive')
Traceback (most recent call last):
  File "C:\UOPatchServer.py", line 155, in <module>
    _CLIENTS[ip_port[0]] = PatchHandler(connection, ip_port[0], _PATCH_DIR)
  File "C:\UOPatchServer.py", line 26, in __init__
    for a_file in listdir(_PATCH_DIR):
FileNotFoundError: [WinError 3] Impossibile trovare il percorso specificato: '/home\\simonomega\\archive\\*.*'
>>>

I change the _PATCH_DIR at line 134 and here the second problem.

Code:
127.0.0.1 listening on 8888
Socket: Waiting on Connections.
PatchHandler for %s is using directory %s ('127.0.0.1', '.\\archive')
PatchHandler for 127.0.0.1 is using file list ['uosa_win32_7-0-16-0.pat', 'uosa_win32_7-0-16-0.rtp', 'uosa_win32_7-0-16-1.pat', 'uosa_win32_7-0-16-1.rtp', 'uosa_win32_7-0-16-3.pat', 'uosa_win32_7-0-16-3.rtp', 'uosa_win32_7-0-17-0.pat', 'uosa_win32_7-0-17-0.rtp', 'uosa_win32_7-0-18-0.pat', 'uosa_win32_7-0-18-0.rtp', 'uosa_win32_7-0-19-0.pat', 'uosa_win32_7-0-19-0.rtp', 'uosa_win32_7-0-19-1.pat', 'uosa_win32_7-0-19-1.rtp', 'uosa_win32_7-0-20-0.pat', 'uosa_win32_7-0-20-0.rtp', 'uosa_win32_7-0-21-1.pat', 'uosa_win32_7-0-21-1.rtp', 'uosa_win32_7-0-21-2.pat', 'uosa_win32_7-0-21-2.rtp', 'uosa_win32_7-0-22-0.pat', 'uosa_win32_7-0-22-0.rtp', 'uosa_win32_7-0-22-8.pat', 'uosa_win32_7-0-22-8.rtp', 'uosa_win32_7-0-23-0.pat', 'uosa_win32_7-0-23-0.rtp', 'uosa_win32_7-0-23-1.pat', 'uosa_win32_7-0-23-1.rtp', 'uosa_win32_7-0-24-2.pat', 'uosa_win32_7-0-24-2.rtp']
Socket: Waiting on Connections.>> 127.0.0.1 sent Client Hello/Request (b'\x00\x00\x00\x15')
 
Server Sending: Protocol, Command, and UseSelf
>> 127.0.0.1 sent Version Information ({'addon': 'Win32_UOSA', 'version': 51, 'namelen': 10})
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-0.pat', 'namelen': b'\x00\x00\x00\x17'}
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x03\x19<\x98', 'working_filename': b'uosa_win32_7-0-16-0.rtp', 'namelen': b'\x00\x00\x00\x17'}>> 127.0.0.1 sent a Request for File uosa_win32_7-0-16-0.pat ({'working_filename': 'uosa_win32_7-0-16-0.pat', 'namelen': 23, 'padding': 0})
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-1.pat', 'namelen': b'\x00\x00\x00\x17'}>> 127.0.0.1 sent Version Information ({'addon': 'Win32_UOSA', 'version': 51, 'namelen': 10})Exception in thread Thread-4:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 68, in commands
    if file == None:
NameError: global name 'file' is not defined
 
 
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\t\xde\x9c', 'working_filename': b'uosa_win32_7-0-16-1.rtp', 'namelen': b'\x00\x00\x00\x17'}>> 127.0.0.1 sent a Request for File uosa_win32_7-0-16-1.pat ({'working_filename': 'uosa_win32_7-0-16-1.pat', 'namelen': 23, 'padding': 0})<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-0.pat', 'namelen': b'\x00\x00\x00\x17'}
 
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-3.pat', 'namelen': b'\x00\x00\x00\x17'}Exception in thread Thread-6:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 68, in commands
    if file == None:
NameError: global name 'file' is not defined
 
>> 127.0.0.1 sent Version Information ({'addon': 'Win32_UOSA', 'version': 51, 'namelen': 10})<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x03\x19<\x98', 'working_filename': b'uosa_win32_7-0-16-0.rtp', 'namelen': b'\x00\x00\x00\x17'}
 
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x01\xe2', 'working_filename': b'uosa_win32_7-0-16-3.rtp', 'namelen': b'\x00\x00\x00\x17'}>> 127.0.0.1 sent a Request for File uosa_win32_7-0-16-0.pat ({'working_filename': 'uosa_win32_7-0-16-0.pat', 'namelen': 23, 'padding': 0})<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-0.pat', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-1.pat', 'namelen': b'\x00\x00\x00\x17'}
 
 
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-17-0.pat', 'namelen': b'\x00\x00\x00\x17'}>> 127.0.0.1 sent Version Information ({'addon': 'Win32_UOSA', 'version': 51, 'namelen': 10})Exception in thread Thread-8:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 68, in commands
    if file == None:
NameError: global name 'file' is not defined
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x03\x19<\x98', 'working_filename': b'uosa_win32_7-0-16-0.rtp', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\t\xde\x9c', 'working_filename': b'uosa_win32_7-0-16-1.rtp', 'namelen': b'\x00\x00\x00\x17'}
 
 
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x03\xbec\xd8', 'working_filename': b'uosa_win32_7-0-17-0.rtp', 'namelen': b'\x00\x00\x00\x17'}>> 127.0.0.1 sent a Request for File uosa_win32_7-0-16-0.rtp ({'working_filename': 'uosa_win32_7-0-16-0.rtp', 'namelen': 23, 'padding': 0})<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-0.pat', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-1.pat', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-3.pat', 'namelen': b'\x00\x00\x00\x17'}
 
 
 
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-18-0.pat', 'namelen': b'\x00\x00\x00\x17'}Exception in thread Thread-10:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 68, in commands
    if file == None:
NameError: global name 'file' is not defined
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x03\x19<\x98', 'working_filename': b'uosa_win32_7-0-16-0.rtp', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\t\xde\x9c', 'working_filename': b'uosa_win32_7-0-16-1.rtp', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x01\xe2', 'working_filename': b'uosa_win32_7-0-16-3.rtp', 'namelen': b'\x00\x00\x00\x17'}
 
 
 
<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x0e\x11)', 'working_filename': b'uosa_win32_7-0-18-0.rtp', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-1.pat', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-16-3.pat', 'namelen': b'\x00\x00\x00\x17'}<< Sever sent 127.0.0.1 PatchListData entry {'working_filelen': b'\x00\x00\x00\r', 'working_filename': b'uosa_win32_7-0-17-0.pat', 'namelen': b'\x00\x00\x00\x17'}
 
Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 128, in run
    self.requesthandler()
  File "C:\UOPatchServer.py", line 92, in requesthandler
    self.data = self.connection.recv(self.buffer_len)
ConnectionResetError: [WinError 10054] Connessione in corso interrotta forzatamente dall'host remoto
 
 
 
Exception in thread Thread-3:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 63, in commands
    self.connection.send(file_listing['namelen'])
ConnectionResetError: [WinError 10054] Connessione in corso interrotta forzatamente dall'host remoto
 
Exception in thread Thread-9:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 63, in commands
    self.connection.send(file_listing['namelen'])
ConnectionResetError: [WinError 10054] Connessione in corso interrotta forzatamente dall'host remoto
 
Exception in thread Thread-7:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 63, in commands
    self.connection.send(file_listing['namelen'])
ConnectionResetError: [WinError 10054] Connessione in corso interrotta forzatamente dall'host remoto
 
Exception in thread Thread-5:
Traceback (most recent call last):
  File "C:\Program Files\Python33\lib\threading.py", line 901, in _bootstrap_inner
    self.run()
  File "C:\Program Files\Python33\lib\threading.py", line 858, in run
    self._target(*self._args, **self._kwargs)
  File "C:\UOPatchServer.py", line 63, in commands
    self.connection.send(file_listing['namelen'])
ConnectionResetError: [WinError 10054] Connessione in corso interrotta forzatamente dall'host remoto

Can someone help me?

Thanks,

Andrea

I'm using Python 3.3.3 on Windows 7 Home premium x64
 

jargo2000

Sorceror
Is there any updates or have things stalled for know. Looks like good progress so far. I am a crappy hack of a programmer or I would lend aid.
 

Simon Omega

Traveler
Sorry, Divorce, Single Dad, New Job, Life... I thought these efforts had died off as well.
If there is still interest I can try to simplify the Python script I made: http://www.runuo.com/community/resources/uo-patch-server.96/
I can even try to implement patch continuation (I think I found out how that works in the block count).

If anyone still needs this let me know. I'll look at it.

Been beta testing some games, playing some games, and working on a creation Sareus and I have going on.
I can make some time.
 
Top