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:
First off we need to make the Zlib part of the core.
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.
Now find the definition of DisplayGumpFast packet and let it inhert the new interface
Easy, isn't it ?
Now the time has come to assembly all new packet, let's call it DisplayCompressedGumpFast.
Okay, it's a search&replace job till now.
Open Gumps/Gump.cs and edit SendTo method:
Now add a new method to the Gump class
You will probably need to change the type of m_Packet variable
Open GumpEntry.cs and edit AppendTo method this way
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
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
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
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:
[SIZE=2]
[/SIZE][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]interface [/COLOR][/SIZE][SIZE=2][COLOR=#008080]IGumpPacket
[/COLOR][/SIZE][SIZE=2]{
[/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] AppendLayout ([/SIZE][SIZE=2][COLOR=#0000ff]bool[/COLOR][/SIZE][SIZE=2] val);
[/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] AppendLayout ([/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2] val);
[/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] AppendLayout ([/SIZE][SIZE=2][COLOR=#0000ff]byte[/COLOR][/SIZE][SIZE=2][] buffer);
[/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] AppendLayoutNS ([/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2] val);
[/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2] TextEntries { [/SIZE][SIZE=2][COLOR=#0000ff]get[/COLOR][/SIZE][SIZE=2]; [/SIZE][SIZE=2][COLOR=#0000ff]set[/COLOR][/SIZE][SIZE=2]; }
[/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2] Switches { [/SIZE][SIZE=2][COLOR=#0000ff]get[/COLOR][/SIZE][SIZE=2]; [/SIZE][SIZE=2][COLOR=#0000ff]set[/COLOR][/SIZE][SIZE=2]; }
}
[/SIZE]
Now find the definition of DisplayGumpFast packet and let it inhert the new interface
Code:
[SIZE=2][COLOR=#0000ff]
public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]sealed [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]class [/COLOR][/SIZE][SIZE=2][COLOR=#008080]DisplayGumpFast[/COLOR][/SIZE][SIZE=2] : [/SIZE][SIZE=2][COLOR=#008080]Packet[/COLOR][/SIZE][SIZE=2][COLOR=red], [/COLOR][/SIZE][SIZE=2][COLOR=#008080][COLOR=red]IGumpPacket[/COLOR]
[/COLOR][/SIZE]
Now the time has come to assembly all new packet, let's call it DisplayCompressedGumpFast.
Code:
[SIZE=2]public [/SIZE][SIZE=2]sealed [/SIZE][SIZE=2]class [/SIZE][SIZE=2]DisplayCompressedGumpFast[/SIZE][SIZE=2] : [/SIZE][SIZE=2]Packet[/SIZE][SIZE=2], [/SIZE][SIZE=2]IGumpPacket
[/SIZE][SIZE=2]{
[/SIZE][SIZE=2][COLOR=#0000ff]static[/COLOR][/SIZE][SIZE=2] DisplayCompressedGumpFast()
{
m_True = [/SIZE][SIZE=2][COLOR=#008080]Gump[/COLOR][/SIZE][SIZE=2].StringToBuffer([/SIZE][SIZE=2][COLOR=#800000]" 1"[/COLOR][/SIZE][SIZE=2]);
m_False = [/SIZE][SIZE=2][COLOR=#008080]Gump[/COLOR][/SIZE][SIZE=2].StringToBuffer([/SIZE][SIZE=2][COLOR=#800000]" 0"[/COLOR][/SIZE][SIZE=2]);
m_Buffer = [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE] [SIZE=2][COLOR=#0000ff]byte[/COLOR][/SIZE][SIZE=2][0x400]; [/SIZE][SIZE=2][COLOR=#008000]
[/COLOR][/SIZE][SIZE=2] m_Buffer[0] = 0x20;[/SIZE][SIZE=2][COLOR=#008000]
[/COLOR][/SIZE][SIZE=2]}
[/SIZE]
[SIZE=2]public[/SIZE][SIZE=2] DisplayCompressedGumpFast([/SIZE][SIZE=2]Gump[/SIZE][SIZE=2] g)
: [/SIZE][SIZE=2]base[/SIZE][SIZE=2](0xDD)
{
m_Layout = [/SIZE][SIZE=2]new[/SIZE][SIZE=2]PacketWriter[/SIZE][SIZE=2]();
m_Text = [/SIZE][SIZE=2]new[/SIZE][SIZE=2]PacketWriter[/SIZE][SIZE=2]();
EnsureCapacity(0x400);
m_Stream.Write(g.Serial);
m_Stream.Write(g.TypeID);
m_Stream.Write(g.X);
m_Stream.Write(g.Y);
}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]void[/SIZE][SIZE=2] AppendLayout([/SIZE][SIZE=2]bool[/SIZE][SIZE=2] val)
{
AppendLayout(val ? m_True : m_False);
}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]void[/SIZE][SIZE=2] AppendLayout([/SIZE][SIZE=2]int[/SIZE][SIZE=2] val)
{
[/SIZE][SIZE=2] string[/SIZE][SIZE=2] text = val.ToString();
[/SIZE][SIZE=2] int[/SIZE][SIZE=2] num = [/SIZE][SIZE=2]System.Text.Encoding[/SIZE][SIZE=2].ASCII.GetBytes(text, 0, text.Length, m_Buffer, 1) + 1;
m_Layout.Write(m_Buffer, 0, num);
m_LayoutLength += num;
}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]void[/SIZE][SIZE=2] AppendLayout([/SIZE][SIZE=2]byte[/SIZE][SIZE=2][] buffer)
{
[/SIZE][SIZE=2] int[/SIZE][SIZE=2] num = buffer.Length;
m_Layout.Write(buffer, 0, num);
m_LayoutLength += num;
}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]void[/SIZE][SIZE=2] AppendLayoutNS([/SIZE][SIZE=2]int[/SIZE][SIZE=2] val)
{
[/SIZE][SIZE=2] string[/SIZE][SIZE=2] text = val.ToString();
[/SIZE][SIZE=2] int[/SIZE][SIZE=2] num = [/SIZE][SIZE=2]System.Text.Encoding[/SIZE][SIZE=2].ASCII.GetBytes(text, 0, text.Length, m_Buffer, 1);
m_Layout.Write(m_Buffer, 1, num);
m_LayoutLength += num;
}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]void[/SIZE][SIZE=2] Output()
{
[/SIZE][SIZE=2][COLOR=#0000ff]byte[/COLOR][/SIZE][SIZE=2][] buffer = [/SIZE][SIZE=2][COLOR=#0000ff]new[/COLOR][/SIZE] [SIZE=2][COLOR=#0000ff]byte[/COLOR][/SIZE][SIZE=2][BufferSize];[/SIZE][SIZE=2][COLOR=#008000]
[/COLOR][/SIZE][SIZE=2][COLOR=#0000ff] int[/COLOR][/SIZE][SIZE=2] length = buffer.Length;[/SIZE]
[SIZE=2]
[/SIZE][SIZE=2] if[/SIZE][SIZE=2] (
[/SIZE][SIZE=2] ZLib[/SIZE][SIZE=2].compress2(
buffer,
[/SIZE][SIZE=2] ref[/SIZE][SIZE=2] length,
m_Layout.ToArray(),
m_LayoutLength,
[/SIZE][SIZE=2] ZLibCompressionLevel[/SIZE][SIZE=2].Z_BEST_SPEED) == [/SIZE][SIZE=2]ZLibError[/SIZE][SIZE=2].Z_OK
)
{
WriteBuffer(
buffer,
[/SIZE][SIZE=2] ref[/SIZE][SIZE=2] length,
m_LayoutLength
);
[/SIZE][SIZE=2]
[/SIZE][SIZE=2] if[/SIZE][SIZE=2] (
[/SIZE][SIZE=2] ZLib[/SIZE][SIZE=2].compress2(
buffer,
[/SIZE][SIZE=2] ref[/SIZE][SIZE=2] length,
m_Text.ToArray(),
m_TextLength,
[/SIZE][SIZE=2] ZLibCompressionLevel[/SIZE][SIZE=2].Z_BEST_SPEED) == [/SIZE][SIZE=2]ZLibError[/SIZE][SIZE=2].Z_OK
)
{
m_Stream.Write(m_TextLines);
[/SIZE][SIZE=2]
[/SIZE][SIZE=2] WriteBuffer(
buffer,
[/SIZE][SIZE=2] ref[/SIZE][SIZE=2] length,
m_TextLength
);
[/SIZE][SIZE=2]
[/SIZE][SIZE=2] }
}
}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]void[/SIZE][SIZE=2] WriteBuffer([/SIZE][SIZE=2]byte[/SIZE][SIZE=2][] buffer, [/SIZE][SIZE=2]ref[/SIZE][SIZE=2]int[/SIZE][SIZE=2] length, [/SIZE][SIZE=2]int[/SIZE][SIZE=2] contentLength)
{
m_Stream.Write(length + 4);
[/SIZE][SIZE=2] m_Stream.Write(contentLength);[/SIZE][SIZE=2]
[/SIZE][SIZE=2] m_Stream.Write(buffer, 0, length);
[/SIZE][SIZE=2] length = buffer.Length; [/SIZE][SIZE=2]
[/SIZE][SIZE=2]}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]void[/SIZE][SIZE=2] WriteText([/SIZE][SIZE=2]ArrayList[/SIZE][SIZE=2] text)
{
m_TextLines = text.Count;
[/SIZE][SIZE=2] for[/SIZE][SIZE=2] ([/SIZE][SIZE=2]int[/SIZE][SIZE=2] i = 0; i < text.Count; ++i)
{
[/SIZE][SIZE=2] string[/SIZE][SIZE=2] v = ([/SIZE][SIZE=2]string[/SIZE][SIZE=2])text[i];
[/SIZE][SIZE=2][COLOR=#0000ff]if[/COLOR][/SIZE][SIZE=2] (v == [/SIZE][SIZE=2][COLOR=#0000ff]null[/COLOR][/SIZE][SIZE=2])
v = [/SIZE][SIZE=2][COLOR=#800000]""[/COLOR][/SIZE][SIZE=2];[/SIZE]
[SIZE=2]
[/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2] length = v.Length;[/SIZE]
[SIZE=2]
m_TextLength += length * 2;
m_Text.Write(([/SIZE][SIZE=2]ushort[/SIZE][SIZE=2])length);
m_Text.WriteBigUniFixed(v, length);
}
m_TextLength += text.Count * 2;
}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]int[/SIZE][SIZE=2] Switches
{
[/SIZE][SIZE=2] get
[/SIZE][SIZE=2] {
[/SIZE][SIZE=2] return [/SIZE][SIZE=2]this[/SIZE][SIZE=2].m_Switches;
}
[/SIZE][SIZE=2] set
[/SIZE][SIZE=2] {
[/SIZE][SIZE=2] this[/SIZE][SIZE=2].m_Switches = [/SIZE][SIZE=2]value[/SIZE][SIZE=2];
}
}
[/SIZE][SIZE=2]public [/SIZE][SIZE=2]int[/SIZE][SIZE=2] TextEntries
{
[/SIZE][SIZE=2] get
[/SIZE][SIZE=2] {
[/SIZE][SIZE=2] return [/SIZE][SIZE=2]this[/SIZE][SIZE=2].m_TextEntries;
}
[/SIZE][SIZE=2] set
[/SIZE][SIZE=2] {
[/SIZE][SIZE=2] this[/SIZE][SIZE=2].m_TextEntries = [/SIZE][SIZE=2]value[/SIZE][SIZE=2];
}
}
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]static [/SIZE][SIZE=2]byte[/SIZE][SIZE=2][] m_Buffer;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]static [/SIZE][SIZE=2]byte[/SIZE][SIZE=2][] m_False;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]static [/SIZE][SIZE=2]byte[/SIZE][SIZE=2][] m_True;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]const [/SIZE][SIZE=2]ushort[/SIZE][SIZE=2] BufferSize = 0x2000;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]int[/SIZE][SIZE=2] m_LayoutLength;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]int[/SIZE][SIZE=2] m_Switches;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]int[/SIZE][SIZE=2] m_TextEntries;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]int[/SIZE][SIZE=2] m_TextLength;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]int[/SIZE][SIZE=2] m_TextLines;
[/SIZE][SIZE=2]private [/SIZE][SIZE=2]PacketWriter[/SIZE][SIZE=2] m_Layout;
[/SIZE][SIZE=2][COLOR=#0000ff]private [/COLOR][/SIZE][SIZE=2][COLOR=#008080]PacketWriter[/COLOR][/SIZE][SIZE=2] m_Text;
}
[/SIZE]
Okay, it's a search&replace job till now.
Open Gumps/Gump.cs and edit SendTo method:
Code:
[SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] SendTo( [/SIZE][SIZE=2][COLOR=#008080]NetState[/COLOR][/SIZE][SIZE=2] state )
{
state.AddGump( [/SIZE][SIZE=2][COLOR=#0000ff]this[/COLOR][/SIZE][SIZE=2] );
[/SIZE][SIZE=2][COLOR=#0000ff] if[/COLOR][/SIZE][SIZE=2] (state.Version != [/SIZE][SIZE=2][COLOR=#0000ff]null[/COLOR][/SIZE][SIZE=2] && state.Version.Major >= 5)
{
state.Send(CompileCompressed());
}
[/SIZE][SIZE=2][COLOR=#0000ff] else
[/COLOR][/SIZE][SIZE=2] {
state.Send(Compile());
}
}
[/SIZE]
Now add a new method to the Gump class
Code:
[SIZE=2]private [/SIZE][SIZE=2]Packet[/SIZE][SIZE=2] CompileCompressed()
{
[/SIZE][SIZE=2] if[/SIZE][SIZE=2] (m_Packet == [/SIZE][SIZE=2]null[/SIZE][SIZE=2])
{
[/SIZE][SIZE=2][COLOR=#008080]DisplayCompressedGumpFast[/COLOR][/SIZE][SIZE=2] disp = [/SIZE][SIZE=2][COLOR=#0000ff]new [/COLOR][/SIZE][SIZE=2][COLOR=#008080]DisplayCompressedGumpFast[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#0000ff]this[/COLOR][/SIZE][SIZE=2]);
[/SIZE][SIZE=2][COLOR=#0000ff] if[/COLOR][/SIZE][SIZE=2] (!m_Dragable)
{
disp.AppendLayout(m_NoMove);
}
[/SIZE][SIZE=2][COLOR=#0000ff] if[/COLOR][/SIZE][SIZE=2] (!m_Closable)
{
disp.AppendLayout(m_NoClose);
}
[/SIZE][SIZE=2][COLOR=#0000ff] if[/COLOR][/SIZE][SIZE=2] (!m_Disposable)
{
disp.AppendLayout(m_NoDispose);
}
[/SIZE][SIZE=2][COLOR=#0000ff] if[/COLOR][/SIZE][SIZE=2] (!m_Resizable)
{
disp.AppendLayout(m_NoResize);
}
[/SIZE][SIZE=2][COLOR=#0000ff] int[/COLOR][/SIZE][SIZE=2] entries = m_Entries.Count;[/SIZE]
[SIZE=2]
[/SIZE][SIZE=2] for[/SIZE][SIZE=2] ([/SIZE][SIZE=2]int[/SIZE][SIZE=2] i = 0; i < entries; i++)
{
[/SIZE][SIZE=2][COLOR=#008080]GumpEntry[/COLOR][/SIZE][SIZE=2] entry = ([/SIZE][SIZE=2][COLOR=#008080]GumpEntry[/COLOR][/SIZE][SIZE=2])[/SIZE][SIZE=2][COLOR=#0000ff]this[/COLOR][/SIZE][SIZE=2].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;
}
[/SIZE][SIZE=2][COLOR=#0000ff]return[/COLOR][/SIZE][SIZE=2] m_Packet;
}
[/SIZE]
You will probably need to change the type of m_Packet variable
Code:
[SIZE=2][COLOR=#0000ff]private [/COLOR][/SIZE][SIZE=2][COLOR=#008080]Packet[/COLOR][/SIZE][SIZE=2] m_Packet;
[/SIZE]
Open GumpEntry.cs and edit AppendTo method this way
Code:
[SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]abstract [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] AppendTo([/SIZE][SIZE=2][COLOR=red]IGumpPacket[/COLOR][/SIZE][SIZE=2] disp);
[/SIZE]
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:
[SIZE=2]
[/SIZE][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]override [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]void[/COLOR][/SIZE][SIZE=2] AppendTo([/SIZE][SIZE=2][COLOR=red]IGumpPacket[/COLOR][/SIZE][SIZE=2] disp)
{
disp.AppendLayout( m_LayoutName );
disp.AppendLayout( m_X );
disp.AppendLayout( m_Y );
disp.AppendLayout( m_ID1 );
disp.AppendLayout( m_ID2 );
disp.AppendLayout( ([/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2])m_Type );
disp.AppendLayout( m_Param );
disp.AppendLayout( m_ButtonID );
}
[/SIZE]
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