1034 lines
32 KiB
C#
1034 lines
32 KiB
C#
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Utility struct for writing Singles
|
|
/// </summary>
|
|
[StructLayout(LayoutKind.Explicit)]
|
|
public struct SingleUIntUnion
|
|
{
|
|
/// <summary>
|
|
/// Value as a 32 bit float
|
|
/// </summary>
|
|
[FieldOffset(0)]
|
|
public float SingleValue;
|
|
|
|
/// <summary>
|
|
/// Value as an unsigned 32 bit integer
|
|
/// </summary>
|
|
[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");
|
|
}
|
|
|
|
}
|
|
} |