using Lidgren.Network; using System; using System.IO; using System.IO.Compression; using System.Runtime.InteropServices; using System.Text; using Microsoft.Xna.Framework; namespace Barotrauma.Networking { public static class MsgConstants { // MTU currently set to the upper limit of what EOS P2P can do // TODO: determine dynamically so other protocols can use a larger MTU, // as well as handle a client with a lower MTU set outside of our control public const int MTU = 1170; public const int CompressionThreshold = 1000; public const int InitialBufferSize = 256; public const int BufferOverAllocateAmount = 4; } /// /// Utility struct for writing Singles /// [StructLayout(LayoutKind.Explicit)] public struct SingleUIntUnion { /// /// Value as a 32 bit float /// [FieldOffset(0)] public float SingleValue; /// /// Value as an unsigned 32 bit integer /// [FieldOffset(0)] public uint UIntValue; } internal static class MsgWriter { internal static void UpdateBitLength(ref int bitLength, int bitPos) { bitLength = Math.Max(bitLength, bitPos); } internal static void WriteBoolean(ref byte[] buf, ref int bitPos, ref int bitLength, bool val) { #if DEBUG int resetPos = bitPos; #endif EnsureBufferSize(ref buf, bitPos + 1); int bytePos = bitPos / 8; int bitOffset = bitPos % 8; byte bitFlag = (byte)(1 << bitOffset); byte bitMask = (byte)((~bitFlag) & 0xff); buf[bytePos] &= bitMask; if (val) buf[bytePos] |= bitFlag; bitPos++; UpdateBitLength(ref bitLength, bitPos); #if DEBUG bool testVal = MsgReader.ReadBoolean(buf, ref resetPos); if (testVal != val || resetPos != bitPos) { DebugConsole.ThrowError($"Boolean written incorrectly! {testVal}, {val}; {resetPos}, {bitPos}"); } #endif } internal static void WritePadBits(ref byte[] buf, ref int bitPos, ref int bitLength) { int bitOffset = bitPos % 8; bitPos += ((8 - bitOffset) % 8); UpdateBitLength(ref bitLength, bitPos); EnsureBufferSize(ref buf, bitPos); } internal static void WriteByte(ref byte[] buf, ref int bitPos, ref int bitLength, byte val) { EnsureBufferSize(ref buf, bitPos + 8); NetBitWriter.WriteByte(val, 8, buf, bitPos); bitPos += 8; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteUInt16(ref byte[] buf, ref int bitPos, ref int bitLength, UInt16 val) { EnsureBufferSize(ref buf, bitPos + 16); NetBitWriter.WriteUInt16(val, 16, buf, bitPos); bitPos += 16; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteInt16(ref byte[] buf, ref int bitPos, ref int bitLength, Int16 val) { EnsureBufferSize(ref buf, bitPos + 16); NetBitWriter.WriteUInt16((UInt16)val, 16, buf, bitPos); bitPos += 16; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteUInt32(ref byte[] buf, ref int bitPos, ref int bitLength, UInt32 val) { EnsureBufferSize(ref buf, bitPos + 32); NetBitWriter.WriteUInt32(val, 32, buf, bitPos); bitPos += 32; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteInt32(ref byte[] buf, ref int bitPos, ref int bitLength, Int32 val) { EnsureBufferSize(ref buf, bitPos + 32); NetBitWriter.WriteUInt32((UInt32)val, 32, buf, bitPos); bitPos += 32; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteUInt64(ref byte[] buf, ref int bitPos, ref int bitLength, UInt64 val) { EnsureBufferSize(ref buf, bitPos + 64); NetBitWriter.WriteUInt64(val, 64, buf, bitPos); bitPos += 64; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteInt64(ref byte[] buf, ref int bitPos, ref int bitLength, Int64 val) { EnsureBufferSize(ref buf, bitPos + 64); NetBitWriter.WriteUInt64((UInt64)val, 64, buf, bitPos); bitPos += 64; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteSingle(ref byte[] buf, ref int bitPos, ref int bitLength, Single val) { // Use union to avoid BitConverter.GetBytes() which allocates memory on the heap SingleUIntUnion su; su.UIntValue = 0; // must initialize every member of the union to avoid warning su.SingleValue = val; EnsureBufferSize(ref buf, bitPos + 32); NetBitWriter.WriteUInt32(su.UIntValue, 32, buf, bitPos); bitPos += 32; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteDouble(ref byte[] buf, ref int bitPos, ref int bitLength, Double val) { EnsureBufferSize(ref buf, bitPos + 64); byte[] bytes = BitConverter.GetBytes(val); WriteBytes(ref buf, ref bitPos, ref bitLength, bytes, 0, 8); } internal static void WriteColorR8G8B8(ref byte[] buf, ref int bitPos, ref int bitLength, Color val) { EnsureBufferSize(ref buf, bitPos + 24); WriteByte(ref buf, ref bitPos, ref bitLength, val.R); WriteByte(ref buf, ref bitPos, ref bitLength, val.G); WriteByte(ref buf, ref bitPos, ref bitLength, val.B); } internal static void WriteColorR8G8B8A8(ref byte[] buf, ref int bitPos, ref int bitLength, Color val) { EnsureBufferSize(ref buf, bitPos + 32); WriteByte(ref buf, ref bitPos, ref bitLength, val.R); WriteByte(ref buf, ref bitPos, ref bitLength, val.G); WriteByte(ref buf, ref bitPos, ref bitLength, val.B); WriteByte(ref buf, ref bitPos, ref bitLength, val.A); } internal static void WriteString(ref byte[] buf, ref int bitPos, ref int bitLength, string val) { if (string.IsNullOrEmpty(val)) { WriteVariableUInt32(ref buf, ref bitPos, ref bitLength, 0u); return; } byte[] bytes = Encoding.UTF8.GetBytes(val); WriteVariableUInt32(ref buf, ref bitPos, ref bitLength, (uint)bytes.Length); WriteBytes(ref buf, ref bitPos, ref bitLength, bytes, 0, bytes.Length); } internal static void WriteVariableUInt32(ref byte[] buf, ref int bitPos, ref int bitLength, uint value) { uint remainingValue = value; while (remainingValue >= 0x80) { WriteByte(ref buf, ref bitPos, ref bitLength, (byte)(remainingValue | 0x80)); remainingValue >>= 7; } WriteByte(ref buf, ref bitPos, ref bitLength, (byte)remainingValue); } internal static void WriteRangedInteger(ref byte[] buf, ref int bitPos, ref int bitLength, int val, int min, int max) { uint range = (uint)(max - min); int numberOfBits = NetUtility.BitsToHoldUInt(range); EnsureBufferSize(ref buf, bitPos + numberOfBits); uint rvalue = (uint)(val - min); NetBitWriter.WriteUInt32(rvalue, numberOfBits, buf, bitPos); bitPos += numberOfBits; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteRangedSingle(ref byte[] buf, ref int bitPos, ref int bitLength, Single val, Single min, Single max, int numberOfBits) { float range = max - min; float unit = ((val - min) / range); int maxVal = (1 << numberOfBits) - 1; EnsureBufferSize(ref buf, bitPos + numberOfBits); NetBitWriter.WriteUInt32((UInt32)(maxVal * unit), numberOfBits, buf, bitPos); bitPos += numberOfBits; UpdateBitLength(ref bitLength, bitPos); } internal static void WriteBytes(ref byte[] buf, ref int bitPos, ref int bitLength, byte[] val, int pos, int length) { EnsureBufferSize(ref buf, bitPos + length * 8); NetBitWriter.WriteBytes(val, pos, length, buf, bitPos); bitPos += length * 8; UpdateBitLength(ref bitLength, bitPos); } internal static void EnsureBufferSize(ref byte[] buf, int numberOfBits) { int byteLen = (numberOfBits + 7) / 8; if (buf == null) { buf = new byte[byteLen + MsgConstants.BufferOverAllocateAmount]; return; } if (buf.Length < byteLen) { Array.Resize(ref buf, byteLen + MsgConstants.BufferOverAllocateAmount); } } } internal static class MsgReader { internal static bool ReadBoolean(byte[] buf, ref int bitPos) { byte retval = NetBitWriter.ReadByte(buf, 1, bitPos); bitPos++; return retval > 0; } internal static void ReadPadBits(ref int bitPos) { int bitOffset = bitPos % 8; bitPos += (8 - bitOffset) % 8; } internal static byte ReadByte(byte[] buf, ref int bitPos) { byte retval = NetBitWriter.ReadByte(buf, 8, bitPos); bitPos += 8; return retval; } internal static byte PeekByte(byte[] buf, ref int bitPos) { byte retval = NetBitWriter.ReadByte(buf, 8, bitPos); return retval; } internal static UInt16 ReadUInt16(byte[] buf, ref int bitPos) { uint retval = NetBitWriter.ReadUInt16(buf, 16, bitPos); bitPos += 16; return (ushort)retval; } internal static Int16 ReadInt16(byte[] buf, ref int bitPos) { return (Int16)ReadUInt16(buf, ref bitPos); } internal static UInt32 ReadUInt32(byte[] buf, ref int bitPos) { uint retval = NetBitWriter.ReadUInt32(buf, 32, bitPos); bitPos += 32; return retval; } internal static Int32 ReadInt32(byte[] buf, ref int bitPos) { return (Int32)ReadUInt32(buf, ref bitPos); } internal static UInt64 ReadUInt64(byte[] buf, ref int bitPos) { ulong low = NetBitWriter.ReadUInt32(buf, 32, bitPos); bitPos += 32; ulong high = NetBitWriter.ReadUInt32(buf, 32, bitPos); ulong retval = low + (high << 32); bitPos += 32; return retval; } internal static Int64 ReadInt64(byte[] buf, ref int bitPos) { return (Int64)ReadUInt64(buf, ref bitPos); } internal static Single ReadSingle(byte[] buf, ref int bitPos) { if ((bitPos & 7) == 0) // read directly { float retval = BitConverter.ToSingle(buf, bitPos >> 3); bitPos += 32; return retval; } byte[] bytes = ReadBytes(buf, ref bitPos, 4); return BitConverter.ToSingle(bytes, 0); } internal static Double ReadDouble(byte[] buf, ref int bitPos) { if ((bitPos & 7) == 0) // read directly { // read directly double retval = BitConverter.ToDouble(buf, bitPos >> 3); bitPos += 64; return retval; } byte[] bytes = ReadBytes(buf, ref bitPos, 8); return BitConverter.ToDouble(bytes, 0); } internal static Color ReadColorR8G8B8(byte[] buf, ref int bitPos) { byte r = ReadByte(buf, ref bitPos); byte g = ReadByte(buf, ref bitPos); byte b = ReadByte(buf, ref bitPos); return new Color(r, g, b, (byte)255); } internal static Color ReadColorR8G8B8A8(byte[] buf, ref int bitPos) { byte r = ReadByte(buf, ref bitPos); byte g = ReadByte(buf, ref bitPos); byte b = ReadByte(buf, ref bitPos); byte a = ReadByte(buf, ref bitPos); return new Color(r, g, b, a); } internal static UInt32 ReadVariableUInt32(byte[] buf, ref int bitPos) { int bitLength = buf.Length * 8; int result = 0; int shift = 0; while (bitLength - bitPos >= 8) { byte chunk = ReadByte(buf, ref bitPos); result |= (chunk & 0x7f) << shift; shift += 7; if ((chunk & 0x80) == 0) { return (uint)result; } } // ouch; failed to find enough bytes; malformed variable length number? return (uint)result; } internal static String ReadString(byte[] buf, ref int bitPos) { int bitLength = buf.Length * 8; int byteLen = (int)ReadVariableUInt32(buf, ref bitPos); if (byteLen <= 0) { return String.Empty; } if ((ulong)(bitLength - bitPos) < ((ulong)byteLen * 8)) { // not enough data return null; } if ((bitPos & 7) == 0) { // read directly string retval = Encoding.UTF8.GetString(buf, bitPos >> 3, byteLen); bitPos += (8 * byteLen); return retval; } byte[] bytes = ReadBytes(buf, ref bitPos, byteLen); return Encoding.UTF8.GetString(bytes, 0, bytes.Length); } internal static int ReadRangedInteger(byte[] buf, ref int bitPos, int min, int max) { uint range = (uint)(max - min); int numBits = NetUtility.BitsToHoldUInt(range); uint rvalue = NetBitWriter.ReadUInt32(buf, numBits, bitPos); bitPos += numBits; return (int)(min + rvalue); } internal static Single ReadRangedSingle(byte[] buf, ref int bitPos, Single min, Single max, int bitCount) { int maxInt = (1 << bitCount) - 1; int intVal = ReadRangedInteger(buf, ref bitPos, 0, maxInt); Single range = max - min; return min + range * intVal / maxInt; } internal static byte[] ReadBytes(byte[] buf, ref int bitPos, int numberOfBytes) { byte[] retval = new byte[numberOfBytes]; NetBitWriter.ReadBytes(buf, numberOfBytes, bitPos, retval, 0); bitPos += 8 * numberOfBytes; return retval; } } internal sealed class WriteOnlyMessage : IWriteMessage { private byte[] buf = new byte[MsgConstants.InitialBufferSize]; private int seekPos; private int lengthBits; public int BitPosition { get => seekPos; set => seekPos = value; } public int BytePosition => seekPos / 8; public byte[] Buffer => buf; public int LengthBits { get { lengthBits = seekPos > lengthBits ? seekPos : lengthBits; return lengthBits; } set { lengthBits = value; seekPos = seekPos > lengthBits ? lengthBits : seekPos; MsgWriter.EnsureBufferSize(ref buf, lengthBits); } } public int LengthBytes => (LengthBits + 7) / 8; public void WriteBoolean(bool val) { MsgWriter.WriteBoolean(ref buf, ref seekPos, ref lengthBits, val); } public void WritePadBits() { MsgWriter.WritePadBits(ref buf, ref seekPos, ref lengthBits); } public void WriteByte(byte val) { MsgWriter.WriteByte(ref buf, ref seekPos, ref lengthBits, val); } public void WriteUInt16(UInt16 val) { MsgWriter.WriteUInt16(ref buf, ref seekPos, ref lengthBits, val); } public void WriteInt16(Int16 val) { MsgWriter.WriteInt16(ref buf, ref seekPos, ref lengthBits, val); } public void WriteUInt32(UInt32 val) { MsgWriter.WriteUInt32(ref buf, ref seekPos, ref lengthBits, val); } public void WriteInt32(Int32 val) { MsgWriter.WriteInt32(ref buf, ref seekPos, ref lengthBits, val); } public void WriteUInt64(UInt64 val) { MsgWriter.WriteUInt64(ref buf, ref seekPos, ref lengthBits, val); } public void WriteInt64(Int64 val) { MsgWriter.WriteInt64(ref buf, ref seekPos, ref lengthBits, val); } public void WriteSingle(Single val) { MsgWriter.WriteSingle(ref buf, ref seekPos, ref lengthBits, val); } public void WriteDouble(Double val) { MsgWriter.WriteDouble(ref buf, ref seekPos, ref lengthBits, val); } public void WriteColorR8G8B8(Color val) { MsgWriter.WriteColorR8G8B8(ref buf, ref seekPos, ref lengthBits, val); } public void WriteColorR8G8B8A8(Color val) { MsgWriter.WriteColorR8G8B8A8(ref buf, ref seekPos, ref lengthBits, val); } public void WriteVariableUInt32(UInt32 val) { MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, ref lengthBits, val); } public void WriteString(String val) { MsgWriter.WriteString(ref buf, ref seekPos, ref lengthBits, val); } public void WriteIdentifier(Identifier val) { WriteString(val.Value); } public void WriteRangedInteger(int val, int min, int max) { MsgWriter.WriteRangedInteger(ref buf, ref seekPos, ref lengthBits, val, min, max); } public void WriteRangedSingle(Single val, Single min, Single max, int bitCount) { MsgWriter.WriteRangedSingle(ref buf, ref seekPos, ref lengthBits, val, min, max, bitCount); } public void WriteBytes(byte[] val, int startPos, int length) { MsgWriter.WriteBytes(ref buf, ref seekPos, ref lengthBits, val, startPos, length); } public byte[] PrepareForSending(bool compressPastThreshold, out bool isCompressed, out int length) { byte[] outBuf; if (LengthBytes <= MsgConstants.CompressionThreshold || !compressPastThreshold) { isCompressed = false; outBuf = new byte[LengthBytes]; Array.Copy(buf, outBuf, LengthBytes); length = LengthBytes; } else { using MemoryStream output = new MemoryStream(); using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Fastest)) { dstream.Write(buf, 0, LengthBytes); } byte[] compressedBuf = output.ToArray(); //don't send the data as compressed if the data takes up more space after compression //(which may happen when sending a sub/save file that's already been compressed with a better compression ratio) if (compressedBuf.Length >= LengthBytes) { isCompressed = false; outBuf = new byte[LengthBytes]; Array.Copy(buf, outBuf, LengthBytes); length = LengthBytes; } else { isCompressed = true; outBuf = compressedBuf; length = outBuf.Length; DebugConsole.Log($"Compressed message: {LengthBytes} to {length}"); } } return outBuf; } } internal sealed class ReadOnlyMessage : IReadMessage { private int seekPos; private int lengthBits; public int BitPosition { get => seekPos; set => seekPos = value; } public int BytePosition => seekPos / 8; public byte[] Buffer { get; } public int LengthBits { get { lengthBits = seekPos > lengthBits ? seekPos : lengthBits; return lengthBits; } set { lengthBits = value; seekPos = seekPos > lengthBits ? lengthBits : seekPos; } } public int LengthBytes => (LengthBits + 7) / 8; public NetworkConnection Sender { get; } public ReadOnlyMessage(byte[] inBuf, bool isCompressed, int startPos, int byteLength, NetworkConnection sender) { Sender = sender; if (isCompressed) { byte[] decompressedData; using (MemoryStream input = new MemoryStream(inBuf, startPos, byteLength)) { using (MemoryStream output = new MemoryStream()) { using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress)) { dstream.CopyTo(output); } decompressedData = output.ToArray(); } } Buffer = new byte[decompressedData.Length]; try { Array.Copy(decompressedData, 0, Buffer, 0, decompressedData.Length); } catch (ArgumentException e) { throw new ArgumentException( $"Failed to copy the incoming compressed buffer. Source buffer length: {decompressedData.Length}, start position: {0}, length: {decompressedData.Length}, destination buffer length: {Buffer.Length}.", e); } lengthBits = decompressedData.Length * 8; DebugConsole.Log("Decompressing message: " + byteLength + " to " + LengthBytes); } else { Buffer = new byte[inBuf.Length]; try { Array.Copy(inBuf, startPos, Buffer, 0, byteLength); } catch (ArgumentException e) { throw new ArgumentException($"Failed to copy the incoming uncompressed buffer. Source buffer length: {inBuf.Length}, start position: {startPos}, length: {byteLength}, destination buffer length: {Buffer.Length}.", e); } lengthBits = byteLength * 8; } seekPos = 0; } public bool ReadBoolean() { return MsgReader.ReadBoolean(Buffer, ref seekPos); } public void ReadPadBits() { MsgReader.ReadPadBits(ref seekPos); } public byte ReadByte() { return MsgReader.ReadByte(Buffer, ref seekPos); } public byte PeekByte() { return MsgReader.PeekByte(Buffer, ref seekPos); } public UInt16 ReadUInt16() { return MsgReader.ReadUInt16(Buffer, ref seekPos); } public Int16 ReadInt16() { return MsgReader.ReadInt16(Buffer, ref seekPos); } public UInt32 ReadUInt32() { return MsgReader.ReadUInt32(Buffer, ref seekPos); } public Int32 ReadInt32() { return MsgReader.ReadInt32(Buffer, ref seekPos); } public UInt64 ReadUInt64() { return MsgReader.ReadUInt64(Buffer, ref seekPos); } public Int64 ReadInt64() { return MsgReader.ReadInt64(Buffer, ref seekPos); } public Single ReadSingle() { return MsgReader.ReadSingle(Buffer, ref seekPos); } public Double ReadDouble() { return MsgReader.ReadDouble(Buffer, ref seekPos); } public UInt32 ReadVariableUInt32() { return MsgReader.ReadVariableUInt32(Buffer, ref seekPos); } public String ReadString() { return MsgReader.ReadString(Buffer, ref seekPos); } public Identifier ReadIdentifier() { return ReadString().ToIdentifier(); } public Color ReadColorR8G8B8() { return MsgReader.ReadColorR8G8B8(Buffer, ref seekPos); } public Color ReadColorR8G8B8A8() { return MsgReader.ReadColorR8G8B8A8(Buffer, ref seekPos); } public int ReadRangedInteger(int min, int max) { return MsgReader.ReadRangedInteger(Buffer, ref seekPos, min, max); } public Single ReadRangedSingle(Single min, Single max, int bitCount) { return MsgReader.ReadRangedSingle(Buffer, ref seekPos, min, max, bitCount); } public byte[] ReadBytes(int numberOfBytes) { return MsgReader.ReadBytes(Buffer, ref seekPos, numberOfBytes); } } internal sealed class ReadWriteMessage : IWriteMessage, IReadMessage { private byte[] buf; private int seekPos; private int lengthBits; public ReadWriteMessage() { buf = new byte[MsgConstants.InitialBufferSize]; seekPos = 0; lengthBits = 0; } public ReadWriteMessage(byte[] b, int bitPos, int lBits, bool copyBuf) { buf = copyBuf ? (byte[])b.Clone() : b; seekPos = bitPos; lengthBits = lBits; } public int BitPosition { get => seekPos; set => seekPos = value; } public int BytePosition => seekPos / 8; public byte[] Buffer => buf; public int LengthBits { get { lengthBits = seekPos > lengthBits ? seekPos : lengthBits; return lengthBits; } set { lengthBits = value; seekPos = seekPos > lengthBits ? lengthBits : seekPos; } } public int LengthBytes => (LengthBits + 7) / 8; public NetworkConnection Sender => null; public void WriteBoolean(bool val) { MsgWriter.WriteBoolean(ref buf, ref seekPos, ref lengthBits, val); } public void WritePadBits() { MsgWriter.WritePadBits(ref buf, ref seekPos, ref lengthBits); } public void WriteByte(byte val) { MsgWriter.WriteByte(ref buf, ref seekPos, ref lengthBits, val); } public void WriteUInt16(UInt16 val) { MsgWriter.WriteUInt16(ref buf, ref seekPos, ref lengthBits, val); } public void WriteInt16(Int16 val) { MsgWriter.WriteInt16(ref buf, ref seekPos, ref lengthBits, val); } public void WriteUInt32(UInt32 val) { MsgWriter.WriteUInt32(ref buf, ref seekPos, ref lengthBits, val); } public void WriteInt32(Int32 val) { MsgWriter.WriteInt32(ref buf, ref seekPos, ref lengthBits, val); } public void WriteUInt64(UInt64 val) { MsgWriter.WriteUInt64(ref buf, ref seekPos, ref lengthBits, val); } public void WriteInt64(Int64 val) { MsgWriter.WriteInt64(ref buf, ref seekPos, ref lengthBits, val); } public void WriteSingle(Single val) { MsgWriter.WriteSingle(ref buf, ref seekPos, ref lengthBits, val); } public void WriteDouble(Double val) { MsgWriter.WriteDouble(ref buf, ref seekPos, ref lengthBits, val); } public void WriteColorR8G8B8(Color val) { MsgWriter.WriteColorR8G8B8(ref buf, ref seekPos, ref lengthBits, val); } public void WriteColorR8G8B8A8(Color val) { MsgWriter.WriteColorR8G8B8A8(ref buf, ref seekPos, ref lengthBits, val); } public void WriteVariableUInt32(UInt32 val) { MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, ref lengthBits, val); } public void WriteString(String val) { MsgWriter.WriteString(ref buf, ref seekPos, ref lengthBits, val); } public void WriteIdentifier(Identifier val) { WriteString(val.Value); } public void WriteRangedInteger(int val, int min, int max) { MsgWriter.WriteRangedInteger(ref buf, ref seekPos, ref lengthBits, val, min, max); } public void WriteRangedSingle(Single val, Single min, Single max, int bitCount) { MsgWriter.WriteRangedSingle(ref buf, ref seekPos, ref lengthBits, val, min, max, bitCount); } public void WriteBytes(byte[] val, int startPos, int length) { MsgWriter.WriteBytes(ref buf, ref seekPos, ref lengthBits, val, startPos, length); } public bool ReadBoolean() { return MsgReader.ReadBoolean(buf, ref seekPos); } public void ReadPadBits() { MsgReader.ReadPadBits(ref seekPos); } public byte ReadByte() { return MsgReader.ReadByte(buf, ref seekPos); } public byte PeekByte() { return MsgReader.PeekByte(buf, ref seekPos); } public UInt16 ReadUInt16() { return MsgReader.ReadUInt16(buf, ref seekPos); } public Int16 ReadInt16() { return MsgReader.ReadInt16(buf, ref seekPos); } public UInt32 ReadUInt32() { return MsgReader.ReadUInt32(buf, ref seekPos); } public Int32 ReadInt32() { return MsgReader.ReadInt32(buf, ref seekPos); } public UInt64 ReadUInt64() { return MsgReader.ReadUInt64(buf, ref seekPos); } public Int64 ReadInt64() { return MsgReader.ReadInt64(buf, ref seekPos); } public Single ReadSingle() { return MsgReader.ReadSingle(buf, ref seekPos); } public Double ReadDouble() { return MsgReader.ReadDouble(buf, ref seekPos); } public UInt32 ReadVariableUInt32() { return MsgReader.ReadVariableUInt32(buf, ref seekPos); } public String ReadString() { return MsgReader.ReadString(buf, ref seekPos); } public Identifier ReadIdentifier() { return ReadString().ToIdentifier(); } public Color ReadColorR8G8B8() { return MsgReader.ReadColorR8G8B8(buf, ref seekPos); } public Color ReadColorR8G8B8A8() { return MsgReader.ReadColorR8G8B8A8(buf, ref seekPos); } public int ReadRangedInteger(int min, int max) { return MsgReader.ReadRangedInteger(buf, ref seekPos, min, max); } public Single ReadRangedSingle(Single min, Single max, int bitCount) { return MsgReader.ReadRangedSingle(buf, ref seekPos, min, max, bitCount); } public byte[] ReadBytes(int numberOfBytes) { return MsgReader.ReadBytes(buf, ref seekPos, numberOfBytes); } public byte[] PrepareForSending(bool compressPastThreshold, out bool isCompressed, out int outLength) { throw new InvalidOperationException("ReadWriteMessages are not to be sent"); } } }