From 0a0e319f21e03b26311360c01f2a379fd46366e4 Mon Sep 17 00:00:00 2001 From: lidgren Date: Fri, 23 Mar 2012 08:59:32 +0000 Subject: [PATCH] NetIncomingMessage and NetOutgoingMessage common parts unified in NetBuffer; which is now a base class for the former --- Lidgren.Network/Lidgren.Network.csproj | 12 +- Lidgren.Network/NetBuffer.Peek.cs | 312 +++++++++ Lidgren.Network/NetBuffer.Read.Reflection.cs | 103 +++ Lidgren.Network/NetBuffer.Read.cs | 641 ++++++++++++++++++ Lidgren.Network/NetBuffer.Write.Reflection.cs | 91 +++ Lidgren.Network/NetBuffer.Write.cs | 614 +++++++++++++++++ Lidgren.Network/NetBuffer.cs | 103 +++ Lidgren.Network/NetConnection.Handshake.cs | 4 +- Lidgren.Network/NetIncomingMessage.cs | 21 +- Lidgren.Network/NetOutgoingMessage.Write.cs | 11 - Lidgren.Network/NetOutgoingMessage.cs | 2 +- Samples/Chat/Chat.sln | 24 +- Samples/Chat/ChatClient/ChatClient.csproj | 4 - Samples/Chat/ChatServer/ChatServer.csproj | 4 - .../ImageClient/ImageClient.csproj | 4 - Samples/ImageSample/ImageSample.sln | 24 +- .../ImageServer/ImageServer.csproj | 4 - .../Properties/Resources.Designer.cs | 114 ++-- .../Properties/Settings.Designer.cs | 36 +- .../SpeedClient/SpeedTestClient.csproj | 46 +- .../SpeedSample/SpeedSample.sln | 72 +- .../Properties/Resources.Designer.cs | 114 ++-- .../Properties/Settings.Designer.cs | 36 +- .../SpeedServer/SpeedTestServer.csproj | 49 +- UnitTests/ReadWriteTests.cs | 6 +- 25 files changed, 2165 insertions(+), 286 deletions(-) create mode 100644 Lidgren.Network/NetBuffer.Peek.cs create mode 100644 Lidgren.Network/NetBuffer.Read.Reflection.cs create mode 100644 Lidgren.Network/NetBuffer.Read.cs create mode 100644 Lidgren.Network/NetBuffer.Write.Reflection.cs create mode 100644 Lidgren.Network/NetBuffer.Write.cs create mode 100644 Lidgren.Network/NetBuffer.cs diff --git a/Lidgren.Network/Lidgren.Network.csproj b/Lidgren.Network/Lidgren.Network.csproj index 2efa7db..bf9fde3 100644 --- a/Lidgren.Network/Lidgren.Network.csproj +++ b/Lidgren.Network/Lidgren.Network.csproj @@ -67,6 +67,12 @@ + + + + + + @@ -79,16 +85,10 @@ - - - - - - diff --git a/Lidgren.Network/NetBuffer.Peek.cs b/Lidgren.Network/NetBuffer.Peek.cs new file mode 100644 index 0000000..7e18529 --- /dev/null +++ b/Lidgren.Network/NetBuffer.Peek.cs @@ -0,0 +1,312 @@ +/* Copyright (c) 2010 Michael Lidgren + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +using System; +using System.Diagnostics; +using System.Net; + +namespace Lidgren.Network +{ + public partial class NetBuffer + { + /// + /// Gets the internal data buffer + /// + public byte[] PeekDataBuffer() { return m_data; } + + // + // 1 bit + // + /// + /// Reads a 1-bit Boolean without advancing the read pointer + /// + public bool PeekBoolean() + { + NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError); + byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition); + return (retval > 0 ? true : false); + } + + // + // 8 bit + // + /// + /// Reads a Byte without advancing the read pointer + /// + public byte PeekByte() + { + NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError); + byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + return retval; + } + + /// + /// Reads an SByte without advancing the read pointer + /// + [CLSCompliant(false)] + public sbyte PeekSByte() + { + NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError); + byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + return (sbyte)retval; + } + + /// + /// Reads the specified number of bits into a Byte without advancing the read pointer + /// + public byte PeekByte(int numberOfBits) + { + byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition); + return retval; + } + + /// + /// Reads the specified number of bytes without advancing the read pointer + /// + public byte[] PeekBytes(int numberOfBytes) + { + NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError); + + byte[] retval = new byte[numberOfBytes]; + NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0); + return retval; + } + + /// + /// Reads the specified number of bytes without advancing the read pointer + /// + public void PeekBytes(byte[] into, int offset, int numberOfBytes) + { + NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError); + NetException.Assert(offset + numberOfBytes <= into.Length); + + NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset); + return; + } + + // + // 16 bit + // + /// + /// Reads an Int16 without advancing the read pointer + /// + public Int16 PeekInt16() + { + NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError); + uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition); + return (short)retval; + } + + /// + /// Reads a UInt16 without advancing the read pointer + /// + [CLSCompliant(false)] + public UInt16 PeekUInt16() + { + NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError); + uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition); + return (ushort)retval; + } + + // + // 32 bit + // + /// + /// Reads an Int32 without advancing the read pointer + /// + public Int32 PeekInt32() + { + NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); + uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + return (Int32)retval; + } + + /// + /// Reads the specified number of bits into an Int32 without advancing the read pointer + /// + public Int32 PeekInt32(int numberOfBits) + { + NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadInt() can only read between 1 and 32 bits"); + NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); + + uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + + if (numberOfBits == 32) + return (int)retval; + + int signBit = 1 << (numberOfBits - 1); + if ((retval & signBit) == 0) + return (int)retval; // positive + + // negative + unchecked + { + uint mask = ((uint)-1) >> (33 - numberOfBits); + uint tmp = (retval & mask) + 1; + return -((int)tmp); + } + } + + /// + /// Reads a UInt32 without advancing the read pointer + /// + [CLSCompliant(false)] + public UInt32 PeekUInt32() + { + NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); + uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + return retval; + } + + /// + /// Reads the specified number of bits into a UInt32 without advancing the read pointer + /// + [CLSCompliant(false)] + public UInt32 PeekUInt32(int numberOfBits) + { + NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadUInt() can only read between 1 and 32 bits"); + //NetException.Assert(m_bitLength - m_readBitPtr >= numberOfBits, "tried to read past buffer size"); + + UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + return retval; + } + + // + // 64 bit + // + /// + /// Reads a UInt64 without advancing the read pointer + /// + [CLSCompliant(false)] + public UInt64 PeekUInt64() + { + NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); + + ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition + 32); + + ulong retval = low + (high << 32); + + return retval; + } + + /// + /// Reads an Int64 without advancing the read pointer + /// + public Int64 PeekInt64() + { + NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); + unchecked + { + ulong retval = PeekUInt64(); + long longRetval = (long)retval; + return longRetval; + } + } + + /// + /// Reads the specified number of bits into an UInt64 without advancing the read pointer + /// + [CLSCompliant(false)] + public UInt64 PeekUInt64(int numberOfBits) + { + NetException.Assert((numberOfBits > 0 && numberOfBits <= 64), "ReadUInt() can only read between 1 and 64 bits"); + NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); + + ulong retval; + if (numberOfBits <= 32) + { + retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + } + else + { + retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32; + } + return retval; + } + + /// + /// Reads the specified number of bits into an Int64 without advancing the read pointer + /// + public Int64 PeekInt64(int numberOfBits) + { + NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits"); + return (long)PeekUInt64(numberOfBits); + } + + // + // Floating point + // + /// + /// Reads a 32-bit Single without advancing the read pointer + /// + public float PeekFloat() + { + return PeekSingle(); + } + + /// + /// Reads a 32-bit Single without advancing the read pointer + /// + public float PeekSingle() + { + NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); + + if ((m_readPosition & 7) == 0) // read directly + { + float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3); + return retval; + } + + byte[] bytes = PeekBytes(4); + return BitConverter.ToSingle(bytes, 0); + } + + /// + /// Reads a 64-bit Double without advancing the read pointer + /// + public double PeekDouble() + { + NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); + + if ((m_readPosition & 7) == 0) // read directly + { + // read directly + double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3); + return retval; + } + + byte[] bytes = PeekBytes(8); + return BitConverter.ToDouble(bytes, 0); + } + + /// + /// Reads a string without advancing the read pointer + /// + public string PeekString() + { + int wasReadPosition = m_readPosition; + string retval = ReadString(); + m_readPosition = wasReadPosition; + return retval; + } + } +} + diff --git a/Lidgren.Network/NetBuffer.Read.Reflection.cs b/Lidgren.Network/NetBuffer.Read.Reflection.cs new file mode 100644 index 0000000..b34fbec --- /dev/null +++ b/Lidgren.Network/NetBuffer.Read.Reflection.cs @@ -0,0 +1,103 @@ +/* Copyright (c) 2010 Michael Lidgren + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +using System; +using System.Reflection; + +namespace Lidgren.Network +{ + public partial class NetBuffer + { + /// + /// Reads all public and private declared instance fields of the object in alphabetical order using reflection + /// + public void ReadAllFields(object target) + { + ReadAllFields(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + + /// + /// Reads all fields with the specified binding of the object in alphabetical order using reflection + /// + public void ReadAllFields(object target, BindingFlags flags) + { + if (target == null) + return; + Type tp = target.GetType(); + + FieldInfo[] fields = tp.GetFields(flags); + NetUtility.SortMembersList(fields); + + foreach (FieldInfo fi in fields) + { + object value; + + // find read method + MethodInfo readMethod; + if (s_readMethods.TryGetValue(fi.FieldType, out readMethod)) + { + // read value + value = readMethod.Invoke(this, null); + + // set the value + fi.SetValue(target, value); + } + } + } + + /// + /// Reads all public and private declared instance fields of the object in alphabetical order using reflection + /// + public void ReadAllProperties(object target) + { + ReadAllProperties(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + + /// + /// Reads all fields with the specified binding of the object in alphabetical order using reflection + /// + public void ReadAllProperties(object target, BindingFlags flags) + { + if (target == null) + throw new ArgumentNullException("target"); + + if (target == null) + return; + Type tp = target.GetType(); + + PropertyInfo[] fields = tp.GetProperties(flags); + NetUtility.SortMembersList(fields); + foreach (PropertyInfo fi in fields) + { + object value; + + // find read method + MethodInfo readMethod; + if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod)) + { + // read value + value = readMethod.Invoke(this, null); + + // set the value + MethodInfo setMethod = fi.GetSetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic); + setMethod.Invoke(target, new object[] { value }); + } + } + } + } +} diff --git a/Lidgren.Network/NetBuffer.Read.cs b/Lidgren.Network/NetBuffer.Read.cs new file mode 100644 index 0000000..6b766a3 --- /dev/null +++ b/Lidgren.Network/NetBuffer.Read.cs @@ -0,0 +1,641 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using System.Net; + +namespace Lidgren.Network +{ + public partial class NetBuffer + { + private const string c_readOverflowError = "Trying to read past the buffer size - likely caused by mismatching Write/Reads, different size or order."; + + /// + /// Reads a boolean value (stored as a single bit) written using Write(bool) + /// + public bool ReadBoolean() + { + NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError); + byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition); + m_readPosition += 1; + return (retval > 0 ? true : false); + } + + /// + /// Reads a byte + /// + public byte ReadByte() + { + NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError); + byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + m_readPosition += 8; + return retval; + } + + /// + /// Reads a byte and returns true or false for success + /// + public bool ReadByte(out byte result) + { + if (m_bitLength - m_readPosition < 8) + { + result = 0; + return false; + } + result = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + m_readPosition += 8; + return true; + } + + /// + /// Reads a signed byte + /// + [CLSCompliant(false)] + public sbyte ReadSByte() + { + NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError); + byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + m_readPosition += 8; + return (sbyte)retval; + } + + /// + /// Reads 1 to 8 bits into a byte + /// + public byte ReadByte(int numberOfBits) + { + NetException.Assert(numberOfBits > 0 && numberOfBits <= 8, "ReadByte(bits) can only read between 1 and 8 bits"); + byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition); + m_readPosition += numberOfBits; + return retval; + } + + /// + /// Reads the specified number of bytes + /// + public byte[] ReadBytes(int numberOfBytes) + { + NetException.Assert(m_bitLength - m_readPosition + 7 >= (numberOfBytes * 8), c_readOverflowError); + + byte[] retval = new byte[numberOfBytes]; + NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0); + m_readPosition += (8 * numberOfBytes); + return retval; + } + + /// + /// Reads the specified number of bytes and returns true for success + /// + public bool ReadBytes(int numberOfBytes, out byte[] result) + { + if (m_bitLength - m_readPosition + 7 < (numberOfBytes * 8)) + { + result = null; + return false; + } + + result = new byte[numberOfBytes]; + NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, result, 0); + m_readPosition += (8 * numberOfBytes); + return true; + } + + /// + /// Reads the specified number of bytes into a preallocated array + /// + /// The destination array + /// The offset where to start writing in the destination array + /// The number of bytes to read + public void ReadBytes(byte[] into, int offset, int numberOfBytes) + { + NetException.Assert(m_bitLength - m_readPosition + 7 >= (numberOfBytes * 8), c_readOverflowError); + NetException.Assert(offset + numberOfBytes <= into.Length); + + NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset); + m_readPosition += (8 * numberOfBytes); + return; + } + + /// + /// Reads the specified number of bits into a preallocated array + /// + /// The destination array + /// The offset where to start writing in the destination array + /// The number of bits to read + public void ReadBits(byte[] into, int offset, int numberOfBits) + { + NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); + NetException.Assert(offset + NetUtility.BytesToHoldBits(numberOfBits) <= into.Length); + + int numberOfWholeBytes = numberOfBits / 8; + int extraBits = numberOfBits - (numberOfWholeBytes * 8); + + NetBitWriter.ReadBytes(m_data, numberOfWholeBytes, m_readPosition, into, offset); + m_readPosition += (8 * numberOfWholeBytes); + + if (extraBits > 0) + into[offset + numberOfWholeBytes] = ReadByte(extraBits); + + return; + } + + /// + /// Reads a 16 bit signed integer written using Write(Int16) + /// + public Int16 ReadInt16() + { + NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError); + uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition); + m_readPosition += 16; + return (short)retval; + } + + /// + /// Reads a 16 bit unsigned integer written using Write(UInt16) + /// + [CLSCompliant(false)] + public UInt16 ReadUInt16() + { + NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError); + uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition); + m_readPosition += 16; + return (ushort)retval; + } + + /// + /// Reads a 32 bit signed integer written using Write(Int32) + /// + public Int32 ReadInt32() + { + NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); + uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + m_readPosition += 32; + return (Int32)retval; + } + + /// + /// Reads a 32 bit signed integer written using Write(Int32) + /// + [CLSCompliant(false)] + public bool ReadInt32(out Int32 result) + { + if (m_bitLength - m_readPosition < 32) + { + result = 0; + return false; + } + + result = (Int32)NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + m_readPosition += 32; + return true; + } + + /// + /// Reads a signed integer stored in 1 to 32 bits, written using Write(Int32, Int32) + /// + public Int32 ReadInt32(int numberOfBits) + { + NetException.Assert(numberOfBits > 0 && numberOfBits <= 32, "ReadInt32(bits) can only read between 1 and 32 bits"); + NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); + + uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + m_readPosition += numberOfBits; + + if (numberOfBits == 32) + return (int)retval; + + int signBit = 1 << (numberOfBits - 1); + if ((retval & signBit) == 0) + return (int)retval; // positive + + // negative + unchecked + { + uint mask = ((uint)-1) >> (33 - numberOfBits); + uint tmp = (retval & mask) + 1; + return -((int)tmp); + } + } + + /// + /// Reads an 32 bit unsigned integer written using Write(UInt32) + /// + [CLSCompliant(false)] + public UInt32 ReadUInt32() + { + NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); + uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + m_readPosition += 32; + return retval; + } + + /// + /// Reads an 32 bit unsigned integer written using Write(UInt32) and returns true for success + /// + [CLSCompliant(false)] + public bool ReadUInt32(out UInt32 result) + { + if (m_bitLength - m_readPosition < 32) + { + result = 0; + return false; + } + result = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + m_readPosition += 32; + return true; + } + + /// + /// Reads an unsigned integer stored in 1 to 32 bits, written using Write(UInt32, Int32) + /// + [CLSCompliant(false)] + public UInt32 ReadUInt32(int numberOfBits) + { + NetException.Assert(numberOfBits > 0 && numberOfBits <= 32, "ReadUInt32(bits) can only read between 1 and 32 bits"); + //NetException.Assert(m_bitLength - m_readBitPtr >= numberOfBits, "tried to read past buffer size"); + + UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + m_readPosition += numberOfBits; + return retval; + } + + /// + /// Reads a 64 bit unsigned integer written using Write(UInt64) + /// + [CLSCompliant(false)] + public UInt64 ReadUInt64() + { + NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); + + ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + m_readPosition += 32; + ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + + ulong retval = low + (high << 32); + + m_readPosition += 32; + return retval; + } + + /// + /// Reads a 64 bit signed integer written using Write(Int64) + /// + public Int64 ReadInt64() + { + NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); + unchecked + { + ulong retval = ReadUInt64(); + long longRetval = (long)retval; + return longRetval; + } + } + + /// + /// Reads an unsigned integer stored in 1 to 64 bits, written using Write(UInt64, Int32) + /// + [CLSCompliant(false)] + public UInt64 ReadUInt64(int numberOfBits) + { + NetException.Assert(numberOfBits > 0 && numberOfBits <= 64, "ReadUInt64(bits) can only read between 1 and 64 bits"); + NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); + + ulong retval; + if (numberOfBits <= 32) + { + retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + } + else + { + retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32; + } + m_readPosition += numberOfBits; + return retval; + } + + /// + /// Reads a signed integer stored in 1 to 64 bits, written using Write(Int64, Int32) + /// + public Int64 ReadInt64(int numberOfBits) + { + NetException.Assert(((numberOfBits > 0) && (numberOfBits <= 64)), "ReadInt64(bits) can only read between 1 and 64 bits"); + return (long)ReadUInt64(numberOfBits); + } + + /// + /// Reads a 32 bit floating point value written using Write(Single) + /// + public float ReadFloat() + { + return ReadSingle(); + } + + /// + /// Reads a 32 bit floating point value written using Write(Single) + /// + public float ReadSingle() + { + NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); + + if ((m_readPosition & 7) == 0) // read directly + { + float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3); + m_readPosition += 32; + return retval; + } + + byte[] bytes = ReadBytes(4); + return BitConverter.ToSingle(bytes, 0); + } + + /// + /// Reads a 32 bit floating point value written using Write(Single) + /// + public bool ReadSingle(out float result) + { + if (m_bitLength - m_readPosition < 32) + { + result = 0.0f; + return false; + } + + if ((m_readPosition & 7) == 0) // read directly + { + result = BitConverter.ToSingle(m_data, m_readPosition >> 3); + m_readPosition += 32; + return true; + } + + byte[] bytes = ReadBytes(4); + result = BitConverter.ToSingle(bytes, 0); + return true; + } + + /// + /// Reads a 64 bit floating point value written using Write(Double) + /// + public double ReadDouble() + { + NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); + + if ((m_readPosition & 7) == 0) // read directly + { + // read directly + double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3); + m_readPosition += 64; + return retval; + } + + byte[] bytes = ReadBytes(8); + return BitConverter.ToDouble(bytes, 0); + } + + // + // Variable bit count + // + + /// + /// Reads a variable sized UInt32 written using WriteVariableUInt32() + /// + [CLSCompliant(false)] + public uint ReadVariableUInt32() + { + int num1 = 0; + int num2 = 0; + while (true) + { + byte num3 = this.ReadByte(); + num1 |= (num3 & 0x7f) << num2; + num2 += 7; + if ((num3 & 0x80) == 0) + return (uint)num1; + } + } + + /// + /// Reads a variable sized UInt32 written using WriteVariableUInt32() and returns true for success + /// + [CLSCompliant(false)] + public bool ReadVariableUInt32(out uint result) + { + int num1 = 0; + int num2 = 0; + while (true) + { + byte num3; + if (ReadByte(out num3) == false) + { + result = 0; + return false; + } + num1 |= (num3 & 0x7f) << num2; + num2 += 7; + if ((num3 & 0x80) == 0) + { + result = (uint)num1; + return true; + } + } + } + + /// + /// Reads a variable sized Int32 written using WriteVariableInt32() + /// + public int ReadVariableInt32() + { + uint n = ReadVariableUInt32(); + return (int)(n >> 1) ^ -(int)(n & 1); // decode zigzag + } + + /// + /// Reads a variable sized Int64 written using WriteVariableInt64() + /// + public Int64 ReadVariableInt64() + { + UInt64 n = ReadVariableUInt64(); + return (Int64)(n >> 1) ^ -(long)(n & 1); // decode zigzag + } + + /// + /// Reads a variable sized UInt32 written using WriteVariableInt64() + /// + [CLSCompliant(false)] + public UInt64 ReadVariableUInt64() + { + UInt64 num1 = 0; + int num2 = 0; + while (true) + { + //if (num2 == 0x23) + // throw new FormatException("Bad 7-bit encoded integer"); + + byte num3 = this.ReadByte(); + num1 |= ((UInt64)num3 & 0x7f) << num2; + num2 += 7; + if ((num3 & 0x80) == 0) + return num1; + } + } + + /// + /// Reads a 32 bit floating point value written using WriteSignedSingle() + /// + /// The number of bits used when writing the value + /// A floating point value larger or equal to -1 and smaller or equal to 1 + public float ReadSignedSingle(int numberOfBits) + { + uint encodedVal = ReadUInt32(numberOfBits); + int maxVal = (1 << numberOfBits) - 1; + return ((float)(encodedVal + 1) / (float)(maxVal + 1) - 0.5f) * 2.0f; + } + + /// + /// Reads a 32 bit floating point value written using WriteUnitSingle() + /// + /// The number of bits used when writing the value + /// A floating point value larger or equal to 0 and smaller or equal to 1 + public float ReadUnitSingle(int numberOfBits) + { + uint encodedVal = ReadUInt32(numberOfBits); + int maxVal = (1 << numberOfBits) - 1; + return (float)(encodedVal + 1) / (float)(maxVal + 1); + } + + /// + /// Reads a 32 bit floating point value written using WriteRangedSingle() + /// + /// The minimum value used when writing the value + /// The maximum value used when writing the value + /// The number of bits used when writing the value + /// A floating point value larger or equal to MIN and smaller or equal to MAX + public float ReadRangedSingle(float min, float max, int numberOfBits) + { + float range = max - min; + int maxVal = (1 << numberOfBits) - 1; + float encodedVal = (float)ReadUInt32(numberOfBits); + float unit = encodedVal / (float)maxVal; + return min + (unit * range); + } + + /// + /// Reads a 32 bit integer value written using WriteRangedInteger() + /// + /// The minimum value used when writing the value + /// The maximum value used when writing the value + /// A signed integer value larger or equal to MIN and smaller or equal to MAX + public int ReadRangedInteger(int min, int max) + { + uint range = (uint)(max - min); + int numBits = NetUtility.BitsToHoldUInt(range); + + uint rvalue = ReadUInt32(numBits); + return (int)(min + rvalue); + } + + /// + /// Reads a string written using Write(string) + /// + public string ReadString() + { + int byteLen = (int)ReadVariableUInt32(); + + if (byteLen == 0) + return String.Empty; + + NetException.Assert(m_bitLength - m_readPosition >= (byteLen * 8), c_readOverflowError); + + if ((m_readPosition & 7) == 0) + { + // read directly + string retval = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, byteLen); + m_readPosition += (8 * byteLen); + return retval; + } + + byte[] bytes = ReadBytes(byteLen); + return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length); + } + + /// + /// Reads a string written using Write(string) and returns true for success + /// + public bool ReadString(out string result) + { + uint byteLen; + if (ReadVariableUInt32(out byteLen) == false) + { + result = String.Empty; + return false; + } + + if (byteLen == 0) + { + result = String.Empty; + return true; + } + + if (m_bitLength - m_readPosition < (byteLen * 8)) + { + result = String.Empty; + return false; + } + + if ((m_readPosition & 7) == 0) + { + // read directly + result = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, (int)byteLen); + m_readPosition += (8 * (int)byteLen); + return true; + } + + byte[] bytes; + if (ReadBytes((int)byteLen, out bytes) == false) + { + result = String.Empty; + return false; + } + + result = System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length); + return true; + } + + /// + /// Reads a stored IPv4 endpoint description + /// + public IPEndPoint ReadIPEndpoint() + { + byte len = ReadByte(); + byte[] addressBytes = ReadBytes(len); + int port = (int)ReadUInt16(); + + IPAddress address = new IPAddress(addressBytes); + return new IPEndPoint(address, port); + } + + /// + /// Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes. + /// + public void SkipPadBits() + { + m_readPosition = ((m_readPosition + 7) >> 3) * 8; + } + + /// + /// Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes. + /// + public void ReadPadBits() + { + m_readPosition = ((m_readPosition + 7) >> 3) * 8; + } + + /// + /// Pads data with the specified number of bits. + /// + public void SkipPadBits(int numberOfBits) + { + m_readPosition += numberOfBits; + } + } +} diff --git a/Lidgren.Network/NetBuffer.Write.Reflection.cs b/Lidgren.Network/NetBuffer.Write.Reflection.cs new file mode 100644 index 0000000..47142b5 --- /dev/null +++ b/Lidgren.Network/NetBuffer.Write.Reflection.cs @@ -0,0 +1,91 @@ +/* Copyright (c) 2010 Michael Lidgren + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +using System; +using System.Reflection; + +namespace Lidgren.Network +{ + public partial class NetBuffer + { + /// + /// Writes all public and private declared instance fields of the object in alphabetical order using reflection + /// + public void WriteAllFields(object ob) + { + WriteAllFields(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + + /// + /// Writes all fields with specified binding in alphabetical order using reflection + /// + public void WriteAllFields(object ob, BindingFlags flags) + { + if (ob == null) + return; + Type tp = ob.GetType(); + + FieldInfo[] fields = tp.GetFields(flags); + NetUtility.SortMembersList(fields); + + foreach (FieldInfo fi in fields) + { + object value = fi.GetValue(ob); + + // find the appropriate Write method + MethodInfo writeMethod; + if (s_writeMethods.TryGetValue(fi.FieldType, out writeMethod)) + writeMethod.Invoke(this, new object[] { value }); + else + throw new NetException("Failed to find write method for type " + fi.FieldType); + } + } + + /// + /// Writes all public and private declared instance properties of the object in alphabetical order using reflection + /// + public void WriteAllProperties(object ob) + { + WriteAllProperties(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + + /// + /// Writes all properties with specified binding in alphabetical order using reflection + /// + public void WriteAllProperties(object ob, BindingFlags flags) + { + if (ob == null) + return; + Type tp = ob.GetType(); + + PropertyInfo[] fields = tp.GetProperties(flags); + NetUtility.SortMembersList(fields); + + foreach (PropertyInfo fi in fields) + { + MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic); + object value = getMethod.Invoke(ob, null); + + // find the appropriate Write method + MethodInfo writeMethod; + if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod)) + writeMethod.Invoke(this, new object[] { value }); + } + } + } +} \ No newline at end of file diff --git a/Lidgren.Network/NetBuffer.Write.cs b/Lidgren.Network/NetBuffer.Write.cs new file mode 100644 index 0000000..2052483 --- /dev/null +++ b/Lidgren.Network/NetBuffer.Write.cs @@ -0,0 +1,614 @@ +/* Copyright (c) 2010 Michael Lidgren + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +using System; +using System.Collections.Generic; +using System.Net; +using System.Reflection; +using System.Text; +using System.Runtime.InteropServices; + +namespace Lidgren.Network +{ + [StructLayout(LayoutKind.Explicit)] + public struct SingleUIntUnion + { + [FieldOffset(0)] + public float SingleValue; + + [FieldOffset(0)] + [CLSCompliant(false)] + public uint UIntValue; + } + + public partial class NetBuffer + { + /// + /// Ensures the buffer can hold this number of bits + /// + public void EnsureBufferSize(int numberOfBits) + { + int byteLen = ((numberOfBits + 7) >> 3); + if (m_data == null) + { + m_data = new byte[byteLen + c_overAllocateAmount]; + return; + } + if (m_data.Length < byteLen) + Array.Resize(ref m_data, byteLen + c_overAllocateAmount); + return; + } + + /// + /// Ensures the buffer can hold this number of bits + /// + internal void InternalEnsureBufferSize(int numberOfBits) + { + int byteLen = ((numberOfBits + 7) >> 3); + if (m_data == null) + { + m_data = new byte[byteLen]; + return; + } + if (m_data.Length < byteLen) + Array.Resize(ref m_data, byteLen); + return; + } + + /// + /// Writes a boolean value using 1 bit + /// + public void Write(bool value) + { + EnsureBufferSize(m_bitLength + 1); + NetBitWriter.WriteByte((value ? (byte)1 : (byte)0), 1, m_data, m_bitLength); + m_bitLength += 1; + } + + /// + /// Write a byte + /// + public void Write(byte source) + { + EnsureBufferSize(m_bitLength + 8); + NetBitWriter.WriteByte(source, 8, m_data, m_bitLength); + m_bitLength += 8; + } + + /// + /// Writes a signed byte + /// + [CLSCompliant(false)] + public void Write(sbyte source) + { + EnsureBufferSize(m_bitLength + 8); + NetBitWriter.WriteByte((byte)source, 8, m_data, m_bitLength); + m_bitLength += 8; + } + + /// + /// Writes 1 to 8 bits of a byte + /// + public void Write(byte source, int numberOfBits) + { + NetException.Assert((numberOfBits > 0 && numberOfBits <= 8), "Write(byte, numberOfBits) can only write between 1 and 8 bits"); + EnsureBufferSize(m_bitLength + numberOfBits); + NetBitWriter.WriteByte(source, numberOfBits, m_data, m_bitLength); + m_bitLength += numberOfBits; + } + + /// + /// Writes all bytes in an array + /// + public void Write(byte[] source) + { + if (source == null) + throw new ArgumentNullException("source"); + int bits = source.Length * 8; + EnsureBufferSize(m_bitLength + bits); + NetBitWriter.WriteBytes(source, 0, source.Length, m_data, m_bitLength); + m_bitLength += bits; + } + + /// + /// Writes the specified number of bytes from an array + /// + public void Write(byte[] source, int offsetInBytes, int numberOfBytes) + { + if (source == null) + throw new ArgumentNullException("source"); + int bits = numberOfBytes * 8; + EnsureBufferSize(m_bitLength + bits); + NetBitWriter.WriteBytes(source, offsetInBytes, numberOfBytes, m_data, m_bitLength); + m_bitLength += bits; + } + + /// + /// Writes an unsigned 16 bit integer + /// + /// + [CLSCompliant(false)] + public void Write(UInt16 source) + { + EnsureBufferSize(m_bitLength + 16); + NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength); + m_bitLength += 16; + } + + /// + /// Writes an unsigned integer using 1 to 16 bits + /// + [CLSCompliant(false)] + public void Write(UInt16 source, int numberOfBits) + { + NetException.Assert((numberOfBits > 0 && numberOfBits <= 16), "Write(ushort, numberOfBits) can only write between 1 and 16 bits"); + EnsureBufferSize(m_bitLength + numberOfBits); + NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength); + m_bitLength += numberOfBits; + } + + /// + /// Writes a signed 16 bit integer + /// + public void Write(Int16 source) + { + EnsureBufferSize(m_bitLength + 16); + NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength); + m_bitLength += 16; + } + +#if UNSAFE + /// + /// Writes a 32 bit signed integer + /// + public unsafe void Write(Int32 source) + { + EnsureBufferSize(m_bitLength + 32); + + // can write fast? + if (m_bitLength % 8 == 0) + { + fixed (byte* numRef = &Data[m_bitLength / 8]) + { + *((int*)numRef) = source; + } + } + else + { + NetBitWriter.WriteUInt32((UInt32)source, 32, Data, m_bitLength); + } + m_bitLength += 32; + } +#else + /// + /// Writes a 32 bit signed integer + /// + public void Write(Int32 source) + { + EnsureBufferSize(m_bitLength + 32); + NetBitWriter.WriteUInt32((UInt32)source, 32, m_data, m_bitLength); + m_bitLength += 32; + } +#endif + +#if UNSAFE + /// + /// Writes a 32 bit unsigned integer + /// + public unsafe void Write(UInt32 source) + { + EnsureBufferSize(m_bitLength + 32); + + // can write fast? + if (m_bitLength % 8 == 0) + { + fixed (byte* numRef = &Data[m_bitLength / 8]) + { + *((uint*)numRef) = source; + } + } + else + { + NetBitWriter.WriteUInt32(source, 32, Data, m_bitLength); + } + + m_bitLength += 32; + } +#else + /// + /// Writes a 32 bit unsigned integer + /// + [CLSCompliant(false)] + public void Write(UInt32 source) + { + EnsureBufferSize(m_bitLength + 32); + NetBitWriter.WriteUInt32(source, 32, m_data, m_bitLength); + m_bitLength += 32; + } +#endif + + /// + /// Writes a 32 bit signed integer + /// + [CLSCompliant(false)] + public void Write(UInt32 source, int numberOfBits) + { + NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(uint, numberOfBits) can only write between 1 and 32 bits"); + EnsureBufferSize(m_bitLength + numberOfBits); + NetBitWriter.WriteUInt32(source, numberOfBits, m_data, m_bitLength); + m_bitLength += numberOfBits; + } + + /// + /// Writes a signed integer using 1 to 32 bits + /// + public void Write(Int32 source, int numberOfBits) + { + NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(int, numberOfBits) can only write between 1 and 32 bits"); + EnsureBufferSize(m_bitLength + numberOfBits); + + if (numberOfBits != 32) + { + // make first bit sign + int signBit = 1 << (numberOfBits - 1); + if (source < 0) + source = (-source - 1) | signBit; + else + source &= (~signBit); + } + + NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength); + + m_bitLength += numberOfBits; + } + + /// + /// Writes a 64 bit unsigned integer + /// + [CLSCompliant(false)] + public void Write(UInt64 source) + { + EnsureBufferSize(m_bitLength + 64); + NetBitWriter.WriteUInt64(source, 64, m_data, m_bitLength); + m_bitLength += 64; + } + + /// + /// Writes an unsigned integer using 1 to 64 bits + /// + [CLSCompliant(false)] + public void Write(UInt64 source, int numberOfBits) + { + EnsureBufferSize(m_bitLength + numberOfBits); + NetBitWriter.WriteUInt64(source, numberOfBits, m_data, m_bitLength); + m_bitLength += numberOfBits; + } + + /// + /// Writes a 64 bit signed integer + /// + public void Write(Int64 source) + { + EnsureBufferSize(m_bitLength + 64); + ulong usource = (ulong)source; + NetBitWriter.WriteUInt64(usource, 64, m_data, m_bitLength); + m_bitLength += 64; + } + + /// + /// Writes a signed integer using 1 to 64 bits + /// + public void Write(Int64 source, int numberOfBits) + { + EnsureBufferSize(m_bitLength + numberOfBits); + ulong usource = (ulong)source; + NetBitWriter.WriteUInt64(usource, numberOfBits, m_data, m_bitLength); + m_bitLength += numberOfBits; + } + + // + // Floating point + // +#if UNSAFE + /// + /// Writes a 32 bit floating point value + /// + public unsafe void Write(float source) + { + uint val = *((uint*)&source); +#if BIGENDIAN + val = NetUtility.SwapByteOrder(val); +#endif + Write(val); + } +#else + /// + /// Writes a 32 bit floating point value + /// + public void Write(float source) + { + // 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 = source; + +#if BIGENDIAN + // swap byte order + su.UIntValue = NetUtility.SwapByteOrder(su.UIntValue); +#endif + Write(su.UIntValue); + } +#endif + +#if UNSAFE + /// + /// Writes a 64 bit floating point value + /// + public unsafe void Write(double source) + { + ulong val = *((ulong*)&source); +#if BIGENDIAN + val = NetUtility.SwapByteOrder(val); +#endif + Write(val); + } +#else + /// + /// Writes a 64 bit floating point value + /// + public void Write(double source) + { + byte[] val = BitConverter.GetBytes(source); +#if BIGENDIAN + // 0 1 2 3 4 5 6 7 + + // swap byte order + byte tmp = val[7]; + val[7] = val[0]; + val[0] = tmp; + + tmp = val[6]; + val[6] = val[1]; + val[1] = tmp; + + tmp = val[5]; + val[5] = val[2]; + val[2] = tmp; + + tmp = val[4]; + val[4] = val[3]; + val[3] = tmp; +#endif + Write(val); + } +#endif + + // + // Variable bits + // + + /// + /// Write Base128 encoded variable sized unsigned integer of up to 32 bits + /// + /// number of bytes written + [CLSCompliant(false)] + public int WriteVariableUInt32(uint value) + { + int retval = 1; + uint num1 = (uint)value; + while (num1 >= 0x80) + { + this.Write((byte)(num1 | 0x80)); + num1 = num1 >> 7; + retval++; + } + this.Write((byte)num1); + return retval; + } + + /// + /// Write Base128 encoded variable sized signed integer of up to 32 bits + /// + /// number of bytes written + public int WriteVariableInt32(int value) + { + uint zigzag = (uint)(value << 1) ^ (uint)(value >> 31); + return WriteVariableUInt32(zigzag); + } + + /// + /// Write Base128 encoded variable sized signed integer of up to 64 bits + /// + /// number of bytes written + public int WriteVariableInt64(Int64 value) + { + ulong zigzag = (ulong)(value << 1) ^ (ulong)(value >> 63); + return WriteVariableUInt64(zigzag); + } + + /// + /// Write Base128 encoded variable sized unsigned integer of up to 64 bits + /// + /// number of bytes written + [CLSCompliant(false)] + public int WriteVariableUInt64(UInt64 value) + { + int retval = 1; + UInt64 num1 = (UInt64)value; + while (num1 >= 0x80) + { + this.Write((byte)(num1 | 0x80)); + num1 = num1 >> 7; + retval++; + } + this.Write((byte)num1); + return retval; + } + + /// + /// Compress (lossy) a float in the range -1..1 using numberOfBits bits + /// + public void WriteSignedSingle(float value, int numberOfBits) + { + NetException.Assert(((value >= -1.0) && (value <= 1.0)), " WriteSignedSingle() must be passed a float in the range -1 to 1; val is " + value); + + float unit = (value + 1.0f) * 0.5f; + int maxVal = (1 << numberOfBits) - 1; + uint writeVal = (uint)(unit * (float)maxVal); + + Write(writeVal, numberOfBits); + } + + /// + /// Compress (lossy) a float in the range 0..1 using numberOfBits bits + /// + public void WriteUnitSingle(float value, int numberOfBits) + { + NetException.Assert(((value >= 0.0) && (value <= 1.0)), " WriteUnitSingle() must be passed a float in the range 0 to 1; val is " + value); + + int maxValue = (1 << numberOfBits) - 1; + uint writeVal = (uint)(value * (float)maxValue); + + Write(writeVal, numberOfBits); + } + + /// + /// Compress a float within a specified range using a certain number of bits + /// + public void WriteRangedSingle(float value, float min, float max, int numberOfBits) + { + NetException.Assert(((value >= min) && (value <= max)), " WriteRangedSingle() must be passed a float in the range MIN to MAX; val is " + value); + + float range = max - min; + float unit = ((value - min) / range); + int maxVal = (1 << numberOfBits) - 1; + Write((UInt32)((float)maxVal * unit), numberOfBits); + } + + /// + /// Writes an integer with the least amount of bits need for the specified range + /// Returns number of bits written + /// + public int WriteRangedInteger(int min, int max, int value) + { + NetException.Assert(value >= min && value <= max, "Value not within min/max range!"); + + uint range = (uint)(max - min); + int numBits = NetUtility.BitsToHoldUInt(range); + + uint rvalue = (uint)(value - min); + Write(rvalue, numBits); + + return numBits; + } + + /// + /// Write a string + /// + public void Write(string source) + { + if (string.IsNullOrEmpty(source)) + { + EnsureBufferSize(m_bitLength + 8); + WriteVariableUInt32(0); + return; + } + + byte[] bytes = Encoding.UTF8.GetBytes(source); + EnsureBufferSize(m_bitLength + 8 + (bytes.Length * 8)); + WriteVariableUInt32((uint)bytes.Length); + Write(bytes); + } + + /// + /// Writes an endpoint description + /// + public void Write(IPEndPoint endPoint) + { + byte[] bytes = endPoint.Address.GetAddressBytes(); + Write((byte)bytes.Length); + Write(bytes); + Write((ushort)endPoint.Port); + } + + /// + /// Writes the local time to a message; readable (and convertable to local time) by the remote host using ReadTime() + /// + public void WriteTime(double localTime, bool highPrecision) + { + if (highPrecision) + Write(localTime); + else + Write((float)localTime); + } + + /// + /// Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes. + /// + public void WritePadBits() + { + m_bitLength = ((m_bitLength + 7) >> 3) * 8; + EnsureBufferSize(m_bitLength); + } + + /// + /// Pads data with the specified number of bits. + /// + public void WritePadBits(int numberOfBits) + { + m_bitLength += numberOfBits; + EnsureBufferSize(m_bitLength); + } + + /// + /// Append all the bits of message to this message + /// + public void Write(NetOutgoingMessage message) + { + EnsureBufferSize(m_bitLength + (message.LengthBytes * 8)); + + Write(message.m_data, 0, message.LengthBytes); + + // did we write excessive bits? + int bitsInLastByte = (message.m_bitLength % 8); + if (bitsInLastByte != 0) + { + int excessBits = 8 - bitsInLastByte; + m_bitLength -= excessBits; + } + } + + /// + /// Append all the bits of message to this message + /// + public void Write(NetIncomingMessage message) + { + EnsureBufferSize(m_bitLength + (message.LengthBytes * 8)); + + Write(message.m_data, 0, message.LengthBytes); + + // did we write excessive bits? + int bitsInLastByte = (message.m_bitLength % 8); + if (bitsInLastByte != 0) + { + int excessBits = 8 - bitsInLastByte; + m_bitLength -= excessBits; + } + } + } +} diff --git a/Lidgren.Network/NetBuffer.cs b/Lidgren.Network/NetBuffer.cs new file mode 100644 index 0000000..da23d90 --- /dev/null +++ b/Lidgren.Network/NetBuffer.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Lidgren.Network +{ + public partial class NetBuffer + { + // @TODO Add ReadTime() to incomingmessage class + private const int c_overAllocateAmount = 4; + + private static readonly Dictionary s_readMethods; + private static readonly Dictionary s_writeMethods; + + internal byte[] m_data; + internal int m_bitLength; + internal int m_readPosition; + + /// + /// Gets or sets the internal data buffer + /// + public byte[] Data + { + get { return m_data; } + set { m_data = value; } + } + + /// + /// Gets or sets the length of the used portion of the buffer in bytes + /// + public int LengthBytes + { + get { return ((m_bitLength + 7) >> 3); } + set + { + m_bitLength = value * 8; + InternalEnsureBufferSize(m_bitLength); + } + } + + /// + /// Gets or sets the length of the used portion of the buffer in bits + /// + public int LengthBits + { + get { return m_bitLength; } + set + { + m_bitLength = value; + InternalEnsureBufferSize(m_bitLength); + } + } + + /// + /// Gets or sets the read position in the buffer, in bits (not bytes) + /// + public long Position + { + get { return (long)m_readPosition; } + set { m_readPosition = (int)value; } + } + + /// + /// Gets the position in the buffer in bytes; note that the bits of the first returned byte may already have been read - check the Position property to make sure. + /// + public int PositionInBytes + { + get { return (int)(m_readPosition / 8); } + } + + static NetBuffer() + { + Type[] integralTypes = typeof(Byte).Assembly.GetTypes(); + + s_readMethods = new Dictionary(); + MethodInfo[] methods = typeof(NetIncomingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public); + foreach (MethodInfo mi in methods) + { + if (mi.GetParameters().Length == 0 && mi.Name.StartsWith("Read", StringComparison.InvariantCulture)) + { + string n = mi.Name.Substring(4); + foreach (Type it in integralTypes) + { + if (it.Name == n) + s_readMethods[it] = mi; + } + } + } + + s_writeMethods = new Dictionary(); + methods = typeof(NetOutgoingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public); + foreach (MethodInfo mi in methods) + { + if (mi.Name.Equals("Write", StringComparison.InvariantCulture)) + { + ParameterInfo[] pis = mi.GetParameters(); + if (pis.Length == 1) + s_writeMethods[pis[0].ParameterType] = mi; + } + } + } + } +} diff --git a/Lidgren.Network/NetConnection.Handshake.cs b/Lidgren.Network/NetConnection.Handshake.cs index 579de68..246b6f1 100644 --- a/Lidgren.Network/NetConnection.Handshake.cs +++ b/Lidgren.Network/NetConnection.Handshake.cs @@ -184,12 +184,12 @@ namespace Lidgren.Network { if (m_localHailMessage != null) { - byte[] hi = m_localHailMessage.PeekDataBuffer(); + byte[] hi = m_localHailMessage.Data; if (hi != null && hi.Length >= m_localHailMessage.LengthBytes) { if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10) throw new NetException("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes)); - om.Write(m_localHailMessage.PeekDataBuffer(), 0, m_localHailMessage.LengthBytes); + om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes); } } } diff --git a/Lidgren.Network/NetIncomingMessage.cs b/Lidgren.Network/NetIncomingMessage.cs index 44d7061..b32ac21 100644 --- a/Lidgren.Network/NetIncomingMessage.cs +++ b/Lidgren.Network/NetIncomingMessage.cs @@ -26,10 +26,8 @@ namespace Lidgren.Network /// Incoming message either sent from a remote peer or generated within the library /// [DebuggerDisplay("Type={MessageType} LengthBits={LengthBits}")] - public partial class NetIncomingMessage + public sealed class NetIncomingMessage : NetBuffer { - internal byte[] m_data; - internal int m_bitLength; internal NetIncomingMessageType m_incomingMessageType; internal IPEndPoint m_senderEndpoint; internal NetConnection m_senderConnection; @@ -68,23 +66,6 @@ namespace Lidgren.Network /// public double ReceiveTime { get { return m_receiveTime; } } - /// - /// Gets the length of the message payload in bytes - /// - public int LengthBytes - { - get { return ((m_bitLength + 7) >> 3); } - } - - /// - /// Gets the length of the message payload in bits - /// - public int LengthBits - { - get { return m_bitLength; } - internal set { m_bitLength = value; } - } - internal NetIncomingMessage() { } diff --git a/Lidgren.Network/NetOutgoingMessage.Write.cs b/Lidgren.Network/NetOutgoingMessage.Write.cs index e2f88fc..5c098ca 100644 --- a/Lidgren.Network/NetOutgoingMessage.Write.cs +++ b/Lidgren.Network/NetOutgoingMessage.Write.cs @@ -25,17 +25,6 @@ using System.Runtime.InteropServices; namespace Lidgren.Network { - [StructLayout(LayoutKind.Explicit)] - public struct SingleUIntUnion - { - [FieldOffset(0)] - public float SingleValue; - - [FieldOffset(0)] - [CLSCompliant(false)] - public uint UIntValue; - } - public sealed partial class NetOutgoingMessage { private const int c_overAllocateAmount = 4; diff --git a/Lidgren.Network/NetOutgoingMessage.cs b/Lidgren.Network/NetOutgoingMessage.cs index def593d..ac54e26 100644 --- a/Lidgren.Network/NetOutgoingMessage.cs +++ b/Lidgren.Network/NetOutgoingMessage.cs @@ -26,7 +26,7 @@ namespace Lidgren.Network /// Outgoing message used to send data to remote peer(s) /// [DebuggerDisplay("LengthBits={LengthBits}")] - public sealed partial class NetOutgoingMessage + public sealed class NetOutgoingMessage : NetBuffer { internal NetMessageType m_messageType; internal bool m_isSent; diff --git a/Samples/Chat/Chat.sln b/Samples/Chat/Chat.sln index b607647..64916ee 100644 --- a/Samples/Chat/Chat.sln +++ b/Samples/Chat/Chat.sln @@ -5,10 +5,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatClient", "ChatClient\Ch EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatServer", "ChatServer\ChatServer.csproj", "{388FD8FC-6B0B-403F-B4B1-6AC2F025C00F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplesCommon", "..\SamplesCommon\SamplesCommon\SamplesCommon.csproj", "{64D2482A-3C16-4D1F-A3F6-058FC3D73E21}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lidgren.Network", "..\..\Lidgren.Network\Lidgren.Network.csproj", "{49BA1C69-6104-41AC-A5D8-B54FA9F696E8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplesCommon", "..\SamplesCommon\SamplesCommon.csproj", "{773069DA-B66E-4667-ADCB-0D215AD8CF3E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,16 +39,6 @@ Global {388FD8FC-6B0B-403F-B4B1-6AC2F025C00F}.Release|Mixed Platforms.Build.0 = Release|x86 {388FD8FC-6B0B-403F-B4B1-6AC2F025C00F}.Release|x86.ActiveCfg = Release|x86 {388FD8FC-6B0B-403F-B4B1-6AC2F025C00F}.Release|x86.Build.0 = Release|x86 - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Debug|Any CPU.Build.0 = Debug|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Debug|x86.ActiveCfg = Debug|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Release|Any CPU.ActiveCfg = Release|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Release|Any CPU.Build.0 = Release|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21}.Release|x86.ActiveCfg = Release|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -59,6 +49,16 @@ Global {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU {49BA1C69-6104-41AC-A5D8-B54FA9F696E8}.Release|x86.ActiveCfg = Release|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Debug|x86.ActiveCfg = Debug|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Release|Any CPU.Build.0 = Release|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Samples/Chat/ChatClient/ChatClient.csproj b/Samples/Chat/ChatClient/ChatClient.csproj index ecb687d..67d49d4 100644 --- a/Samples/Chat/ChatClient/ChatClient.csproj +++ b/Samples/Chat/ChatClient/ChatClient.csproj @@ -72,10 +72,6 @@ {49BA1C69-6104-41AC-A5D8-B54FA9F696E8} Lidgren.Network - - {64D2482A-3C16-4D1F-A3F6-058FC3D73E21} - SamplesCommon -