#region References using System; using System.Collections.Generic; using System.IO; using System.Text; #endregion namespace Server.Network { /// /// Provides functionality for writing primitive binary data. /// public class PacketWriter { private static readonly Stack m_Pool = new Stack(); public static PacketWriter CreateInstance() { return CreateInstance(32); } public static PacketWriter CreateInstance(int capacity) { PacketWriter pw = null; lock (m_Pool) { if (m_Pool.Count > 0) { pw = m_Pool.Pop(); if (pw != null) { pw.m_Capacity = capacity; pw.m_Stream.SetLength(0); } } } if (pw == null) { pw = new PacketWriter(capacity); } return pw; } public static void ReleaseInstance(PacketWriter pw) { lock (m_Pool) { if (!m_Pool.Contains(pw)) { m_Pool.Push(pw); } else { try { using (var op = new StreamWriter("neterr.log")) { op.WriteLine("{0}\tInstance pool contains writer", DateTime.UtcNow); } } catch { Console.WriteLine("net error"); } } } } /// /// Internal stream which holds the entire packet. /// private readonly MemoryStream m_Stream; private int m_Capacity; /// /// Internal format buffer. /// private readonly byte[] m_Buffer = new byte[4]; /// /// Instantiates a new PacketWriter instance with the default capacity of 4 bytes. /// public PacketWriter() : this(32) { } /// /// Instantiates a new PacketWriter instance with a given capacity. /// /// Initial capacity for the internal stream. public PacketWriter(int capacity) { m_Stream = new MemoryStream(capacity); m_Capacity = capacity; } /// /// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1. /// public void Write(bool value) { m_Stream.WriteByte((byte)(value ? 1 : 0)); } /// /// Writes a 1-byte unsigned integer value to the underlying stream. /// public void Write(byte value) { m_Stream.WriteByte(value); } /// /// Writes a 1-byte signed integer value to the underlying stream. /// public void Write(sbyte value) { m_Stream.WriteByte((byte)value); } /// /// Writes a 2-byte signed integer value to the underlying stream. /// public void Write(short value) { m_Buffer[0] = (byte)(value >> 8); m_Buffer[1] = (byte)value; m_Stream.Write(m_Buffer, 0, 2); } /// /// Writes a 2-byte unsigned integer value to the underlying stream. /// public void Write(ushort value) { m_Buffer[0] = (byte)(value >> 8); m_Buffer[1] = (byte)value; m_Stream.Write(m_Buffer, 0, 2); } /// /// Writes a 4-byte signed integer value to the underlying stream. /// public void Write(int value) { m_Buffer[0] = (byte)(value >> 24); m_Buffer[1] = (byte)(value >> 16); m_Buffer[2] = (byte)(value >> 8); m_Buffer[3] = (byte)value; m_Stream.Write(m_Buffer, 0, 4); } /// /// Writes a 4-byte unsigned integer value to the underlying stream. /// public void Write(uint value) { m_Buffer[0] = (byte)(value >> 24); m_Buffer[1] = (byte)(value >> 16); m_Buffer[2] = (byte)(value >> 8); m_Buffer[3] = (byte)value; m_Stream.Write(m_Buffer, 0, 4); } /// /// Writes a sequence of bytes to the underlying stream /// public void Write(byte[] buffer, int offset, int size) { m_Stream.Write(buffer, offset, size); } /// /// 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. /// public void WriteAsciiFixed(string value, int size) { if (value == null) { Console.WriteLine("Network: Attempted to WriteAsciiFixed() with null value"); value = String.Empty; } var length = value.Length; m_Stream.SetLength(m_Stream.Length + size); if (length >= size) { m_Stream.Position += Encoding.ASCII.GetBytes(value, 0, size, m_Stream.GetBuffer(), (int)m_Stream.Position); } else { Encoding.ASCII.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position); m_Stream.Position += size; } /*byte[] buffer = Encoding.ASCII.GetBytes( value ); if ( buffer.Length >= size ) { m_Stream.Write( buffer, 0, size ); } else { m_Stream.Write( buffer, 0, buffer.Length ); Fill( size - buffer.Length ); }*/ } /// /// Writes a dynamic-length ASCII-encoded string value to the underlying stream, followed by a 1-byte null character. /// public void WriteAsciiNull(string value) { if (value == null) { Console.WriteLine("Network: Attempted to WriteAsciiNull() with null value"); value = String.Empty; } var length = value.Length; m_Stream.SetLength(m_Stream.Length + length + 1); Encoding.ASCII.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position); m_Stream.Position += length + 1; /*byte[] buffer = Encoding.ASCII.GetBytes( value ); m_Stream.Write( buffer, 0, buffer.Length ); m_Stream.WriteByte( 0 );*/ } /// /// Writes a dynamic-length little-endian unicode string value to the underlying stream, followed by a 2-byte null character. /// public void WriteLittleUniNull(string value) { if (value == null) { Console.WriteLine("Network: Attempted to WriteLittleUniNull() with null value"); value = String.Empty; } var length = value.Length; m_Stream.SetLength(m_Stream.Length + ((length + 1) * 2)); m_Stream.Position += Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position); m_Stream.Position += 2; /*byte[] buffer = Encoding.Unicode.GetBytes( value ); m_Stream.Write( buffer, 0, buffer.Length ); m_Buffer[0] = 0; m_Buffer[1] = 0; m_Stream.Write( m_Buffer, 0, 2 );*/ } /// /// Writes a fixed-length little-endian unicode string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters. /// public void WriteLittleUniFixed(string value, int size) { if (value == null) { Console.WriteLine("Network: Attempted to WriteLittleUniFixed() with null value"); value = String.Empty; } size *= 2; var length = value.Length; m_Stream.SetLength(m_Stream.Length + size); if ((length * 2) >= size) { m_Stream.Position += Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position); } else { Encoding.Unicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position); m_Stream.Position += size; } /*size *= 2; byte[] buffer = Encoding.Unicode.GetBytes( value ); if ( buffer.Length >= size ) { m_Stream.Write( buffer, 0, size ); } else { m_Stream.Write( buffer, 0, buffer.Length ); Fill( size - buffer.Length ); }*/ } /// /// Writes a dynamic-length big-endian unicode string value to the underlying stream, followed by a 2-byte null character. /// public void WriteBigUniNull(string value) { if (value == null) { Console.WriteLine("Network: Attempted to WriteBigUniNull() with null value"); value = String.Empty; } var length = value.Length; m_Stream.SetLength(m_Stream.Length + ((length + 1) * 2)); m_Stream.Position += Encoding.BigEndianUnicode.GetBytes( value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position); m_Stream.Position += 2; /*byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value ); m_Stream.Write( buffer, 0, buffer.Length ); m_Buffer[0] = 0; m_Buffer[1] = 0; m_Stream.Write( m_Buffer, 0, 2 );*/ } /// /// Writes a fixed-length big-endian unicode string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters. /// public void WriteBigUniFixed(string value, int size) { if (value == null) { Console.WriteLine("Network: Attempted to WriteBigUniFixed() with null value"); value = String.Empty; } size *= 2; var length = value.Length; m_Stream.SetLength(m_Stream.Length + size); if ((length * 2) >= size) { m_Stream.Position += Encoding.BigEndianUnicode.GetBytes( value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position); } else { Encoding.BigEndianUnicode.GetBytes(value, 0, length, m_Stream.GetBuffer(), (int)m_Stream.Position); m_Stream.Position += size; } /*size *= 2; byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value ); if ( buffer.Length >= size ) { m_Stream.Write( buffer, 0, size ); } else { m_Stream.Write( buffer, 0, buffer.Length ); Fill( size - buffer.Length ); }*/ } /// /// Fills the stream from the current position up to (capacity) with 0x00's /// public void Fill() { Fill((int)(m_Capacity - m_Stream.Length)); } /// /// Writes a number of 0x00 byte values to the underlying stream. /// public void Fill(int length) { if (m_Stream.Position == m_Stream.Length) { m_Stream.SetLength(m_Stream.Length + length); m_Stream.Seek(0, SeekOrigin.End); } else { m_Stream.Write(new byte[length], 0, length); } } /// /// Gets the total stream length. /// public long Length => m_Stream.Length; /// /// Gets or sets the current stream position. /// public long Position { get => m_Stream.Position; set => m_Stream.Position = value; } /// /// The internal stream used by this PacketWriter instance. /// public MemoryStream UnderlyingStream => m_Stream; /// /// Offsets the current position from an origin. /// public long Seek(long offset, SeekOrigin origin) { return m_Stream.Seek(offset, origin); } /// /// Gets the entire stream content as a byte array. /// public byte[] ToArray() { return m_Stream.ToArray(); } } }