Go Back   RunUO - Ultima Online Emulation > RunUO > Core Modifications > Network Modifications

Network Modifications This forum is for modifications to the networking code of RunUO

Reply
 
Thread Tools Display Modes
Old 02-25-2006, 01:51 PM   #1 (permalink)
Forum Expert
 
arul's Avatar
 
Join Date: Jan 2005
Location: Hic sunt leones ...
Age: 21
Posts: 1,289
Send a message via MSN to arul
Post Compressed gumps [0xDD]

5.x.x client introduces a new packet for Zlib compressed gumps.
Those gump packets are sometimes 5 times smaller then uncompressed ones.

Keep in mind if you modify the core you will lost script support here.

This requires a quite extensive core modification and you will not be able to do it without basic knowledge of C# and RunUO architecture. I cannot fully support this modification, thus if you have no clue what to do disregard this post, thanks.

The architeture of the new compressed gump packet is following:

Code:
BYTE PacketId (0xDD)
DWORD GumpSerial
DWORD GumpTypeID
DWORD X
DWORD Y
 
DWORD LengthOfTheCompressedLayout
DWORD LengthOfTheUncompressedLayout
BYTE[*] CompressedLayout
 
DWORD TextLines
DWORD LengthOfTheCompressedText
DWORD LengthOfTheUncompressedText
BYTE[*] CompressedText
First off we need to make the Zlib part of the core.

Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace Server
{
    public enum ZLibCompressionLevel
    {
        Z_BEST_COMPRESSION = 9,
        Z_BEST_SPEED = 1,
        Z_DEFAULT_COMPRESSION = -1,
        Z_NO_COMPRESSION = 0
    }
    public enum ZLibError
    {
        Z_BUF_ERROR = -5,
        Z_DATA_ERROR = -3,
        Z_ERRNO = -1,
        Z_MEM_ERROR = -4,
        Z_NEED_DICT = 2,
        Z_OK = 0,
        Z_STREAM_END = 1,
        Z_STREAM_ERROR = -2,
        Z_VERSION_ERROR = -6
    } 
    class ZLib
    {
        public ZLib()
        {
        }
        [DllImport("zlib")]
        public static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
        [DllImport("zlib")]
        public static extern ZLibError compress2(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibCompressionLevel level);
 
        [DllImport("zlib")]
        public static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen);
    }
}
Now, we want to have ability to send both compressed and uncompressed gump depending on the client version.
To do that, open Packets.cs and put in this interface.
Code:

public interface IGumpPacket
{
void AppendLayout (bool val);
void AppendLayout (int val);
void AppendLayout (byte[] buffer);
void AppendLayoutNS (int val);
int TextEntries { get; set; }
int Switches { get; set; }
}
Now find the definition of DisplayGumpFast packet and let it inhert the new interface
Code:

    public sealed class DisplayGumpFast : Packet, IGumpPacket
Easy, isn't it ?

Now the time has come to assembly all new packet, let's call it DisplayCompressedGumpFast.

Code:
public sealed class DisplayCompressedGumpFast : Packet, IGumpPacket
{
static DisplayCompressedGumpFast()
     {
          m_True = Gump.StringToBuffer(" 1");
          m_False = Gump.StringToBuffer(" 0");
          m_Buffer = new byte[0x400]; 
   m_Buffer[0] = 0x20;
}

public DisplayCompressedGumpFast(Gump g)
          : base(0xDD)
     {
          m_Layout = newPacketWriter();
          m_Text = newPacketWriter();
          EnsureCapacity(0x400);
          m_Stream.Write(g.Serial);
          m_Stream.Write(g.TypeID);
          m_Stream.Write(g.X);
          m_Stream.Write(g.Y);
     }
 
public void AppendLayout(bool val)
     {
          AppendLayout(val ? m_True : m_False);
     }
 
public void AppendLayout(int val)
     {
   string text = val.ToString();
   int num = System.Text.Encoding.ASCII.GetBytes(text, 0, text.Length, m_Buffer, 1) + 1;
          m_Layout.Write(m_Buffer, 0, num);
          m_LayoutLength += num;
     }
 
public void AppendLayout(byte[] buffer)
     {
   int num = buffer.Length;
          m_Layout.Write(buffer, 0, num);
          m_LayoutLength += num;
     }
 
public void AppendLayoutNS(int val)
     {
   string text = val.ToString();
   int num = System.Text.Encoding.ASCII.GetBytes(text, 0, text.Length, m_Buffer, 1);
          m_Layout.Write(m_Buffer, 1, num);
          m_LayoutLength += num;
     }
 
public void Output()
     {
byte[] buffer = new byte[BufferSize];
   int length = buffer.Length;
 

   if (
        ZLib.compress2(
                    buffer,
             ref length,
                    m_Layout.ToArray(),
                    m_LayoutLength,
             ZLibCompressionLevel.Z_BEST_SPEED) == ZLibError.Z_OK
               )
               {
 
                    WriteBuffer(
                    buffer, 
             ref length, 
                    m_LayoutLength
                    );
 

        if (
        ZLib.compress2(
                    buffer,
             ref length,
                    m_Text.ToArray(),
                    m_TextLength,
             ZLibCompressionLevel.Z_BEST_SPEED) == ZLibError.Z_OK
                    )
               {
 
                    m_Stream.Write(m_TextLines);

             WriteBuffer(
                    buffer, 
             ref length, 
                    m_TextLength
                    );

       }
          }
     }
 
public void WriteBuffer(byte[] buffer, refint length, int contentLength)
     {
         m_Stream.Write(length + 4); 
  m_Stream.Write(contentLength);
  m_Stream.Write(buffer, 0, length); 
  length = buffer.Length; 
}
 
public void WriteText(ArrayList text)
     {
          m_TextLines = text.Count; 
 
   for (int i = 0; i < text.Count; ++i)
          {
        string v = (string)text[i];
if (v == null)
                    v = "";
 

int length = v.Length;

               m_TextLength += length * 2;
               m_Text.Write((ushort)length);
               m_Text.WriteBigUniFixed(v, length);
          }
 
          m_TextLength += text.Count * 2;
     }
 
public int Switches
     {
  get
  {
        return this.m_Switches;
         }
 
  set
  {
        this.m_Switches = value;
         }
     }
 
public int TextEntries
     {
   get
   {
        return this.m_TextEntries;
          }
 
   set
   {
        this.m_TextEntries = value;
          }
     }
 
private static byte[] m_Buffer;
 
private static byte[] m_False;
private static byte[] m_True;
private const ushort BufferSize = 0x2000;
private int m_LayoutLength;
private int m_Switches;
private int m_TextEntries;
private int m_TextLength;
private int m_TextLines;
private PacketWriter m_Layout;
private PacketWriter m_Text;
}
Okay, it's a search&replace job till now.

Open Gumps/Gump.cs and edit SendTo method:
Code:
public void SendTo( NetState state )
    {
          state.AddGump( this );
    if (state.Version != null && state.Version.Major >= 5)
          {
               state.Send(CompileCompressed());
          }
   else
   {
               state.Send(Compile());
          }
     }
Now add a new method to the Gump class
Code:
private Packet CompileCompressed()
     {
  if (m_Packet == null)
          {
DisplayCompressedGumpFast disp = new DisplayCompressedGumpFast(this);
       if (!m_Dragable)
               {
                    disp.AppendLayout(m_NoMove);
               }
       if (!m_Closable)
               {
                    disp.AppendLayout(m_NoClose);
               }
       if (!m_Disposable)
               {
                    disp.AppendLayout(m_NoDispose);
               }
       if (!m_Resizable)
               {
                    disp.AppendLayout(m_NoResize);
               }
 
       int entries = m_Entries.Count;

       for (int i = 0; i < entries; i++)
               {
GumpEntry entry = (GumpEntry)this.m_Entries[i];
                    disp.AppendLayout(m_BeginLayout);
                    entry.AppendTo(disp);
                    disp.AppendLayout(m_EndLayout);
               }
 
               disp.WriteText(m_Strings);
 
               m_TextEntries = disp.TextEntries;
               m_Switches = disp.Switches;
 
               disp.Output();
 
               m_Packet = disp;
          }
 
return m_Packet;
     }
You will probably need to change the type of m_Packet variable
Code:
private Packet m_Packet;
Open GumpEntry.cs and edit AppendTo method this way
Code:
public abstract void AppendTo(IGumpPacket disp);
ok, now the most annoying part, you have to open all classes which inherts the GumpEntry class and edit the AppendTo method the similar way as above.
(edit)
the AppendTo method of the VirtueGump ( Engines/Virtues/VirtueGump.cs ) needs to be updated either.

example, GumpButton.cs
Code:

public override void AppendTo(IGumpPacket disp)
     {
          disp.AppendLayout( m_LayoutName );
          disp.AppendLayout( m_X );
          disp.AppendLayout( m_Y );
          disp.AppendLayout( m_ID1 );
          disp.AppendLayout( m_ID2 );
          disp.AppendLayout( (int)m_Type );
          disp.AppendLayout( m_Param );
          disp.AppendLayout( m_ButtonID );
     }
This should be all, if I forget to post something, let me know please, also sorry for the code format, I copied the code out of Visual Studio and it auto-formated the code so terribly
__________________
Angels are falling the very last time, down they're burning in hate and decline, unfaithful and violent we're breaking the spell, we're god, we're scissor, in heaven and hell!

Last edited by arul; 03-03-2006 at 08:19 AM.
arul is offline   Reply With Quote
Old 03-03-2006, 05:02 AM   #2 (permalink)
 
Join Date: Dec 2003
Posts: 11
Default Spaces and small thing forgot...

ya i found a few things prolly becouse of the way it pasted but also small thing ya forgot

newbyte[ >> new byte[
Encoding.ASCII.GetBytes( >> System.Text. Encoding.ASCII.GetBytes(
refint >> ref int

System.Text. had to be add'd becouse the file that comes in the package
isnt using System.Text by default... :P
but looks good and works great... thx
redwolf1024 is offline   Reply With Quote
Old 03-03-2006, 08:21 AM   #3 (permalink)
Forum Expert
 
arul's Avatar
 
Join Date: Jan 2005
Location: Hic sunt leones ...
Age: 21
Posts: 1,289
Send a message via MSN to arul
Default

Thanks!
The vBulletins text parsing engine do get me down
__________________
Angels are falling the very last time, down they're burning in hate and decline, unfaithful and violent we're breaking the spell, we're god, we're scissor, in heaven and hell!
arul is offline   Reply With Quote
Old 03-11-2006, 02:17 AM   #4 (permalink)
Forum Expert
 
Join Date: Jan 2003
Posts: 564
Default

using this mod everybody needs to have ML or any updated client is able to receive compressed gumps?
__________________
UO Central: O Mistério do Oculto
(Free Shard Brasileiro)

brodock is offline   Reply With Quote
Old 03-11-2006, 05:45 AM   #5 (permalink)
Forum Expert
 
arul's Avatar
 
Join Date: Jan 2005
Location: Hic sunt leones ...
Age: 21
Posts: 1,289
Send a message via MSN to arul
Default

Compressed gumps are sent only to client clients with 5 or higher major version number. To the rest are sent uncompressed gumps as you can see here
Code:
 public void SendTo( NetState state )
    {
    state.AddGump( this );
    if (state.Version != null && state.Version.Major >= 5)
          {
               state.Send(CompileCompressed());
          }
   else
   {
               state.Send(Compile());
          }
     }
__________________
Angels are falling the very last time, down they're burning in hate and decline, unfaithful and violent we're breaking the spell, we're god, we're scissor, in heaven and hell!
arul is offline   Reply With Quote
Old 03-11-2006, 02:13 PM   #6 (permalink)
Forum Expert
 
Join Date: Jan 2003
Posts: 564
Default

thanks
__________________
UO Central: O Mistério do Oculto
(Free Shard Brasileiro)

brodock is offline   Reply With Quote
Old 10-12-2006, 12:15 PM   #7 (permalink)
Forum Newbie
 
Join Date: Feb 2006
Posts: 6
Default an issue

There is one issue with your code - you seem to have overlooked the purpose of m_packet. It is there to cache the compiled (and in this case compressed) version of a gump. This is usefull if you have a gump that does not change based on recipient or any frequently changing variables, e.g. a Message of the Day gump. It means you can create a single gump and send it to anyone who needs it, rather than calling the constructor and compile methods every time you want to send it. For example, the code
Code:
Gump Announcement = new AnnouncementGump(message);
foreach(NetState state in NetState.Instances)
	state.Mobile.SendGump(Announcement);
is much more efficient than
Code:
foreach(NetState state in NetState.Instances)
	state.Mobile.SendGump(new AnnouncementGump(message));
Your code would create a bug, however, because it uses m_packet to cache both compressed and uncompressed packets. Therefore, if you send a gump to someone with a new client, it will cache a compressed gump. If you then send it to an old client, it will send the same compressed gump.

To remedy this I would recomend using 2 packet variables -
Code:
		private DisplayGumpFast m_Packet;
		private DisplayCompressedGumpFast m_CompressedPacket;
and using m_CompressedPacket in place of m_Packet in CompileCompressed(). In addition, extend Invalidate() to
Code:
		public void Invalidate()
		{
			if ( m_Packet != null || m_CompressedPacket != null )
			{
				m_Packet = null;
				m_CompressedPacket = null;

				if ( m_Strings.Count > 0 )
					m_Strings.Clear();
			}
		}
This should allow the same gump to be sent to both old and new clients. It may also be a good idea to enable scripts to set the desired level of compression, so that, for example, a very large message of the day gump, or any other large gump with constant appearance, can be compressed once with high compression and sent repeatedly. I'll be working on that later today.
BagelMan is offline   Reply With Quote
Old 10-12-2006, 12:25 PM   #8 (permalink)
ConnectUO Creator
 
Jeff's Avatar
 
Join Date: Jan 2004
Age: 28
Posts: 4,892
Default

Hmm, necro....anywho i beleve 2.0 already has support for compressed gumps.
__________________
Jeff Boulanger
ConnectUO - Core Developer

Want to help make ConnectUO better? Click here to submit your ideas/requests
Use your talent to compete against other community members in RunUO hosted coding competitions

If you know XNA (even if its just a little) or are a good artist(2d or 3d) and are interested in making games for a hobby send me a pm or drop by #xna in irc.runuo.com. I'm looking to put together a small game development team.


Please do not pm me for support. If you are having issues please post in the appropriate forum. Thanks for your continued support of both ConnectUO and RunUO
Jeff is offline   Reply With Quote
Old 10-12-2006, 12:38 PM   #9 (permalink)
Forum Newbie
 
Join Date: Feb 2006
Posts: 6
Default

I assumed a "necro" of a 7 month old thread would be acceptable on a board that still has posts from february of last year on the first page. And since RunUO 2.0 is still reffered to as "coming soon", I thought this could still be usefull.

edit: And uppon inspection of RunUO 2.0 source, it would appear that they removed packet caching from the gump code altogether. Does anyone know why?

Last edited by BagelMan; 10-12-2006 at 12:45 PM.
BagelMan is offline   Reply With Quote
Old 10-12-2006, 04:02 PM   #10 (permalink)
Forum Expert
 
Join Date: Oct 2002
Age: 45
Posts: 4,372
Default

Quote:
Originally Posted by Jeff
Hmm, necro....anywho i beleve 2.0 already has support for compressed gumps.
It's still of interest, if nothing else from an educational standpoint.
HellRazor is offline   Reply With Quote
Reply

Bookmarks


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are Off
Refbacks are Off



Powered by vBulletin® Version 3.7.0
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
SEO by vBSEO 3.2.0 RC5