From e2a5762229b3d48bb585ba525d3070ce54e1a1f7 Mon Sep 17 00:00:00 2001 From: AgentFire Date: Wed, 13 Dec 2023 00:35:48 +0300 Subject: [PATCH] Nullability context and code refactor (#11) Co-authored-by: Pieter-Jan Briers --- Lidgren.Network/Lidgren.Network.csproj | 12 +- Lidgren.Network/NetBigInteger.cs | 48 +- Lidgren.Network/NetBuffer.Peek.cs | 63 ++- Lidgren.Network/NetBuffer.Read.Reflection.cs | 12 +- Lidgren.Network/NetBuffer.Read.cs | 180 +++--- Lidgren.Network/NetBuffer.Write.Reflection.cs | 16 +- Lidgren.Network/NetBuffer.Write.cs | 92 ++-- Lidgren.Network/NetBuffer.cs | 14 +- Lidgren.Network/NetClient.cs | 18 +- Lidgren.Network/NetConnection.Handshake.cs | 27 +- Lidgren.Network/NetConnection.Latency.cs | 7 +- Lidgren.Network/NetConnection.MTU.cs | 3 +- Lidgren.Network/NetConnection.cs | 36 +- Lidgren.Network/NetException.cs | 9 +- Lidgren.Network/NetFragmentationInfo.cs | 18 +- Lidgren.Network/NetIncomingMessage.cs | 8 +- Lidgren.Network/NetOutgoingMessage.cs | 11 +- Lidgren.Network/NetPeer.Discovery.cs | 6 +- Lidgren.Network/NetPeer.Fragmentation.cs | 45 +- Lidgren.Network/NetPeer.Internal.cs | 227 ++++---- Lidgren.Network/NetPeer.LatencySimulation.cs | 98 ++-- Lidgren.Network/NetPeer.Logging.cs | 4 +- Lidgren.Network/NetPeer.MessagePools.cs | 64 ++- .../NetPeer.ReceivedFragmentGroup.cs | 17 + Lidgren.Network/NetPeer.Send.cs | 8 +- Lidgren.Network/NetPeer.cs | 110 ++-- Lidgren.Network/NetPeerConfiguration.cs | 2 +- Lidgren.Network/NetPeerStatistics.cs | 9 +- Lidgren.Network/NetQueue.cs | 22 +- Lidgren.Network/NetRandom.Implementations.cs | 20 +- Lidgren.Network/NetRandomSeed.cs | 2 +- Lidgren.Network/NetReliableOrderedReceiver.cs | 11 +- Lidgren.Network/NetReliableSenderChannel.cs | 26 +- .../NetReliableSequencedReceiver.cs | 2 +- .../NetReliableUnorderedReceiver.cs | 4 +- Lidgren.Network/NetSenderChannelBase.cs | 7 +- Lidgren.Network/NetStoredReliableMessage.cs | 4 +- Lidgren.Network/NetUPnP.cs | 385 +++++++------ Lidgren.Network/NetUnreliableSenderChannel.cs | 30 +- .../NetUnreliableUnorderedReceiver.cs | 2 +- Lidgren.Network/NetUtility.Dns.cs | 521 +++++++++--------- Lidgren.Network/NetUtility.cs | 136 ++--- Lidgren.Network/Platform/PlatformWin32.cs | 13 +- Lidgren.Network/Sockets/NetSocketAddress.cs | 4 +- RELEASE-NOTES.md | 4 + UnitTests/NetQueueTests.cs | 4 +- UnitTests/NetUtilityTests.cs | 4 +- UnitTests/PeerTestBase.cs | 119 ++-- UnitTests/ReadWriteTests.cs | 2 +- UnitTests/UnitTests.csproj | 15 +- 50 files changed, 1292 insertions(+), 1209 deletions(-) create mode 100644 Lidgren.Network/NetPeer.ReceivedFragmentGroup.cs diff --git a/Lidgren.Network/Lidgren.Network.csproj b/Lidgren.Network/Lidgren.Network.csproj index 52446ea6..6df88771 100644 --- a/Lidgren.Network/Lidgren.Network.csproj +++ b/Lidgren.Network/Lidgren.Network.csproj @@ -5,6 +5,8 @@ SpaceWizards.Lidgren.Network true true + enable + 11.0 $(DefineConstants);USE_RELEASE_STATISTICS @@ -28,11 +30,15 @@ - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - + diff --git a/Lidgren.Network/NetBigInteger.cs b/Lidgren.Network/NetBigInteger.cs index 4988f391..7d7c62e1 100644 --- a/Lidgren.Network/NetBigInteger.cs +++ b/Lidgren.Network/NetBigInteger.cs @@ -1,8 +1,8 @@ using System; -using System.Text; -using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Text; namespace Lidgren.Network { @@ -53,6 +53,7 @@ private static int GetByteLength( private NetBigInteger() { + m_magnitude = null!; } private NetBigInteger( @@ -155,7 +156,7 @@ public NetBigInteger( } // strip leading zeros from the string str - while (index < str.Length && Int32.Parse(str[index].ToString(), style) == 0) + while (index < str.Length && int.Parse(str[index].ToString(), style) == 0) { index++; } @@ -555,7 +556,7 @@ public NetBigInteger And( return result; } - + private int calcBitLength( int indx, int[] mag) @@ -643,7 +644,7 @@ public int CompareTo( return CompareTo((NetBigInteger)obj); } - + // unsigned comparison on two arrays - note the arrays may // start with leading zeros. private static int CompareTo( @@ -889,12 +890,12 @@ public NetBigInteger[] DivideAndRemainder( } public override bool Equals( - object obj) + object? obj) { if (obj == this) return true; - NetBigInteger biggie = obj as NetBigInteger; + NetBigInteger? biggie = obj as NetBigInteger; if (biggie == null) return false; @@ -971,7 +972,7 @@ public int IntValue : -m_magnitude[m_magnitude.Length - 1]; } } - + public NetBigInteger Max( NetBigInteger value) { @@ -1001,8 +1002,7 @@ public NetBigInteger ModInverse( if (m.m_sign < 1) throw new ArithmeticException("Modulus must be positive"); - NetBigInteger x = new NetBigInteger(); - NetBigInteger gcd = ExtEuclid(this, m, x, null); + NetBigInteger gcd = ExtEuclid(this, m, out NetBigInteger x, null); if (!gcd.Equals(One)) throw new ArithmeticException("Numbers not relatively prime."); @@ -1020,8 +1020,8 @@ public NetBigInteger ModInverse( private static NetBigInteger ExtEuclid( NetBigInteger a, NetBigInteger b, - NetBigInteger u1Out, - NetBigInteger u2Out) + out NetBigInteger u1Out, + NetBigInteger? u2Out) { NetBigInteger u1 = One; NetBigInteger u3 = a; @@ -1041,11 +1041,7 @@ private static NetBigInteger ExtEuclid( v3 = q[1]; } - if (u1Out != null) - { - u1Out.m_sign = u1.m_sign; - u1Out.m_magnitude = u1.m_magnitude; - } + u1Out = new NetBigInteger(u1.m_sign, u1.m_magnitude, false); if (u2Out != null) { @@ -1081,8 +1077,8 @@ public NetBigInteger ModPow( if (m_sign == 0) return Zero; - int[] zVal = null; - int[] yAccum = null; + int[]? zVal = null; + int[]? yAccum = null; int[] yVal; // Montgomery exponentiation is only possible if the modulus is odd, @@ -1136,6 +1132,9 @@ public NetBigInteger ModPow( yVal = new int[m.m_magnitude.Length]; + NetException.Assert(yAccum != null); + NetException.Assert(zVal != null); + // // from LSW to MSW // @@ -1189,8 +1188,7 @@ public NetBigInteger ModPow( { Multiply(yAccum, yVal, zVal); Remainder(yAccum, m.m_magnitude); - Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, - yVal.Length); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); ZeroOut(yAccum); } } @@ -1588,7 +1586,7 @@ public NetBigInteger Pow(int exp) return y; } - + private int Remainder( int m) { @@ -2186,7 +2184,7 @@ public string ToString( else { // This is algorithm 1a from chapter 4.4 in Seminumerical Algorithms, slow but it works - Stack S = new Stack(); + Stack S = new Stack(); NetBigInteger bs = ValueOf(radix); NetBigInteger u = Abs(); @@ -2210,7 +2208,7 @@ public string ToString( // Then pop the stack while (S.Count != 0) { - sb.Append((string)S.Pop()); + sb.Append(S.Pop()); } } @@ -2344,7 +2342,7 @@ public bool TestBit( return ((word >> (n % 32)) & 1) > 0; } } - + #if WINDOWS_RUNTIME internal sealed class Stack { diff --git a/Lidgren.Network/NetBuffer.Peek.cs b/Lidgren.Network/NetBuffer.Peek.cs index 7bf10310..57ff2f71 100644 --- a/Lidgren.Network/NetBuffer.Peek.cs +++ b/Lidgren.Network/NetBuffer.Peek.cs @@ -28,7 +28,7 @@ public partial class NetBuffer /// /// Gets the internal data buffer /// - public byte[] PeekDataBuffer() { return m_data; } + public byte[]? PeekDataBuffer() { return m_data; } // // 1 bit @@ -39,7 +39,7 @@ public partial class NetBuffer public bool PeekBoolean() { NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError); - byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition); + byte retval = NetBitWriter.ReadByte(Data, 1, m_readPosition); return (retval > 0 ? true : false); } @@ -52,7 +52,7 @@ public bool PeekBoolean() public byte PeekByte() { NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError); - byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + byte retval = NetBitWriter.ReadByte(Data, 8, m_readPosition); return retval; } @@ -63,7 +63,7 @@ public byte PeekByte() public sbyte PeekSByte() { NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError); - byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + byte retval = NetBitWriter.ReadByte(Data, 8, m_readPosition); return (sbyte)retval; } @@ -72,7 +72,7 @@ public sbyte PeekSByte() /// public byte PeekByte(int numberOfBits) { - byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition); + byte retval = NetBitWriter.ReadByte(Data, numberOfBits, m_readPosition); return retval; } @@ -86,7 +86,7 @@ public Span PeekBytes(Span into) { NetException.Assert(m_bitLength - m_readPosition >= (into.Length * 8), c_readOverflowError); - NetBitWriter.ReadBytes(m_data, m_readPosition, into); + NetBitWriter.ReadBytes(Data, m_readPosition, into); return into; } @@ -116,10 +116,10 @@ public void PeekBytes(byte[] into, int offset, int numberOfBytes) /// /// Reads an Int16 without advancing the read pointer /// - public Int16 PeekInt16() + public short PeekInt16() { NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError); - uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition); + uint retval = NetBitWriter.ReadUInt16(Data, 16, m_readPosition); return (short)retval; } @@ -127,10 +127,10 @@ public Int16 PeekInt16() /// Reads a UInt16 without advancing the read pointer /// [CLSCompliant(false)] - public UInt16 PeekUInt16() + public ushort PeekUInt16() { NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError); - uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition); + uint retval = NetBitWriter.ReadUInt16(Data, 16, m_readPosition); return (ushort)retval; } @@ -140,22 +140,22 @@ public UInt16 PeekUInt16() /// /// Reads an Int32 without advancing the read pointer /// - public Int32 PeekInt32() + public int PeekInt32() { NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); - uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); - return (Int32)retval; + uint retval = NetBitWriter.ReadUInt32(Data, 32, m_readPosition); + return (int)retval; } /// /// Reads the specified number of bits into an Int32 without advancing the read pointer /// - public Int32 PeekInt32(int numberOfBits) + public int 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); + uint retval = NetBitWriter.ReadUInt32(Data, numberOfBits, m_readPosition); if (numberOfBits == 32) return (int)retval; @@ -177,10 +177,10 @@ public Int32 PeekInt32(int numberOfBits) /// Reads a UInt32 without advancing the read pointer /// [CLSCompliant(false)] - public UInt32 PeekUInt32() + public uint PeekUInt32() { NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); - uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + uint retval = NetBitWriter.ReadUInt32(Data, 32, m_readPosition); return retval; } @@ -188,12 +188,12 @@ public UInt32 PeekUInt32() /// Reads the specified number of bits into a UInt32 without advancing the read pointer /// [CLSCompliant(false)] - public UInt32 PeekUInt32(int numberOfBits) + public uint 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); + uint retval = NetBitWriter.ReadUInt32(Data, numberOfBits, m_readPosition); return retval; } @@ -204,12 +204,12 @@ public UInt32 PeekUInt32(int numberOfBits) /// Reads a UInt64 without advancing the read pointer /// [CLSCompliant(false)] - public UInt64 PeekUInt64() + public ulong 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 low = NetBitWriter.ReadUInt32(Data, 32, m_readPosition); + ulong high = NetBitWriter.ReadUInt32(Data, 32, m_readPosition + 32); ulong retval = low + (high << 32); @@ -219,7 +219,7 @@ public UInt64 PeekUInt64() /// /// Reads an Int64 without advancing the read pointer /// - public Int64 PeekInt64() + public long PeekInt64() { NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); unchecked @@ -234,7 +234,7 @@ public Int64 PeekInt64() /// Reads the specified number of bits into an UInt64 without advancing the read pointer /// [CLSCompliant(false)] - public UInt64 PeekUInt64(int numberOfBits) + public ulong 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); @@ -242,12 +242,12 @@ public UInt64 PeekUInt64(int numberOfBits) ulong retval; if (numberOfBits <= 32) { - retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + retval = (ulong)NetBitWriter.ReadUInt32(Data, numberOfBits, m_readPosition); } else { - retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); - retval |= (UInt64)NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition + 32) << 32; + retval = NetBitWriter.ReadUInt32(Data, 32, m_readPosition); + retval |= (ulong)NetBitWriter.ReadUInt32(Data, numberOfBits - 32, m_readPosition + 32) << 32; } return retval; } @@ -255,7 +255,7 @@ public UInt64 PeekUInt64(int numberOfBits) /// /// Reads the specified number of bits into an Int64 without advancing the read pointer /// - public Int64 PeekInt64(int numberOfBits) + public long PeekInt64(int numberOfBits) { NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits"); return (long)PeekUInt64(numberOfBits); @@ -299,7 +299,7 @@ public float PeekSingle() if ((m_readPosition & 7) == 0) // read directly { - float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3); + float retval = BitConverter.ToSingle(Data, m_readPosition >> 3); return retval; } @@ -317,7 +317,7 @@ public double PeekDouble() if ((m_readPosition & 7) == 0) // read directly { // read directly - double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3); + double retval = BitConverter.ToDouble(Data, m_readPosition >> 3); return retval; } @@ -348,5 +348,4 @@ public int PeekStringSize() return byteLen; } } -} - +} \ No newline at end of file diff --git a/Lidgren.Network/NetBuffer.Read.Reflection.cs b/Lidgren.Network/NetBuffer.Read.Reflection.cs index cb716f10..72e0a6aa 100644 --- a/Lidgren.Network/NetBuffer.Read.Reflection.cs +++ b/Lidgren.Network/NetBuffer.Read.Reflection.cs @@ -46,11 +46,10 @@ public void ReadAllFields(object target, BindingFlags flags) foreach (FieldInfo fi in fields) { - object value; + object? value; // find read method - MethodInfo readMethod; - if (s_readMethods.TryGetValue(fi.FieldType, out readMethod)) + if (s_readMethods.TryGetValue(fi.FieldType, out MethodInfo? readMethod)) { // read value value = readMethod.Invoke(this, null); @@ -83,11 +82,10 @@ public void ReadAllProperties(object target, BindingFlags flags) NetUtility.SortMembersList(fields); foreach (PropertyInfo fi in fields) { - object value; + object? value; // find read method - MethodInfo readMethod; - if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod)) + if (s_readMethods.TryGetValue(fi.PropertyType, out MethodInfo? readMethod)) { // read value value = readMethod.Invoke(this, null); @@ -95,7 +93,7 @@ public void ReadAllProperties(object target, BindingFlags flags) // set the value var setMethod = fi.GetSetMethod(); if (setMethod != null) - setMethod.Invoke(target, new object[] { value }); + setMethod.Invoke(target, new object?[] { value }); } } } diff --git a/Lidgren.Network/NetBuffer.Read.cs b/Lidgren.Network/NetBuffer.Read.cs index a5d5fc97..52338448 100644 --- a/Lidgren.Network/NetBuffer.Read.cs +++ b/Lidgren.Network/NetBuffer.Read.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Net; using System.Threading; +using System.Diagnostics.CodeAnalysis; #if !__NOIPENDPOINT__ using NetEndPoint = System.Net.IPEndPoint; @@ -28,7 +29,7 @@ public bool ReadBoolean() m_readPosition += 1; return retval; } - + /// /// Reads a byte /// @@ -77,15 +78,15 @@ public byte ReadByte(int numberOfBits) } /// - /// Reads the specified number of bytes - /// + /// Reads the specified number of bytes + /// public Span ReadBytes(Span into) { NetException.Assert(m_bitLength - m_readPosition + 7 >= (into.Length * 8), c_readOverflowError); - NetBitWriter.ReadBytes(m_data, m_readPosition, into); - m_readPosition += (8 * into.Length); - return into; + NetBitWriter.ReadBytes(Data, m_readPosition, into); + m_readPosition += (8 * into.Length); + return into; } /// @@ -101,7 +102,7 @@ public byte[] ReadBytes(int numberOfBytes) /// /// Reads the specified number of bytes and returns true for success /// - public bool ReadBytes(int numberOfBytes, out byte[] result) + public bool ReadBytes(int numberOfBytes, [MaybeNullWhen(false)] out byte[] result) { if (m_bitLength - m_readPosition + 7 < (numberOfBytes * 8)) { @@ -110,25 +111,25 @@ public bool ReadBytes(int numberOfBytes, out byte[] result) } result = new byte[numberOfBytes]; - NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, result, 0); + NetBitWriter.ReadBytes(Data, numberOfBytes, m_readPosition, result, 0); m_readPosition += (8 * numberOfBytes); return true; } /// - /// Reads the specified number of bytes and returns true for success - /// + /// Reads the specified number of bytes and returns true for success + /// public bool TryReadBytes(Span into) - { - if (m_bitLength - m_readPosition + 7 < (into.Length * 8)) - { - return false; - } + { + if (m_bitLength - m_readPosition + 7 < (into.Length * 8)) + { + return false; + } - NetBitWriter.ReadBytes(m_data, m_readPosition, into); - m_readPosition += (8 * into.Length); - return true; - } + NetBitWriter.ReadBytes(Data, m_readPosition, into); + m_readPosition += (8 * into.Length); + return true; + } /// /// Reads the specified number of bytes into a preallocated array @@ -141,32 +142,32 @@ 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); + NetBitWriter.ReadBytes(Data, numberOfBytes, m_readPosition, into, offset); m_readPosition += (8 * numberOfBytes); return; } /// - /// Reads the specified number of bits into a preallocated span - /// - /// The destination array - /// The number of bits to read - public void ReadBits(Span into, int numberOfBits) - { - NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); - NetException.Assert(NetUtility.BytesToHoldBits(numberOfBits) <= into.Length); + /// Reads the specified number of bits into a preallocated span + /// + /// The destination array + /// The number of bits to read + public void ReadBits(Span into, int numberOfBits) + { + NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); + NetException.Assert(NetUtility.BytesToHoldBits(numberOfBits) <= into.Length); - int numberOfWholeBytes = numberOfBits / 8; - int extraBits = numberOfBits - (numberOfWholeBytes * 8); + int numberOfWholeBytes = numberOfBits / 8; + int extraBits = numberOfBits - (numberOfWholeBytes * 8); - NetBitWriter.ReadBytes(m_data, m_readPosition, into.Slice(0, numberOfWholeBytes)); - m_readPosition += (8 * numberOfWholeBytes); + NetBitWriter.ReadBytes(Data, m_readPosition, into.Slice(0, numberOfWholeBytes)); + m_readPosition += (8 * numberOfWholeBytes); - if (extraBits > 0) - into[numberOfWholeBytes] = ReadByte(extraBits); + if (extraBits > 0) + into[numberOfWholeBytes] = ReadByte(extraBits); - return; - } + return; + } /// /// Reads the specified number of bits into a preallocated array @@ -182,7 +183,7 @@ public void ReadBits(byte[] into, int offset, int numberOfBits) /// /// Reads a 16 bit signed integer written using Write(Int16) /// - public Int16 ReadInt16() + public short ReadInt16() { short retval = PeekInt16(); m_readPosition += 16; @@ -193,7 +194,7 @@ public Int16 ReadInt16() /// Reads a 16 bit unsigned integer written using Write(UInt16) /// [CLSCompliant(false)] - public UInt16 ReadUInt16() + public ushort ReadUInt16() { ushort retval = PeekUInt16(); m_readPosition += 16; @@ -203,7 +204,7 @@ public UInt16 ReadUInt16() /// /// Reads a 32 bit signed integer written using Write(Int32) /// - public Int32 ReadInt32() + public int ReadInt32() { NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); int retval = PeekInt32(); @@ -215,7 +216,7 @@ public Int32 ReadInt32() /// Reads a 32 bit signed integer written using Write(Int32) /// [CLSCompliant(false)] - public bool ReadInt32(out Int32 result) + public bool ReadInt32(out int result) { if (m_bitLength - m_readPosition < 32) { @@ -231,7 +232,7 @@ public bool ReadInt32(out Int32 result) /// /// Reads a signed integer stored in 1 to 32 bits, written using Write(Int32, Int32) /// - public Int32 ReadInt32(int numberOfBits) + public int ReadInt32(int numberOfBits) { int retval = PeekInt32(numberOfBits); m_readPosition += numberOfBits; @@ -243,7 +244,7 @@ public Int32 ReadInt32(int numberOfBits) /// Reads an 32 bit unsigned integer written using Write(UInt32) /// [CLSCompliant(false)] - public UInt32 ReadUInt32() + public uint ReadUInt32() { uint retval = PeekUInt32(); m_readPosition += 32; @@ -254,7 +255,7 @@ public UInt32 ReadUInt32() /// Reads an 32 bit unsigned integer written using Write(UInt32) and returns true for success /// [CLSCompliant(false)] - public bool ReadUInt32(out UInt32 result) + public bool ReadUInt32(out uint result) { if (m_bitLength - m_readPosition < 32) { @@ -271,9 +272,9 @@ public bool ReadUInt32(out UInt32 result) /// Reads an unsigned integer stored in 1 to 32 bits, written using Write(UInt32, Int32) /// [CLSCompliant(false)] - public UInt32 ReadUInt32(int numberOfBits) + public uint ReadUInt32(int numberOfBits) { - UInt32 retval = PeekUInt32(numberOfBits); + uint retval = PeekUInt32(numberOfBits); m_readPosition += numberOfBits; return retval; } @@ -282,7 +283,7 @@ public UInt32 ReadUInt32(int numberOfBits) /// Reads a 64 bit unsigned integer written using Write(UInt64) /// [CLSCompliant(false)] - public UInt64 ReadUInt64() + public ulong ReadUInt64() { ulong retval = PeekUInt64(); @@ -293,7 +294,7 @@ public UInt64 ReadUInt64() /// /// Reads a 64 bit signed integer written using Write(Int64) /// - public Int64 ReadInt64() + public long ReadInt64() { NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); unchecked @@ -308,7 +309,7 @@ public Int64 ReadInt64() /// Reads an unsigned integer stored in 1 to 64 bits, written using Write(UInt64, Int32) /// [CLSCompliant(false)] - public UInt64 ReadUInt64(int numberOfBits) + public ulong ReadUInt64(int numberOfBits) { ulong retval = PeekUInt64(numberOfBits); m_readPosition += numberOfBits; @@ -318,7 +319,7 @@ public UInt64 ReadUInt64(int numberOfBits) /// /// Reads a signed integer stored in 1 to 64 bits, written using Write(Int64, Int32) /// - public Int64 ReadInt64(int numberOfBits) + public long ReadInt64(int numberOfBits) { NetException.Assert(((numberOfBits > 0) && (numberOfBits <= 64)), "ReadInt64(bits) can only read between 1 and 64 bits"); return (long)ReadUInt64(numberOfBits); @@ -418,8 +419,7 @@ public bool ReadVariableUInt32(out uint result) int num2 = 0; while (m_bitLength - m_readPosition >= 8) { - byte num3; - if (ReadByte(out num3) == false) + if (ReadByte(out byte num3) == false) { result = 0; return false; @@ -448,19 +448,19 @@ public int ReadVariableInt32() /// /// Reads a variable sized Int64 written using WriteVariableInt64() /// - public Int64 ReadVariableInt64() + public long ReadVariableInt64() { - UInt64 n = ReadVariableUInt64(); - return (Int64)(n >> 1) ^ -(long)(n & 1); // decode zigzag + ulong n = ReadVariableUInt64(); + return (long)(n >> 1) ^ -(long)(n & 1); // decode zigzag } /// /// Reads a variable sized UInt32 written using WriteVariableInt64() /// [CLSCompliant(false)] - public UInt64 ReadVariableUInt64() + public ulong ReadVariableUInt64() { - UInt64 num1 = 0; + ulong num1 = 0; int num2 = 0; while (m_bitLength - m_readPosition >= 8) { @@ -468,7 +468,7 @@ public UInt64 ReadVariableUInt64() // throw new FormatException("Bad 7-bit encoded integer"); byte num3 = this.ReadByte(); - num1 |= ((UInt64)num3 & 0x7f) << num2; + num1 |= ((ulong)num3 & 0x7f) << num2; num2 += 7; if ((num3 & 0x80) == 0) return num1; @@ -533,20 +533,20 @@ public int ReadRangedInteger(int min, int max) return (int)(min + rvalue); } - /// - /// Reads a 64 bit integer value written using WriteRangedInteger() (64 version) - /// - /// 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 long ReadRangedInteger(long min, long max) - { - ulong range = (ulong)(max - min); - int numBits = NetUtility.BitsToHoldUInt64(range); - - ulong rvalue = ReadUInt64(numBits); - return min + (long)rvalue; - } + /// + /// Reads a 64 bit integer value written using WriteRangedInteger() (64 version) + /// + /// 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 long ReadRangedInteger(long min, long max) + { + ulong range = (ulong)(max - min); + int numBits = NetUtility.BitsToHoldUInt64(range); + + ulong rvalue = ReadUInt64(numBits); + return min + (long)rvalue; + } /// /// Reads a string written using Write(string) @@ -556,13 +556,13 @@ public string ReadString() int byteLen = (int)ReadVariableUInt32(); if (byteLen <= 0) - return String.Empty; + return string.Empty; if ((ulong)(m_bitLength - m_readPosition) < ((ulong)byteLen * 8)) { // not enough data #if DEBUG - + throw new NetException(c_readOverflowError); #else m_readPosition = m_bitLength; @@ -572,6 +572,11 @@ public string ReadString() if ((m_readPosition & 7) == 0) { + if (m_data == null) + { + throw new InvalidOperationException("m_data is null"); + } + // read directly string retval = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, byteLen); m_readPosition += (8 * byteLen); @@ -582,7 +587,9 @@ public string ReadString() { var buffer = ReadBytes(stackalloc byte[byteLen]); return Encoding.UTF8.GetString(buffer); - } else { + } + else + { byte[] bytes = ReadBytes(byteLen); return Encoding.UTF8.GetString(bytes, 0, bytes.Length); } @@ -591,29 +598,33 @@ public string ReadString() /// /// Reads a string written using Write(string) and returns true for success /// - public bool ReadString(out string result) + public bool ReadString([MaybeNullWhen(false)] out string result) { - uint byteLen; - if (ReadVariableUInt32(out byteLen) == false) + if (ReadVariableUInt32(out uint byteLen) == false) { - result = String.Empty; + result = string.Empty; return false; } if (byteLen <= 0) { - result = String.Empty; + result = string.Empty; return true; } if (m_bitLength - m_readPosition < (byteLen * 8)) { - result = String.Empty; + result = string.Empty; return false; } if ((m_readPosition & 7) == 0) { + if (m_data == null) + { + throw new InvalidOperationException("m_data is null"); + } + // read directly result = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, (int)byteLen); m_readPosition += (8 * (int)byteLen); @@ -630,14 +641,13 @@ public bool ReadString(out string result) return true; } - result = String.Empty; + result = string.Empty; return false; } - byte[] bytes; - if (ReadBytes((int)byteLen, out bytes) == false) + if (ReadBytes((int)byteLen, out byte[]? bytes) == false) { - result = String.Empty; + result = string.Empty; return false; } @@ -648,7 +658,7 @@ public bool ReadString(out string result) /// /// Reads a value, in local time comparable to NetTime.Now, written using WriteTime() for the connection supplied /// - public double ReadTime(NetConnection connection, bool highPrecision) + public double ReadTime(NetConnection? connection, bool highPrecision) { double remoteTime = (highPrecision ? ReadDouble() : (double)ReadSingle()); diff --git a/Lidgren.Network/NetBuffer.Write.Reflection.cs b/Lidgren.Network/NetBuffer.Write.Reflection.cs index 1498bf1d..3d811ab7 100644 --- a/Lidgren.Network/NetBuffer.Write.Reflection.cs +++ b/Lidgren.Network/NetBuffer.Write.Reflection.cs @@ -45,12 +45,11 @@ public void WriteAllFields(object ob, BindingFlags flags) foreach (FieldInfo fi in fields) { - object value = fi.GetValue(ob); + 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 }); + if (s_writeMethods.TryGetValue(fi.FieldType, out MethodInfo? writeMethod)) + writeMethod.Invoke(this, new object?[] { value }); else throw new NetException("Failed to find write method for type " + fi.FieldType); } @@ -78,15 +77,14 @@ public void WriteAllProperties(object ob, BindingFlags flags) foreach (PropertyInfo fi in fields) { - MethodInfo getMethod = fi.GetGetMethod(); + MethodInfo? getMethod = fi.GetGetMethod(); if (getMethod != null) { - object value = getMethod.Invoke(ob, null); + 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 }); + if (s_writeMethods.TryGetValue(fi.PropertyType, out MethodInfo? writeMethod)) + writeMethod.Invoke(this, new object?[] { value }); } } } diff --git a/Lidgren.Network/NetBuffer.Write.cs b/Lidgren.Network/NetBuffer.Write.cs index e1512e29..2a1d10e8 100644 --- a/Lidgren.Network/NetBuffer.Write.cs +++ b/Lidgren.Network/NetBuffer.Write.cs @@ -24,6 +24,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Runtime.CompilerServices; using System.Text; using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; namespace Lidgren.Network { @@ -32,6 +33,7 @@ public partial class NetBuffer /// /// Ensures the buffer can hold this number of bits /// + [MemberNotNull(nameof(m_data))] public void EnsureBufferSize(int numberOfBits) { int byteLen = ((numberOfBits + 7) >> 3); @@ -46,7 +48,7 @@ public void EnsureBufferSize(int numberOfBits) var newLen = m_data.Length * 2; if (newLen < byteLen) newLen = byteLen; - + Array.Resize(ref m_data, newLen); } } @@ -54,6 +56,7 @@ public void EnsureBufferSize(int numberOfBits) /// /// Ensures the buffer can hold this number of bits /// + [MemberNotNull(nameof(m_data))] internal void InternalEnsureBufferSize(int numberOfBits) { int byteLen = ((numberOfBits + 7) >> 3); @@ -90,10 +93,11 @@ public void Write(byte source) /// /// Writes a byte at a given offset in the buffer /// - public void WriteAt(Int32 offset, byte source) { + public void WriteAt(int offset, byte source) + { int newBitLength = Math.Max(m_bitLength, offset + 8); EnsureBufferSize(newBitLength); - NetBitWriter.WriteByte((byte) source, 8, m_data, offset); + NetBitWriter.WriteByte((byte)source, 8, m_data, offset); m_bitLength = newBitLength; } @@ -131,8 +135,8 @@ public void Write(byte[] source) } /// - /// Writes all bytes in a span - /// + /// Writes all bytes in a span + /// public void Write(ReadOnlySpan source) { int bits = source.Length * 8; @@ -157,7 +161,7 @@ public void Write(byte[] source, int offsetInBytes, int numberOfBytes) /// /// [CLSCompliant(false)] - public void Write(UInt16 source) + public void Write(ushort source) { EnsureBufferSize(m_bitLength + 16); NetBitWriter.WriteUInt16(source, 16, m_data, m_bitLength); @@ -168,7 +172,7 @@ public void Write(UInt16 source) /// Writes a 16 bit unsigned integer at a given offset in the buffer /// [CLSCompliant(false)] - public void WriteAt(Int32 offset, UInt16 source) + public void WriteAt(int offset, ushort source) { int newBitLength = Math.Max(m_bitLength, offset + 16); EnsureBufferSize(newBitLength); @@ -180,7 +184,7 @@ public void WriteAt(Int32 offset, UInt16 source) /// Writes an unsigned integer using 1 to 16 bits /// [CLSCompliant(false)] - public void Write(UInt16 source, int numberOfBits) + public void Write(ushort source, int numberOfBits) { NetException.Assert((numberOfBits > 0 && numberOfBits <= 16), "Write(ushort, numberOfBits) can only write between 1 and 16 bits"); EnsureBufferSize(m_bitLength + numberOfBits); @@ -191,7 +195,7 @@ public void Write(UInt16 source, int numberOfBits) /// /// Writes a signed 16 bit integer /// - public void Write(Int16 source) + public void Write(short source) { EnsureBufferSize(m_bitLength + 16); NetBitWriter.WriteUInt16((ushort)source, 16, m_data, m_bitLength); @@ -201,7 +205,7 @@ public void Write(Int16 source) /// /// Writes a 16 bit signed integer at a given offset in the buffer /// - public void WriteAt(Int32 offset, Int16 source) + public void WriteAt(int offset, short source) { int newBitLength = Math.Max(m_bitLength, offset + 16); EnsureBufferSize(newBitLength); @@ -212,7 +216,7 @@ public void WriteAt(Int32 offset, Int16 source) /// /// Writes a 32 bit signed integer /// - public void Write(Int32 source) + public void Write(int source) { EnsureBufferSize(m_bitLength + 32); @@ -223,7 +227,7 @@ public void Write(Int32 source) } else { - NetBitWriter.WriteUInt32((UInt32)source, 32, Data, m_bitLength); + NetBitWriter.WriteUInt32((uint)source, 32, Data, m_bitLength); } m_bitLength += 32; } @@ -232,11 +236,11 @@ public void Write(Int32 source) /// /// Writes a 32 bit signed integer at a given offset in the buffer /// - public void WriteAt(Int32 offset, Int32 source) + public void WriteAt(int offset, int source) { int newBitLength = Math.Max(m_bitLength, offset + 32); EnsureBufferSize(newBitLength); - NetBitWriter.WriteUInt32((UInt32)source, 32, m_data, offset); + NetBitWriter.WriteUInt32((uint)source, 32, m_data, offset); m_bitLength = newBitLength; } @@ -244,7 +248,7 @@ public void WriteAt(Int32 offset, Int32 source) /// Writes a 32 bit unsigned integer /// [CLSCompliant(false)] - public void Write(UInt32 source) + public void Write(uint source) { EnsureBufferSize(m_bitLength + 32); @@ -265,7 +269,7 @@ public void Write(UInt32 source) /// Writes a 32 bit unsigned integer at a given offset in the buffer /// [CLSCompliant(false)] - public void WriteAt(Int32 offset, UInt32 source) + public void WriteAt(int offset, uint source) { int newBitLength = Math.Max(m_bitLength, offset + 32); EnsureBufferSize(newBitLength); @@ -277,7 +281,7 @@ public void WriteAt(Int32 offset, UInt32 source) /// Writes a 32 bit signed integer /// [CLSCompliant(false)] - public void Write(UInt32 source, int numberOfBits) + public void Write(uint source, int numberOfBits) { NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(uint, numberOfBits) can only write between 1 and 32 bits"); EnsureBufferSize(m_bitLength + numberOfBits); @@ -288,7 +292,7 @@ public void Write(UInt32 source, int numberOfBits) /// /// Writes a signed integer using 1 to 32 bits /// - public void Write(Int32 source, int numberOfBits) + public void Write(int source, int numberOfBits) { NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(int, numberOfBits) can only write between 1 and 32 bits"); EnsureBufferSize(m_bitLength + numberOfBits); @@ -312,7 +316,7 @@ public void Write(Int32 source, int numberOfBits) /// Writes a 64 bit unsigned integer /// [CLSCompliant(false)] - public void Write(UInt64 source) + public void Write(ulong source) { EnsureBufferSize(m_bitLength + 64); NetBitWriter.WriteUInt64(source, 64, m_data, m_bitLength); @@ -323,7 +327,7 @@ public void Write(UInt64 source) /// Writes a 64 bit unsigned integer at a given offset in the buffer /// [CLSCompliant(false)] - public void WriteAt(Int32 offset, UInt64 source) + public void WriteAt(int offset, ulong source) { int newBitLength = Math.Max(m_bitLength, offset + 64); EnsureBufferSize(newBitLength); @@ -335,7 +339,7 @@ public void WriteAt(Int32 offset, UInt64 source) /// Writes an unsigned integer using 1 to 64 bits /// [CLSCompliant(false)] - public void Write(UInt64 source, int numberOfBits) + public void Write(ulong source, int numberOfBits) { EnsureBufferSize(m_bitLength + numberOfBits); NetBitWriter.WriteUInt64(source, numberOfBits, m_data, m_bitLength); @@ -345,7 +349,7 @@ public void Write(UInt64 source, int numberOfBits) /// /// Writes a 64 bit signed integer /// - public void Write(Int64 source) + public void Write(long source) { EnsureBufferSize(m_bitLength + 64); ulong usource = (ulong)source; @@ -356,7 +360,7 @@ public void Write(Int64 source) /// /// Writes a signed integer using 1 to 64 bits /// - public void Write(Int64 source, int numberOfBits) + public void Write(long source, int numberOfBits) { EnsureBufferSize(m_bitLength + numberOfBits); ulong usource = (ulong)source; @@ -405,7 +409,7 @@ public void Write(float source) /// public void Write(double source) { - long val = BitConverter.DoubleToInt64Bits(source); + long val = BitConverter.DoubleToInt64Bits(source); if (!BitConverter.IsLittleEndian) { val = BinaryPrimitives.ReverseEndianness(val); @@ -451,7 +455,7 @@ public int WriteVariableInt32(int value) /// Write Base128 encoded variable sized signed integer of up to 64 bits /// /// number of bytes written - public int WriteVariableInt64(Int64 value) + public int WriteVariableInt64(long value) { ulong zigzag = (ulong)(value << 1) ^ (ulong)(value >> 63); return WriteVariableUInt64(zigzag); @@ -462,10 +466,10 @@ public int WriteVariableInt64(Int64 value) /// /// number of bytes written [CLSCompliant(false)] - public int WriteVariableUInt64(UInt64 value) + public int WriteVariableUInt64(ulong value) { int retval = 1; - UInt64 num1 = (UInt64)value; + ulong num1 = (ulong)value; while (num1 >= 0x80) { this.Write((byte)(num1 | 0x80)); @@ -513,7 +517,7 @@ public void WriteRangedSingle(float value, float min, float max, int numberOfBit float range = max - min; float unit = ((value - min) / range); int maxVal = (1 << numberOfBits) - 1; - Write((UInt32)((float)maxVal * unit), numberOfBits); + Write((uint)((float)maxVal * unit), numberOfBits); } /// @@ -533,27 +537,27 @@ public int WriteRangedInteger(int min, int max, int value) return numBits; } - /// - /// Writes an integer with the least amount of bits need for the specified range - /// Returns number of bits written - /// - public int WriteRangedInteger(long min, long max, long value) - { - NetException.Assert(value >= min && value <= max, "Value not within min/max range!"); + /// + /// Writes an integer with the least amount of bits need for the specified range + /// Returns number of bits written + /// + public int WriteRangedInteger(long min, long max, long value) + { + NetException.Assert(value >= min && value <= max, "Value not within min/max range!"); - ulong range = (ulong)(max - min); - int numBits = NetUtility.BitsToHoldUInt64(range); + ulong range = (ulong)(max - min); + int numBits = NetUtility.BitsToHoldUInt64(range); - ulong rvalue = (ulong)(value - min); - Write(rvalue, numBits); + ulong rvalue = (ulong)(value - min); + Write(rvalue, numBits); - return numBits; - } + return numBits; + } /// /// Write a string /// - public void Write(string source) + public void Write(string? source) { if (string.IsNullOrEmpty(source)) { @@ -566,7 +570,7 @@ public void Write(string source) { Span byteSpan = stackalloc byte[byteCount]; Encoding.UTF8.GetBytes(source, byteSpan); - WriteVariableUInt32((uint) byteCount); + WriteVariableUInt32((uint)byteCount); Write(byteSpan); return; } @@ -636,7 +640,7 @@ public void Write(NetBuffer buffer) { EnsureBufferSize(m_bitLength + (buffer.LengthBytes * 8)); - Write(buffer.m_data, 0, buffer.LengthBytes); + Write(buffer.Data, 0, buffer.LengthBytes); // did we write excessive bits? int bitsInLastByte = (buffer.m_bitLength & 7); diff --git a/Lidgren.Network/NetBuffer.cs b/Lidgren.Network/NetBuffer.cs index 41a62186..e9475d55 100644 --- a/Lidgren.Network/NetBuffer.cs +++ b/Lidgren.Network/NetBuffer.cs @@ -14,17 +14,21 @@ public partial class NetBuffer private static readonly Dictionary s_readMethods; private static readonly Dictionary s_writeMethods; - internal byte[] m_data; + internal byte[]? m_data; internal int m_bitLength; internal int m_readPosition; /// - /// Gets or sets the internal data buffer + /// Gets or sets the internal data buffer. /// + /// + /// Throws an if internal field is null or if set a null value. + /// + /// public byte[] Data { - get { return m_data; } - set { m_data = value; } + get { return m_data ?? throw new InvalidOperationException("Buffer is is pooled and has no valid data buffer."); } + set { m_data = value ?? throw new ArgumentNullException(nameof(value)); } } /// @@ -69,7 +73,7 @@ public int PositionInBytes { get { return (int)(m_readPosition / 8); } } - + static NetBuffer() { s_readMethods = new Dictionary(); diff --git a/Lidgren.Network/NetClient.cs b/Lidgren.Network/NetClient.cs index 66c79708..abb58dfc 100644 --- a/Lidgren.Network/NetClient.cs +++ b/Lidgren.Network/NetClient.cs @@ -33,11 +33,11 @@ public class NetClient : NetPeer /// /// Gets the connection to the server, if any /// - public NetConnection ServerConnection + public NetConnection? ServerConnection { get { - NetConnection retval = null; + NetConnection? retval = null; if (m_connections.Count > 0) { try @@ -84,14 +84,14 @@ public NetClient(NetPeerConfiguration config) /// The remote endpoint to connect to /// The hail message to pass /// server connection, or null if already connected - public override NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage) + public override NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage? hailMessage) { lock (m_connections) { if (m_connections.Count > 0) { LogWarning("Connect attempt failed; Already connected"); - return null; + return null!; } } @@ -100,7 +100,7 @@ public override NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMes if (m_handshakes.Count > 0) { LogWarning("Connect attempt failed; Handshake already in progress"); - return null; + return null!; } } @@ -113,7 +113,7 @@ public override NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMes /// reason for disconnect public void Disconnect(string byeMessage) { - NetConnection serverConnection = ServerConnection; + NetConnection? serverConnection = ServerConnection; if (serverConnection == null) { lock (m_handshakes) @@ -138,7 +138,7 @@ public void Disconnect(string byeMessage) /// public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method) { - NetConnection serverConnection = ServerConnection; + NetConnection? serverConnection = ServerConnection; if (serverConnection == null) { LogWarning("Cannot send message, no server connection!"); @@ -153,7 +153,7 @@ public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod metho /// public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel) { - NetConnection serverConnection = ServerConnection; + NetConnection? serverConnection = ServerConnection; if (serverConnection == null) { LogWarning("Cannot send message, no server connection!"); @@ -169,7 +169,7 @@ public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod metho /// public override string ToString() { - return "[NetClient " + ServerConnection + "]"; + return $"[NetClient {ServerConnection}]"; } } diff --git a/Lidgren.Network/NetConnection.Handshake.cs b/Lidgren.Network/NetConnection.Handshake.cs index 933c1e09..5c611ff4 100644 --- a/Lidgren.Network/NetConnection.Handshake.cs +++ b/Lidgren.Network/NetConnection.Handshake.cs @@ -14,16 +14,16 @@ public partial class NetConnection internal bool m_connectRequested; internal bool m_disconnectRequested; internal bool m_disconnectReqSendBye; - internal string m_disconnectMessage; + internal string? m_disconnectMessage; internal bool m_connectionInitiator; - internal NetIncomingMessage m_remoteHailMessage; + internal NetIncomingMessage? m_remoteHailMessage; internal double m_lastHandshakeSendTime; internal int m_handshakeAttempts; /// /// The message that the remote part specified via Connect() or Approve() - can be null. /// - public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } } + public NetIncomingMessage? RemoteHailMessage { get { return m_remoteHailMessage; } } // heartbeat called when connection still is in m_handshakes of NetPeer internal void UnconnectedHeartbeat(double now) @@ -95,7 +95,7 @@ internal void UnconnectedHeartbeat(double now) } } - internal void ExecuteDisconnect(string reason, bool sendByeMessage) + internal void ExecuteDisconnect(string? reason, bool sendByeMessage) { m_peer.VerifyNetworkThread(); @@ -182,7 +182,7 @@ internal void SendConnectResponse(double now, bool onLibraryThread) SetStatus(NetConnectionStatus.RespondedConnect, "Remotely requested connect"); } - internal void SendDisconnect(string reason, bool onLibraryThread) + internal void SendDisconnect(string? reason, bool onLibraryThread) { if (onLibraryThread) m_peer.VerifyNetworkThread(); @@ -200,12 +200,12 @@ private void WriteLocalHail(NetOutgoingMessage om) { if (m_localHailMessage != null) { - byte[] hi = m_localHailMessage.Data; + byte[]? hi = m_localHailMessage.Data; if (hi != null && hi.Length >= m_localHailMessage.LengthBytes) { if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10) m_peer.ThrowOrLog("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes)); - om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes); + om.Write(hi, 0, m_localHailMessage.LengthBytes); } } } @@ -283,14 +283,13 @@ internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payl { m_peer.VerifyNetworkThread(); - byte[] hail; switch (tp) { case NetMessageType.Connect: if (m_status == NetConnectionStatus.ReceivedInitiation) { // Whee! Server full has already been checked - bool ok = ValidateHandshakeData(ptr, payloadLength, out hail); + bool ok = ValidateHandshakeData(ptr, payloadLength, out byte[]? hail); if (ok) { if (hail != null) @@ -300,7 +299,7 @@ internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payl } else { - m_remoteHailMessage = null; + m_remoteHailMessage = null; } if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval)) @@ -311,7 +310,7 @@ internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payl appMsg.m_senderConnection = this; appMsg.m_senderEndPoint = this.m_remoteEndPoint; if (m_remoteHailMessage != null) - appMsg.Write(m_remoteHailMessage.m_data, 0, m_remoteHailMessage.LengthBytes); + appMsg.Write(m_remoteHailMessage.Data, 0, m_remoteHailMessage.LengthBytes); SetStatus(NetConnectionStatus.RespondedAwaitingApproval, "Awaiting approval"); m_peer.ReleaseMessage(appMsg); return; @@ -357,7 +356,7 @@ internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payl break; case NetConnectionStatus.RespondedConnect: // awesome - + NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength); InitializeRemoteTimeOffset(msg.ReadSingle()); @@ -402,7 +401,7 @@ internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payl private void HandleConnectResponse(double now, NetMessageType tp, int ptr, int payloadLength) { - byte[] hail; + byte[]? hail; switch (m_status) { case NetConnectionStatus.InitiatedConnect: @@ -441,7 +440,7 @@ private void HandleConnectResponse(double now, NetMessageType tp, int ptr, int p } } - private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail) + private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[]? hail) { hail = null; diff --git a/Lidgren.Network/NetConnection.Latency.cs b/Lidgren.Network/NetConnection.Latency.cs index b7b031e4..18c00c33 100644 --- a/Lidgren.Network/NetConnection.Latency.cs +++ b/Lidgren.Network/NetConnection.Latency.cs @@ -62,8 +62,7 @@ internal void SendPing() om.m_messageType = NetMessageType.Ping; int len = om.Encode(m_peer.m_sendBuffer, 0, 0); - bool connectionReset; - m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset); + m_peer.SendPacket(len, m_remoteEndPoint, 1, out _); m_statistics.PacketSent(len, 1); m_peer.Recycle(om); @@ -79,9 +78,7 @@ internal void SendPong(int pingNumber) om.m_messageType = NetMessageType.Pong; int len = om.Encode(m_peer.m_sendBuffer, 0, 0); - bool connectionReset; - - m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset); + m_peer.SendPacket(len, m_remoteEndPoint, 1, out _); m_statistics.PacketSent(len, 1); m_peer.Recycle(om); diff --git a/Lidgren.Network/NetConnection.MTU.cs b/Lidgren.Network/NetConnection.MTU.cs index db3b385c..8468e66b 100644 --- a/Lidgren.Network/NetConnection.MTU.cs +++ b/Lidgren.Network/NetConnection.MTU.cs @@ -152,8 +152,7 @@ private void SendMTUSuccess(int size) om.Write(size); om.m_messageType = NetMessageType.ExpandMTUSuccess; int len = om.Encode(m_peer.m_sendBuffer, 0, 0); - bool connectionReset; - m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset); + m_peer.SendPacket(len, m_remoteEndPoint, 1, out _); m_peer.Recycle(om); //m_peer.LogDebug("Received MTU expand request for " + size + " bytes"); diff --git a/Lidgren.Network/NetConnection.cs b/Lidgren.Network/NetConnection.cs index cad69503..322831dd 100644 --- a/Lidgren.Network/NetConnection.cs +++ b/Lidgren.Network/NetConnection.cs @@ -26,19 +26,19 @@ public partial class NetConnection internal NetEndPoint m_remoteEndPoint; internal NetSenderChannelBase[] m_sendChannels; internal NetReceiverChannelBase[] m_receiveChannels; - internal NetOutgoingMessage m_localHailMessage; + internal NetOutgoingMessage? m_localHailMessage; internal long m_remoteUniqueIdentifier; internal NetQueue> m_queuedOutgoingAcks; internal NetQueue> m_queuedIncomingAcks; private int m_sendBufferWritePtr; private int m_sendBufferNumMessages; - private object m_tag; - internal NetConnectionStatistics m_statistics; + private object? m_tag; + internal readonly NetConnectionStatistics m_statistics; /// /// Gets or sets the application defined object containing data about the connection /// - public object Tag + public object? Tag { get { return m_tag; } set { m_tag = value; } @@ -70,9 +70,9 @@ public object Tag public long RemoteUniqueIdentifier { get { return m_remoteUniqueIdentifier; } } /// - /// Gets the local hail message that was sent as part of the handshake + /// Gets the local hail message that was sent as part of the handshake, if any /// - public NetOutgoingMessage LocalHailMessage { get { return m_localHailMessage; } } + public NetOutgoingMessage? LocalHailMessage { get { return m_localHailMessage; } } // gets the time before automatically resending an unacked message internal double GetResendDelay() @@ -113,7 +113,7 @@ internal void ResetTimeout(double now) m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout; } - internal void SetStatus(NetConnectionStatus status, string reason) + internal void SetStatus(NetConnectionStatus status, string? reason) { // user or library thread @@ -183,7 +183,6 @@ internal void Heartbeat(double now, uint frameCounter) } } - bool connectionReset; // TODO: handle connection reset // // Note: at this point m_sendBufferWritePtr and m_sendBufferNumMessages may be non-null; resends may already be queued up @@ -218,8 +217,7 @@ internal void Heartbeat(double now, uint frameCounter) // write acks for (int i = 0; i < acks; i++) { - NetTuple tuple; - m_queuedOutgoingAcks.TryDequeue(out tuple); + m_queuedOutgoingAcks.TryDequeue(out NetTuple tuple); //m_peer.LogVerbose("Sending ack for " + tuple.Item1 + "#" + tuple.Item2); @@ -232,7 +230,7 @@ internal void Heartbeat(double now, uint frameCounter) { // send packet and go for another round of acks NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0); - m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connectionReset); + m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out _); m_statistics.PacketSent(m_sendBufferWritePtr, 1); m_sendBufferWritePtr = 0; m_sendBufferNumMessages = 0; @@ -242,8 +240,7 @@ internal void Heartbeat(double now, uint frameCounter) // // Parse incoming acks (may trigger resends) // - NetTuple incAck; - while (m_queuedIncomingAcks.TryDequeue(out incAck)) + while (m_queuedIncomingAcks.TryDequeue(out NetTuple incAck)) { //m_peer.LogVerbose("Received ack for " + acktp + "#" + seqNr); NetSenderChannelBase chan = m_sendChannels[(int)incAck.Item1 - 1]; @@ -282,13 +279,13 @@ internal void Heartbeat(double now, uint frameCounter) { m_peer.VerifyNetworkThread(); NetException.Assert(m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0); - m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connectionReset); + m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out _); m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages); m_sendBufferWritePtr = 0; m_sendBufferNumMessages = 0; } } - + // Queue an item for immediate sending on the wire // This method is called from the ISenderChannels internal void QueueSendMessage(NetOutgoingMessage om, int seqNr) @@ -299,7 +296,6 @@ internal void QueueSendMessage(NetOutgoingMessage om, int seqNr) //if (sz > m_currentMTU) // m_peer.LogWarning("Message larger than MTU! Fragmentation must have failed!"); - bool connReset; // TODO: handle connection reset // can fit this message together with previously written to buffer? if (m_sendBufferWritePtr + sz > m_currentMTU) @@ -307,7 +303,7 @@ internal void QueueSendMessage(NetOutgoingMessage om, int seqNr) if (m_sendBufferWritePtr > 0 && m_sendBufferNumMessages > 0) { // previous message in buffer; send these first - m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset); + m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out _); m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages); m_sendBufferWritePtr = 0; m_sendBufferNumMessages = 0; @@ -321,7 +317,7 @@ internal void QueueSendMessage(NetOutgoingMessage om, int seqNr) if (m_sendBufferWritePtr > m_currentMTU) { // send immediately; we're already over MTU - m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out connReset); + m_peer.SendPacket(m_sendBufferWritePtr, m_remoteEndPoint, m_sendBufferNumMessages, out _); m_statistics.PacketSent(m_sendBufferWritePtr, m_sendBufferNumMessages); m_sendBufferWritePtr = 0; m_sendBufferNumMessages = 0; @@ -442,7 +438,7 @@ internal void ReceivedLibraryMessage(NetMessageType tp, int ptr, int payloadLeng //ExecuteDisconnect(msg.ReadString(), false); break; case NetMessageType.Acknowledge: - for (int i = 0; i < payloadLength; i+=3) + for (int i = 0; i < payloadLength; i += 3) { NetMessageType acktp = (NetMessageType)m_peer.m_receiveBuffer[ptr++]; // netmessagetype int seqNr = m_peer.m_receiveBuffer[ptr++]; @@ -568,7 +564,7 @@ public bool CanSendImmediately(NetDeliveryMethod method, int sequenceChannel) return chan.GetFreeWindowSlots() > 0; } - internal void Shutdown(string reason) + internal void Shutdown(string? reason) { ExecuteDisconnect(reason, true); } diff --git a/Lidgren.Network/NetException.cs b/Lidgren.Network/NetException.cs index b802a817..764b27bb 100644 --- a/Lidgren.Network/NetException.cs +++ b/Lidgren.Network/NetException.cs @@ -18,7 +18,8 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Diagnostics; -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace Lidgren.Network { @@ -55,7 +56,7 @@ public NetException(string message, Exception inner) /// Throws an exception, in DEBUG only, if first parameter is false /// [Conditional("DEBUG")] - public static void Assert(bool isOk, string message) + public static void Assert([DoesNotReturnIf(false)] bool isOk, string message) { if (!isOk) throw new NetException(message); @@ -65,10 +66,10 @@ public static void Assert(bool isOk, string message) /// Throws an exception, in DEBUG only, if first parameter is false /// [Conditional("DEBUG")] - public static void Assert(bool isOk) + public static void Assert([DoesNotReturnIf(false)] bool isOk) { if (!isOk) throw new NetException(); } } -} +} \ No newline at end of file diff --git a/Lidgren.Network/NetFragmentationInfo.cs b/Lidgren.Network/NetFragmentationInfo.cs index afd4e7db..a1995f30 100644 --- a/Lidgren.Network/NetFragmentationInfo.cs +++ b/Lidgren.Network/NetFragmentationInfo.cs @@ -4,9 +4,17 @@ namespace Lidgren.Network { public sealed class NetFragmentationInfo { - public int TotalFragmentCount; - public bool[] Received; - public int TotalReceived; - public int FragmentSize; + public int TotalFragmentCount { get; } + public bool[] Received{ get; } + public int TotalReceived{ get; } + public int FragmentSize{ get; } + + public NetFragmentationInfo(int totalFragmentCount, bool[] received, int totalReceived, int fragmentSize) + { + TotalFragmentCount = totalFragmentCount; + Received = received; + TotalReceived = totalReceived; + FragmentSize = fragmentSize; + } } -} +} \ No newline at end of file diff --git a/Lidgren.Network/NetIncomingMessage.cs b/Lidgren.Network/NetIncomingMessage.cs index f8b4c3b2..d3a1b7cf 100644 --- a/Lidgren.Network/NetIncomingMessage.cs +++ b/Lidgren.Network/NetIncomingMessage.cs @@ -33,8 +33,8 @@ namespace Lidgren.Network public sealed class NetIncomingMessage : NetBuffer { internal NetIncomingMessageType m_incomingMessageType; - internal NetEndPoint m_senderEndPoint; - internal NetConnection m_senderConnection; + internal NetEndPoint? m_senderEndPoint; + internal NetConnection? m_senderConnection; internal int m_sequenceNumber; internal NetMessageType m_receivedMessageType; internal bool m_isFragment; @@ -58,12 +58,12 @@ public sealed class NetIncomingMessage : NetBuffer /// /// endpoint of sender, if any /// - public NetEndPoint SenderEndPoint { get { return m_senderEndPoint; } } + public NetEndPoint? SenderEndPoint { get { return m_senderEndPoint; } } /// /// NetConnection of sender, if any /// - public NetConnection SenderConnection { get { return m_senderConnection; } } + public NetConnection? SenderConnection { get { return m_senderConnection; } } /// /// What local time the message was received from the network diff --git a/Lidgren.Network/NetOutgoingMessage.cs b/Lidgren.Network/NetOutgoingMessage.cs index 1e007c21..bef7a00a 100644 --- a/Lidgren.Network/NetOutgoingMessage.cs +++ b/Lidgren.Network/NetOutgoingMessage.cs @@ -78,7 +78,7 @@ internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber) int byteLen = NetUtility.BytesToHoldBits(m_bitLength); if (byteLen > 0) { - Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen); + Buffer.BlockCopy(Data, 0, intoBuffer, ptr, byteLen); ptr += byteLen; } } @@ -102,7 +102,7 @@ internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber) int byteLen = NetUtility.BytesToHoldBits(m_bitLength); if (byteLen > 0) { - Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen); + Buffer.BlockCopy(Data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen); ptr += byteLen; } } @@ -125,10 +125,7 @@ internal int GetEncodedSize() /// public override string ToString() { - if (m_isSent) - return "[NetOutgoingMessage " + m_messageType + " " + this.LengthBytes + " bytes]"; - - return "[NetOutgoingMessage " + this.LengthBytes + " bytes]"; + return m_isSent ? $"[NetOutgoingMessage {m_messageType} {LengthBytes} bytes]" : $"[NetOutgoingMessage {LengthBytes} bytes]"; } } -} +} \ No newline at end of file diff --git a/Lidgren.Network/NetPeer.Discovery.cs b/Lidgren.Network/NetPeer.Discovery.cs index d5d3a97d..e7d05d87 100644 --- a/Lidgren.Network/NetPeer.Discovery.cs +++ b/Lidgren.Network/NetPeer.Discovery.cs @@ -19,7 +19,11 @@ public void DiscoverLocalPeers(int serverPort) um.m_messageType = NetMessageType.Discovery; Interlocked.Increment(ref um.m_recyclingCount); - m_unsentUnconnectedMessages.Enqueue(new NetTuple(new NetEndPoint(NetUtility.GetBroadcastAddress(), serverPort), um)); + var broadcastAddress = NetUtility.GetBroadcastAddress(); + if (broadcastAddress == null) + throw new NetException("Unable to determine broadcast address."); + + m_unsentUnconnectedMessages.Enqueue(new NetTuple(new NetEndPoint(broadcastAddress, serverPort), um)); } /// diff --git a/Lidgren.Network/NetPeer.Fragmentation.cs b/Lidgren.Network/NetPeer.Fragmentation.cs index 98cb08a9..64fd7906 100644 --- a/Lidgren.Network/NetPeer.Fragmentation.cs +++ b/Lidgren.Network/NetPeer.Fragmentation.cs @@ -4,18 +4,11 @@ namespace Lidgren.Network { - internal class ReceivedFragmentGroup - { - //public float LastReceived; - public byte[] Data; - public NetBitVector ReceivedChunks; - } - public partial class NetPeer { private int m_lastUsedFragmentGroup; - private Dictionary> m_receivedFragmentGroups; + private readonly Dictionary> m_receivedFragmentGroups; // on user thread private NetSendResult SendFragmentedMessage(NetOutgoingMessage msg, IList recipients, NetDeliveryMethod method, int sequenceChannel) @@ -86,16 +79,12 @@ private void HandleReleasedFragment(NetIncomingMessage im) // // read fragmentation header and combine fragments // - int group; - int totalBits; - int chunkByteSize; - int chunkNumber; int ptr = NetFragmentationHelper.ReadHeader( - im.m_data, 0, - out group, - out totalBits, - out chunkByteSize, - out chunkNumber + im.Data, 0, + out int group, + out int totalBits, + out int chunkByteSize, + out int chunkNumber ); NetException.Assert(im.LengthBytes > ptr); @@ -113,23 +102,21 @@ out chunkNumber if (chunkNumber >= totalNumChunks) { - LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")"); + LogWarning($"Index out of bounds for chunk {chunkNumber} (total chunks {totalNumChunks})"); return; } - Dictionary groups; - if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups)) + NetException.Assert(im.SenderConnection != null); + + if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out Dictionary? groups)) { groups = new Dictionary(); m_receivedFragmentGroups[im.SenderConnection] = groups; } - ReceivedFragmentGroup info; - if (!groups.TryGetValue(group, out info)) + if (!groups.TryGetValue(group, out ReceivedFragmentGroup? info)) { - info = new ReceivedFragmentGroup(); - info.Data = new byte[totalBytes]; - info.ReceivedChunks = new NetBitVector(totalNumChunks); + info = new ReceivedFragmentGroup(new byte[totalBytes], new NetBitVector(totalNumChunks)); groups[group] = info; } @@ -138,12 +125,12 @@ out chunkNumber // copy to data int offset = (chunkNumber * chunkByteSize); - Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr); + Buffer.BlockCopy(im.Data, ptr, info.Data, offset, im.LengthBytes - ptr); int cnt = info.ReceivedChunks.Count(); - //LogVerbose("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")"); + //LogVerbose($"Found fragment #{chunkNumber} in group {group} offset {offset} of total bits {totalBits} (total chunks done {cnt})"); - LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)"); + LogVerbose($"Received fragment {chunkNumber} of {totalNumChunks} ({cnt} chunks received)"); if (info.ReceivedChunks.Count() == totalNumChunks) { @@ -152,7 +139,7 @@ out chunkNumber im.m_bitLength = (int)totalBits; im.m_isFragment = false; - LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)"); + LogVerbose($"Fragment group #{group} fully received in {totalNumChunks} chunks ({totalBits} bits)"); groups.Remove(group); ReleaseMessage(im); diff --git a/Lidgren.Network/NetPeer.Internal.cs b/Lidgren.Network/NetPeer.Internal.cs index 3bf402da..5876a38c 100644 --- a/Lidgren.Network/NetPeer.Internal.cs +++ b/Lidgren.Network/NetPeer.Internal.cs @@ -5,6 +5,8 @@ using System.Security.Cryptography; using System.Net.Sockets; using System.Collections.Generic; +using System.Xml.Linq; +using System.Diagnostics.CodeAnalysis; #if !__NOIPENDPOINT__ using NetEndPoint = System.Net.IPEndPoint; @@ -15,17 +17,17 @@ namespace Lidgren.Network public partial class NetPeer { private NetPeerStatus m_status; - private Thread m_networkThread; - private Socket m_socket; - internal byte[] m_sendBuffer; - internal byte[] m_receiveBuffer; - internal NetIncomingMessage m_readHelperMessage; + private Thread? m_networkThread; + private Socket? m_socket; + internal readonly byte[] m_sendBuffer; + internal readonly byte[] m_receiveBuffer; + internal readonly NetIncomingMessage m_readHelperMessage; private EndPoint m_senderRemote; - private object m_initializeLock = new object(); + private readonly object m_initializeLock = new object(); private uint m_frameCounter; private double m_lastHeartbeat; private double m_lastSocketBind = float.MinValue; - private NetUPnP m_upnp; + private NetUPnP? m_upnp; internal bool m_needFlushSendQueue; internal readonly NetPeerConfiguration m_configuration; @@ -38,18 +40,18 @@ public partial class NetPeer internal long m_uniqueIdentifier; internal bool m_executeFlushSendQueue; - private AutoResetEvent m_messageReceivedEvent; - private List> m_receiveCallbacks; + private AutoResetEvent? m_messageReceivedEvent; + private List>? m_receiveCallbacks; /// /// Gets the socket, if Start() has been called /// - public Socket Socket { get { return m_socket; } } + public Socket? Socket { get { return m_socket; } } /// /// Call this to register a callback for when a new message arrives /// - public void RegisterReceivedCallback(SendOrPostCallback callback, SynchronizationContext syncContext = null) + public void RegisterReceivedCallback(SendOrPostCallback callback, SynchronizationContext? syncContext = null) { if (syncContext == null) syncContext = SynchronizationContext.Current; @@ -69,7 +71,7 @@ public void UnregisterReceivedCallback(SendOrPostCallback callback) return; // remove all callbacks regardless of sync context - m_receiveCallbacks.RemoveAll(tuple => tuple.Item2.Equals(callback)); + m_receiveCallbacks.RemoveAll(tuple => tuple.Item2.Equals(callback)); if (m_receiveCallbacks.Count < 1) m_receiveCallbacks = null; @@ -106,7 +108,7 @@ internal void ReleaseMessage(NetIncomingMessage msg) } } - private static object _bindSocketMutex = new object(); + private static readonly object _bindSocketMutex = new object(); private void BindSocket(bool reBind) { @@ -117,7 +119,7 @@ private void BindSocket(bool reBind) return; // only allow rebind once every second } m_lastSocketBind = now; - + if (m_socket == null) m_socket = new Socket(m_configuration.LocalAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); @@ -128,19 +130,19 @@ private void BindSocket(bool reBind) m_socket.SendBufferSize = m_configuration.SendBufferSize; m_socket.Blocking = false; - if (m_configuration.DualStack) - { - if (m_configuration.LocalAddress.AddressFamily != AddressFamily.InterNetworkV6) - { - LogWarning("Configuration specifies Dual Stack but does not use IPv6 local address; Dual stack will not work."); - } - else - { - m_socket.DualMode = true; - } - } - - var ep = (EndPoint)new NetEndPoint(m_configuration.LocalAddress, reBind ? m_listenPort : m_configuration.Port); + if (m_configuration.DualStack) + { + if (m_configuration.LocalAddress.AddressFamily != AddressFamily.InterNetworkV6) + { + LogWarning("Configuration specifies Dual Stack but does not use IPv6 local address; Dual stack will not work."); + } + else + { + m_socket.DualMode = true; + } + } + + var ep = (EndPoint)new NetEndPoint(m_configuration.LocalAddress, reBind ? m_listenPort : m_configuration.Port); m_socket.Bind(ep); try @@ -155,7 +157,8 @@ private void BindSocket(bool reBind) // ignore; SIO_UDP_CONNRESET not supported on this platform } - var boundEp = m_socket.LocalEndPoint as NetEndPoint; + NetException.Assert(m_socket.LocalEndPoint != null); + var boundEp = (NetEndPoint)m_socket.LocalEndPoint; LogDebug("Socket bound to " + boundEp + ": " + m_socket.IsBound); m_listenPort = boundEp.Port; } @@ -166,9 +169,6 @@ private void InitializeNetwork() { m_configuration.Lock(); - if (m_status == NetPeerStatus.Running) - return; - if (m_configuration.m_enableUPnP) m_upnp = new NetUPnP(this); @@ -187,14 +187,11 @@ private void InitializeNetwork() // bind to socket BindSocket(false); - m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize]; - m_sendBuffer = new byte[m_configuration.SendBufferSize]; - m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error); - m_readHelperMessage.m_data = m_receiveBuffer; - byte[] macBytes = NetUtility.GetMacAddressBytes() ?? new byte[0]; - var boundEp = m_socket.LocalEndPoint as NetEndPoint; + Debug.Assert(m_socket?.LocalEndPoint != null); + NetEndPoint boundEp = (NetEndPoint) m_socket.LocalEndPoint; + byte[] epBytes = BitConverter.GetBytes(boundEp.GetHashCode()); byte[] combined = new byte[epBytes.Length + macBytes.Length]; Array.Copy(epBytes, 0, combined, 0, epBytes.Length); @@ -275,7 +272,7 @@ private void ExecutePeerShutdown() { m_socket.Shutdown(SocketShutdown.Receive); } - catch(Exception ex) + catch (Exception ex) { LogDebug("Socket.Shutdown exception: " + ex.ToString()); } @@ -302,8 +299,6 @@ private void ExecutePeerShutdown() } m_lastSocketBind = float.MinValue; - m_receiveBuffer = null; - m_sendBuffer = null; m_unsentUnconnectedMessages.Clear(); m_connections.Clear(); m_connectionLookup.Clear(); @@ -377,15 +372,14 @@ private void Heartbeat() // remove connection // m_connections.RemoveAt(i); - m_connectionLookup.Remove((NetSocketAddress) conn.RemoteEndPoint); + m_connectionLookup.Remove((NetSocketAddress)conn.RemoteEndPoint); } } } m_executeFlushSendQueue = false; // send unsent unconnected messages - NetTuple unsent; - while (m_unsentUnconnectedMessages.TryDequeue(out unsent)) + while (m_unsentUnconnectedMessages.TryDequeue(out NetTuple unsent)) { NetOutgoingMessage om = unsent.Item2; @@ -395,13 +389,12 @@ private void Heartbeat() if (om.m_recyclingCount <= 0) Recycle(om); - bool connReset; - SendPacket(len, unsent.Item1, 1, out connReset); + SendPacket(len, unsent.Item1, 1, out bool connReset); } } - if (m_upnp != null) - m_upnp.CheckForDiscoveryTimeout(); + if (m_upnp != null) + m_upnp.CheckForDiscoveryTimeout(); // // read from socket @@ -418,44 +411,46 @@ private void Heartbeat() // update now now = NetTime.Now; - try - { - do - { - ReceiveSocketData(now); - } while (m_socket.Available > 0); - } - catch (SocketException sx) - { - switch (sx.SocketErrorCode) - { - case SocketError.ConnectionReset: - // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable" - // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?! - // So, what to do? - LogWarning("ConnectionReset"); - return; - - case SocketError.NotConnected: - // socket is unbound; try to rebind it (happens on mobile when process goes to sleep) - BindSocket(true); - return; - - default: - LogWarning("Socket exception: " + sx.ToString()); - return; - } - } + try + { + do + { + ReceiveSocketData(now); + } while (m_socket.Available > 0); + } + catch (SocketException sx) + { + switch (sx.SocketErrorCode) + { + case SocketError.ConnectionReset: + // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable" + // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?! + // So, what to do? + LogWarning("ConnectionReset"); + return; + + case SocketError.NotConnected: + // socket is unbound; try to rebind it (happens on mobile when process goes to sleep) + BindSocket(true); + return; + + default: + LogWarning("Socket exception: " + sx.ToString()); + return; + } + } } - private void ReceiveSocketData(double now) - { - int bytesReceived = NetFastSocket.ReceiveFrom( - m_socket, - m_receiveBuffer, 0, m_receiveBuffer.Length, - SocketFlags.None, - out var senderRemote, - ref m_senderRemote); + private void ReceiveSocketData(double now) + { + Debug.Assert(m_socket != null); + + int bytesReceived = NetFastSocket.ReceiveFrom( + m_socket, + m_receiveBuffer, 0, m_receiveBuffer.Length, + SocketFlags.None, + out var senderRemote, + ref m_senderRemote); if (bytesReceived < NetConstants.HeaderByteSize) return; @@ -494,8 +489,7 @@ private void ReceiveSocketData(double now) } } - NetConnection sender = null; - m_connectionLookup.TryGetValue(senderRemote, out sender); + m_connectionLookup.TryGetValue(senderRemote, out NetConnection? sender); // // parse packet into messages @@ -529,7 +523,7 @@ private void ReceiveSocketData(double now) if (bytesReceived - ptr < payloadByteLength) { - LogWarning("Malformed packet from " + (NetEndPoint) senderRemote + "; stated payload length " + payloadByteLength + LogWarning("Malformed packet from " + (NetEndPoint)senderRemote + "; stated payload length " + payloadByteLength + ", remaining bytes " + (bytesReceived - ptr)); return; } @@ -547,7 +541,7 @@ private void ReceiveSocketData(double now) if (sender != null) sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength); else - ReceivedUnconnectedLibraryMessage(now, (NetEndPoint) senderRemote, tp, ptr, payloadByteLength); + ReceivedUnconnectedLibraryMessage(now, (NetEndPoint)senderRemote, tp, ptr, payloadByteLength); } else { @@ -560,10 +554,10 @@ private void ReceiveSocketData(double now) msg.m_sequenceNumber = sequenceNumber; msg.m_receivedMessageType = tp; msg.m_senderConnection = sender; - msg.m_senderEndPoint = (NetEndPoint) senderRemote; + msg.m_senderEndPoint = (NetEndPoint)senderRemote; msg.m_bitLength = payloadBitLength; - Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength); + Buffer.BlockCopy(m_receiveBuffer, ptr, msg.Data, 0, payloadByteLength); if (sender != null) { if (tp == NetMessageType.Unconnected) @@ -589,7 +583,7 @@ private void ReceiveSocketData(double now) } catch (Exception ex) { - LogError("Packet parsing error: " + ex.Message + " from " + (NetEndPoint) senderRemote); + LogError("Packet parsing error: " + ex.Message + " from " + (NetEndPoint)senderRemote); } ptr += payloadByteLength; } @@ -597,9 +591,9 @@ private void ReceiveSocketData(double now) m_statistics.PacketReceived(bytesReceived, numMessages, numFragments); if (sender != null) sender.m_statistics.PacketReceived(bytesReceived, numMessages, numFragments); - } + } - /// + /// /// If NetPeerConfiguration.AutoFlushSendQueue() is false; you need to call this to send all messages queued using SendMessage() /// public void FlushSendQueue() @@ -609,36 +603,47 @@ public void FlushSendQueue() internal void HandleIncomingDiscoveryRequest(double now, NetEndPoint senderEndPoint, int ptr, int payloadByteLength) { - if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryRequest)) + if (!m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryRequest)) { - NetIncomingMessage dm = CreateIncomingMessage(NetIncomingMessageType.DiscoveryRequest, payloadByteLength); - if (payloadByteLength > 0) - Buffer.BlockCopy(m_receiveBuffer, ptr, dm.m_data, 0, payloadByteLength); - dm.m_receiveTime = now; - dm.m_bitLength = payloadByteLength * 8; - dm.m_senderEndPoint = senderEndPoint; - ReleaseMessage(dm); + return; } + + NetIncomingMessage dm = CreateIncomingMessage(NetIncomingMessageType.DiscoveryRequest, payloadByteLength); + + if (payloadByteLength > 0) + { + Buffer.BlockCopy(m_receiveBuffer, ptr, dm.Data, 0, payloadByteLength); + } + + dm.m_receiveTime = now; + dm.m_bitLength = payloadByteLength * 8; + dm.m_senderEndPoint = senderEndPoint; + ReleaseMessage(dm); } internal void HandleIncomingDiscoveryResponse(double now, NetEndPoint senderEndPoint, int ptr, int payloadByteLength) { - if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryResponse)) + if (!m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryResponse)) + { + return; + } + + NetIncomingMessage dr = CreateIncomingMessage(NetIncomingMessageType.DiscoveryResponse, payloadByteLength); + + if (payloadByteLength > 0) { - NetIncomingMessage dr = CreateIncomingMessage(NetIncomingMessageType.DiscoveryResponse, payloadByteLength); - if (payloadByteLength > 0) - Buffer.BlockCopy(m_receiveBuffer, ptr, dr.m_data, 0, payloadByteLength); - dr.m_receiveTime = now; - dr.m_bitLength = payloadByteLength * 8; - dr.m_senderEndPoint = senderEndPoint; - ReleaseMessage(dr); + Buffer.BlockCopy(m_receiveBuffer, ptr, dr.Data, 0, payloadByteLength); } + + dr.m_receiveTime = now; + dr.m_bitLength = payloadByteLength * 8; + dr.m_senderEndPoint = senderEndPoint; + ReleaseMessage(dr); } private void ReceivedUnconnectedLibraryMessage(double now, NetEndPoint senderEndPoint, NetMessageType tp, int ptr, int payloadByteLength) { - NetConnection shake; - if (m_handshakes.TryGetValue(senderEndPoint, out shake)) + if (m_handshakes.TryGetValue(senderEndPoint, out NetConnection? shake)) { shake.ReceivedHandshake(now, tp, ptr, payloadByteLength); return; @@ -771,7 +776,7 @@ internal void AcceptConnection(NetConnection conn) else { m_connections.Add(conn); - m_connectionLookup.Add((NetSocketAddress) conn.m_remoteEndPoint, conn); + m_connectionLookup.Add((NetSocketAddress)conn.m_remoteEndPoint, conn); } } } diff --git a/Lidgren.Network/NetPeer.LatencySimulation.cs b/Lidgren.Network/NetPeer.LatencySimulation.cs index ba3261d9..08422dc2 100644 --- a/Lidgren.Network/NetPeer.LatencySimulation.cs +++ b/Lidgren.Network/NetPeer.LatencySimulation.cs @@ -34,13 +34,20 @@ namespace Lidgren.Network public partial class NetPeer { private readonly List m_delayedPackets = new List(); - private MWCRandom m_latencyRandom = new MWCRandom(); + private readonly MWCRandom m_latencyRandom = new MWCRandom(); private sealed class DelayedPacket { public byte[] Data; public double DelayedUntil; public NetEndPoint Target; + + public DelayedPacket(byte[] data, double delayedUntil, NetEndPoint target) + { + Data = data; + DelayedUntil = delayedUntil; + Target = target; + } } internal void SendPacket(int numBytes, NetEndPoint target, int numMessages, out bool connectionReset) @@ -80,17 +87,14 @@ internal void SendPacket(int numBytes, NetEndPoint target, int numMessages, out if (m_configuration.m_duplicates > 0.0f && m_latencyRandom.NextSingle() < m_configuration.m_duplicates) num++; - float delay = 0; for (int i = 0; i < num; i++) { - delay = m + (m_latencyRandom.NextSingle() * r); + float delay = m + (m_latencyRandom.NextSingle() * r); // Enqueue delayed packet - DelayedPacket p = new DelayedPacket(); - p.Target = target; - p.Data = new byte[numBytes]; + DelayedPacket p = new DelayedPacket(new byte[numBytes], NetTime.Now + delay, target); + Buffer.BlockCopy(m_sendBuffer, 0, p.Data, 0, numBytes); - p.DelayedUntil = NetTime.Now + delay; m_delayedPackets.Add(p); } @@ -105,23 +109,21 @@ private void SendDelayedPackets() double now = NetTime.Now; - bool connectionReset; for (var i = 0; i < m_delayedPackets.Count; i++) { var p = m_delayedPackets[i]; - if (now < p.DelayedUntil) + if (now < p.DelayedUntil) continue; - - ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset); - + ActuallySendPacket(p.Data, p.Data.Length, p.Target, out _); + // Swap packet with last entry in list. // This does not preserve order (we don't care) but is O(1). var replaceIdx = m_delayedPackets.Count - 1; var replacement = m_delayedPackets[replaceIdx]; m_delayedPackets[i] = replacement; m_delayedPackets.RemoveAt(replaceIdx); - + // Make sure to decrement i so we re-process the element we just swapped in. i -= 1; } @@ -131,53 +133,55 @@ private void FlushDelayedPackets() { try { - bool connectionReset; foreach (DelayedPacket p in m_delayedPackets) - ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset); + ActuallySendPacket(p.Data, p.Data.Length, p.Target, out bool connectionReset); m_delayedPackets.Clear(); } catch { } } - //Avoids allocation on mapping to IPv6 - private IPEndPoint targetCopy = new IPEndPoint(IPAddress.Any, 0); - private IPEndPoint targetCopy2 = new IPEndPoint(IPAddress.Any, 0); + //Avoids allocation on mapping to IPv6 + private readonly IPEndPoint targetCopy = new IPEndPoint(IPAddress.Any, 0); + private readonly IPEndPoint targetCopy2 = new IPEndPoint(IPAddress.Any, 0); internal bool ActuallySendPacket(byte[] data, int numBytes, NetEndPoint target, out bool connectionReset) { var dualStack = m_configuration.DualStack && m_configuration.LocalAddress.AddressFamily == AddressFamily.InterNetworkV6; connectionReset = false; - IPAddress ba = default(IPAddress); + IPAddress? ba = default(IPAddress); + + NetException.Assert(m_socket != null); + try { var realTarget = target; ba = NetUtility.GetCachedBroadcastAddress(); - // TODO: refactor this check outta here - if (target.Address.Equals(ba)) - { - // Some networks do not allow - // a global broadcast so we use the BroadcastAddress from the configuration - // this can be resolved to a local broadcast addresss e.g 192.168.x.255 - targetCopy.Address = m_configuration.BroadcastAddress; - targetCopy.Port = target.Port; - realTarget = targetCopy; - m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); - - if (dualStack) - { - NetUtility.CopyEndpoint(realTarget, targetCopy2); //Maps to IPv6 for Dual Mode - realTarget = targetCopy2; - } - - } - else if (dualStack) - { - NetUtility.CopyEndpoint(target, targetCopy); //Maps to IPv6 for Dual Mode - realTarget = targetCopy; - } - - int bytesSent = NetFastSocket.SendTo(m_socket, data, 0, numBytes, SocketFlags.None, realTarget); + // TODO: refactor this check outta here + if (target.Address.Equals(ba)) + { + // Some networks do not allow + // a global broadcast so we use the BroadcastAddress from the configuration + // this can be resolved to a local broadcast addresss e.g 192.168.x.255 + targetCopy.Address = m_configuration.BroadcastAddress; + targetCopy.Port = target.Port; + realTarget = targetCopy; + m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); + + if (dualStack) + { + NetUtility.CopyEndpoint(realTarget, targetCopy2); //Maps to IPv6 for Dual Mode + realTarget = targetCopy2; + } + + } + else if (dualStack) + { + NetUtility.CopyEndpoint(target, targetCopy); //Maps to IPv6 for Dual Mode + realTarget = targetCopy; + } + + int bytesSent = NetFastSocket.SendTo(m_socket, data, 0, numBytes, SocketFlags.None, realTarget); if (numBytes != bytesSent) LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!"); @@ -215,7 +219,9 @@ internal bool SendMTUPacket(int numBytes, NetEndPoint target) { if (!CanAutoExpandMTU) throw new NotSupportedException("MTU expansion not currently supported on this operating system"); - + + NetException.Assert(m_socket != null); + try { // NOTE: Socket.DontFragment doesn't work on dual-stack sockets. @@ -223,7 +229,7 @@ internal bool SendMTUPacket(int numBytes, NetEndPoint target) // See: https://github.com/dotnet/runtime/issues/76410 if (m_socket.DualMode || target.AddressFamily == AddressFamily.InterNetwork) m_socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.DontFragment, true); - + int bytesSent = NetFastSocket.SendTo(m_socket, m_sendBuffer, 0, numBytes, SocketFlags.None, target); if (numBytes != bytesSent) LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!"); diff --git a/Lidgren.Network/NetPeer.Logging.cs b/Lidgren.Network/NetPeer.Logging.cs index 32499728..6add471b 100644 --- a/Lidgren.Network/NetPeer.Logging.cs +++ b/Lidgren.Network/NetPeer.Logging.cs @@ -24,7 +24,7 @@ namespace Lidgren.Network { public partial class NetPeer { - internal event Action LogEvent; + internal event Action? LogEvent; [Conditional("DEBUG")] internal void LogVerbose(string message) @@ -63,7 +63,7 @@ internal void LogError(string message) private void SendLogBase(NetIncomingMessageType type, string text) { LogEvent?.Invoke(type, text); - + if (m_configuration.IsMessageTypeEnabled(type)) ReleaseMessage(CreateIncomingMessage(type, text)); } diff --git a/Lidgren.Network/NetPeer.MessagePools.cs b/Lidgren.Network/NetPeer.MessagePools.cs index bf616679..01c53e9b 100644 --- a/Lidgren.Network/NetPeer.MessagePools.cs +++ b/Lidgren.Network/NetPeer.MessagePools.cs @@ -6,9 +6,9 @@ namespace Lidgren.Network { public partial class NetPeer { - internal List m_storagePool; - private NetQueue m_outgoingMessagesPool; - private NetQueue m_incomingMessagesPool; + internal List? m_storagePool; + private NetQueue? m_outgoingMessagesPool; + private NetQueue? m_incomingMessagesPool; internal int m_storagePoolBytes; internal int m_storageSlotsUsedCount; @@ -20,7 +20,7 @@ private void InitializePools() if (m_configuration.UseMessageRecycling) { - m_storagePool = new List(16); + m_storagePool = new List(16); m_outgoingMessagesPool = new NetQueue(4); m_incomingMessagesPool = new NetQueue(4); } @@ -43,7 +43,7 @@ internal byte[] GetStorage(int minimumCapacityInBytes) { for (int i = 0; i < m_storagePool.Count; i++) { - byte[] retval = m_storagePool[i]; + byte[]? retval = m_storagePool[i]; if (retval != null && retval.Length >= minimumCapacityInBytes) { m_storagePool[i] = null; @@ -59,7 +59,7 @@ internal byte[] GetStorage(int minimumCapacityInBytes) internal void Recycle(byte[] storage) { - if (m_storagePool == null || storage == null) + if (m_storagePool == null) return; lock (m_storagePool) @@ -81,9 +81,9 @@ internal void Recycle(byte[] storage) // pool is full; replace randomly chosen entry to keep size distribution var idx = NetRandom.Instance.Next(m_storagePool.Count); - m_storagePoolBytes -= m_storagePool[idx].Length; + m_storagePoolBytes -= m_storagePool[idx]!.Length; m_storagePoolBytes += storage.Length; - + m_storagePool[idx] = storage; // replace } else @@ -106,23 +106,23 @@ public NetOutgoingMessage CreateMessage() /// /// Creates a new message for sending and writes the provided string to it /// - public NetOutgoingMessage CreateMessage(string content) - { - NetOutgoingMessage om; - - // Since this could be null. - if (string.IsNullOrEmpty(content)) - { - om = CreateMessage(1); // One byte for the internal variable-length zero byte. - } - else - { - om = CreateMessage(2 + content.Length); // Fair guess. - } - - om.Write(content); - return om; - } + public NetOutgoingMessage CreateMessage(string? content) + { + NetOutgoingMessage om; + + // Since this could be null. + if (string.IsNullOrEmpty(content)) + { + om = CreateMessage(1); // One byte for the internal variable-length zero byte. + } + else + { + om = CreateMessage(2 + content.Length); // Fair guess. + } + + om.Write(content); + return om; + } /// /// Creates a new message for sending @@ -130,8 +130,7 @@ public NetOutgoingMessage CreateMessage(string content) /// initial capacity in bytes public NetOutgoingMessage CreateMessage(int initialCapacity) { - NetOutgoingMessage retval; - if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval)) + if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out NetOutgoingMessage? retval)) retval = new NetOutgoingMessage(); NetException.Assert(retval.m_recyclingCount == 0, "Wrong recycling count! Should be zero" + retval.m_recyclingCount); @@ -144,8 +143,7 @@ public NetOutgoingMessage CreateMessage(int initialCapacity) internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] useStorageData) { - NetIncomingMessage retval; - if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval)) + if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out NetIncomingMessage? retval)) retval = new NetIncomingMessage(tp); else retval.m_incomingMessageType = tp; @@ -155,8 +153,7 @@ internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byt internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, int minimumByteSize) { - NetIncomingMessage retval; - if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval)) + if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out NetIncomingMessage? retval)) retval = new NetIncomingMessage(tp); else retval.m_incomingMessageType = tp; @@ -174,9 +171,10 @@ public void Recycle(NetIncomingMessage msg) NetException.Assert(m_incomingMessagesPool.Contains(msg) == false, "Recyling already recycled incoming message! Thread race?"); - byte[] storage = msg.m_data; + byte[] storage = msg.Data; msg.m_data = null; Recycle(storage); + msg.Reset(); if (m_incomingMessagesPool.Count < m_maxCacheCount) @@ -207,7 +205,7 @@ internal void Recycle(NetOutgoingMessage msg) // however, in RELEASE, we'll just have to accept this and move on with life msg.m_recyclingCount = 0; - byte[] storage = msg.m_data; + byte[] storage = msg.Data; msg.m_data = null; // message fragments cannot be recycled diff --git a/Lidgren.Network/NetPeer.ReceivedFragmentGroup.cs b/Lidgren.Network/NetPeer.ReceivedFragmentGroup.cs new file mode 100644 index 00000000..bc0d4356 --- /dev/null +++ b/Lidgren.Network/NetPeer.ReceivedFragmentGroup.cs @@ -0,0 +1,17 @@ +namespace Lidgren.Network; + +public partial class NetPeer +{ + private sealed class ReceivedFragmentGroup + { + //public float LastReceived; + public byte[] Data { get; } + public NetBitVector ReceivedChunks { get; } + + public ReceivedFragmentGroup(byte[] data, NetBitVector receivedChunks) + { + Data = data; + ReceivedChunks = receivedChunks; + } + } +} \ No newline at end of file diff --git a/Lidgren.Network/NetPeer.Send.cs b/Lidgren.Network/NetPeer.Send.cs index c7a3f393..acc29ebe 100644 --- a/Lidgren.Network/NetPeer.Send.cs +++ b/Lidgren.Network/NetPeer.Send.cs @@ -82,7 +82,7 @@ internal static int GetMTU(IList recipients) #endif } - for(int i=0;i m_connections; private readonly Dictionary m_connectionLookup; - private string m_shutdownReason; + private string? m_shutdownReason; /// /// Gets the NetPeerStatus of the NetPeer @@ -65,12 +66,12 @@ public AutoResetEvent MessageReceivedEvent /// /// Returns an UPnP object if enabled in the NetPeerConfiguration /// - public NetUPnP UPnP { get { return m_upnp; } } + public NetUPnP? UPnP { get { return m_upnp; } } /// /// Gets or sets the application defined object containing data about the peer /// - public object Tag + public object? Tag { get { return m_tag; } set { m_tag = value; } @@ -79,7 +80,7 @@ public object Tag /// /// Gets a copy of the list of connections /// - public List Connections + public IReadOnlyList Connections { get { @@ -131,6 +132,12 @@ public NetPeer(NetPeerConfiguration config) { m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0); } + + m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize]; + m_sendBuffer = new byte[m_configuration.SendBufferSize]; + + m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error); + m_readHelperMessage.m_data = m_receiveBuffer; } /// @@ -173,13 +180,12 @@ public void Start() /// /// Get the connection, if any, for a certain remote endpoint /// - public NetConnection GetConnection(NetEndPoint ep) + public NetConnection? GetConnection(NetEndPoint ep) { - NetConnection retval; // this should not pose a threading problem, m_connectionLookup is never added to concurrently // and TryGetValue will not throw an exception on fail, only yield null, which is acceptable - m_connectionLookup.TryGetValue((NetSocketAddress)ep, out retval); + m_connectionLookup.TryGetValue((NetSocketAddress)ep, out NetConnection? retval); return retval; } @@ -187,52 +193,54 @@ public NetConnection GetConnection(NetEndPoint ep) /// /// Read a pending message from any connection, blocking up to maxMillis if needed /// - public NetIncomingMessage WaitMessage(int maxMillis) - { - NetIncomingMessage msg = ReadMessage(); + public NetIncomingMessage? WaitMessage(int maxMillis) + { + NetIncomingMessage? msg = ReadMessage(); - while (msg == null) - { - // This could return true... - if (!MessageReceivedEvent.WaitOne(maxMillis)) - { - return null; - } + while (msg == null) + { + // This could return true... + if (!MessageReceivedEvent.WaitOne(maxMillis)) + { + return null; + } - // ... while this will still returns null. That's why we need to cycle. - msg = ReadMessage(); - } + // ... while this will still returns null. That's why we need to cycle. + msg = ReadMessage(); + } - return msg; - } + return msg; + } /// /// Read a pending message from any connection, if any /// - public NetIncomingMessage ReadMessage() + public NetIncomingMessage? ReadMessage() { - NetIncomingMessage retval; - if (m_releasedIncomingMessages.TryDequeue(out retval)) + if (m_releasedIncomingMessages.TryDequeue(out NetIncomingMessage? retval)) { if (retval.MessageType == NetIncomingMessageType.StatusChanged) { NetConnectionStatus status = (NetConnectionStatus)retval.PeekByte(); + + NetException.Assert(retval.SenderConnection != null); + retval.SenderConnection.m_visibleStatus = status; } } return retval; } - /// - /// Reads a pending message from any connection, if any. - /// Returns true if message was read, otherwise false. - /// - /// True, if message was read. - public bool ReadMessage(out NetIncomingMessage message) - { - message = ReadMessage(); - return message != null; - } + /// + /// Reads a pending message from any connection, if any. + /// Returns true if message was read, otherwise false. + /// + /// True, if message was read. + public bool ReadMessage([MaybeNullWhen(false)] out NetIncomingMessage message) + { + message = ReadMessage(); + return message != null; + } /// /// Read a pending message from any connection, if any @@ -249,6 +257,9 @@ public int ReadMessages(IList addTo) if (nim.MessageType == NetIncomingMessageType.StatusChanged) { NetConnectionStatus status = (NetConnectionStatus)nim.PeekByte(); + + NetException.Assert(nim.SenderConnection != null); + nim.SenderConnection.m_visibleStatus = status; } } @@ -262,9 +273,8 @@ internal void SendLibrary(NetOutgoingMessage msg, NetEndPoint recipient) VerifyNetworkThread(); NetException.Assert(msg.m_isSent == false); - bool connReset; int len = msg.Encode(m_sendBuffer, 0, 0); - SendPacket(len, recipient, 1, out connReset); + SendPacket(len, recipient, 1, out _); // no reliability, no multiple recipients - we can just recycle this message immediately msg.m_recyclingCount = 0; @@ -273,8 +283,8 @@ internal void SendLibrary(NetOutgoingMessage msg, NetEndPoint recipient) private NetEndPoint GetNetEndPoint(string host, int port) { - var family = m_configuration.DualStack ? null : (AddressFamily?) m_configuration.LocalAddress.AddressFamily; - IPAddress address = NetUtility.Resolve(host, family); + var family = m_configuration.DualStack ? null : (AddressFamily?)m_configuration.LocalAddress.AddressFamily; + IPAddress? address = NetUtility.Resolve(host, family); if (address == null) throw new NetException("Could not resolve host"); return new NetEndPoint(address, port); @@ -307,23 +317,22 @@ public NetConnection Connect(NetEndPoint remoteEndPoint) /// /// Create a connection to a remote endpoint /// - public virtual NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage) + public virtual NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage? hailMessage) { if (remoteEndPoint == null) throw new ArgumentNullException("remoteEndPoint"); - if(m_configuration.DualStack) - remoteEndPoint = NetUtility.MapToIPv6(remoteEndPoint); + if (m_configuration.DualStack) + remoteEndPoint = NetUtility.MapToIPv6(remoteEndPoint); lock (m_connections) { if (m_status == NetPeerStatus.NotRunning) throw new NetException("Must call Start() first"); - if (m_connectionLookup.ContainsKey((NetSocketAddress) remoteEndPoint)) + if (m_connectionLookup.ContainsKey((NetSocketAddress)remoteEndPoint)) throw new NetException("Already connected to that endpoint!"); - NetConnection hs; - if (m_handshakes.TryGetValue(remoteEndPoint, out hs)) + if (m_handshakes.TryGetValue(remoteEndPoint, out NetConnection? hs)) { // already trying to connect to that endpoint; make another try switch (hs.m_status) @@ -345,7 +354,7 @@ public virtual NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMess } NetConnection conn = new NetConnection(this, remoteEndPoint); - conn.SetStatus(NetConnectionStatus.InitiatedConnect, "user called connect"); + conn.SetStatus(NetConnectionStatus.InitiatedConnect, "user called connect"); conn.m_localHailMessage = hailMessage; // handle on network thread @@ -365,8 +374,7 @@ public void RawSend(byte[] arr, int offset, int length, NetEndPoint destination) { // wrong thread - this miiiight crash with network thread... but what's a boy to do. Array.Copy(arr, offset, m_sendBuffer, 0, length); - bool unused; - SendPacket(length, destination, 1, out unused); + SendPacket(length, destination, 1, out _); } /// @@ -385,7 +393,7 @@ internal void ThrowOrLog(string message) /// /// Disconnects all active connections and closes the socket /// - public void Shutdown(string bye) + public void Shutdown(string? bye) { // called on user thread if (m_socket == null) diff --git a/Lidgren.Network/NetPeerConfiguration.cs b/Lidgren.Network/NetPeerConfiguration.cs index f339d10a..1a8135f6 100644 --- a/Lidgren.Network/NetPeerConfiguration.cs +++ b/Lidgren.Network/NetPeerConfiguration.cs @@ -534,7 +534,7 @@ public float SimulatedDuplicatesChance /// public NetPeerConfiguration Clone() { - NetPeerConfiguration retval = this.MemberwiseClone() as NetPeerConfiguration; + NetPeerConfiguration retval = (NetPeerConfiguration)this.MemberwiseClone(); retval.m_isLocked = false; return retval; } diff --git a/Lidgren.Network/NetPeerStatistics.cs b/Lidgren.Network/NetPeerStatistics.cs index 2b818a9f..51798ea3 100644 --- a/Lidgren.Network/NetPeerStatistics.cs +++ b/Lidgren.Network/NetPeerStatistics.cs @@ -113,7 +113,14 @@ public int BytesInRecyclePool { get { - lock (m_peer.m_storagePool) + var pool = m_peer.m_storagePool; + + if (pool == null) + { + return 0; + } + + lock (pool) return m_peer.m_storagePoolBytes; } } diff --git a/Lidgren.Network/NetQueue.cs b/Lidgren.Network/NetQueue.cs index a578af65..94ce9734 100644 --- a/Lidgren.Network/NetQueue.cs +++ b/Lidgren.Network/NetQueue.cs @@ -23,6 +23,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; +using System.Diagnostics.CodeAnalysis; // // Comment for Linux Mono users: reports of library thread hangs on EnterReadLock() suggests switching to plain lock() works better @@ -58,7 +59,8 @@ public sealed class NetQueue /// /// Gets the number of items in the queue /// - public int Count { + public int Count + { get { m_lock.EnterReadLock(); @@ -196,7 +198,7 @@ private void SetCapacity(int newCapacity) /// /// Gets an item from the head of the queue, or returns default(T) if empty /// - public bool TryDequeue(out T item) + public bool TryDequeue([MaybeNullWhen(false)] out T item) { if (m_size == 0) { @@ -213,11 +215,11 @@ public bool TryDequeue(out T item) return false; } - item = m_items[m_head]; + item = m_items[m_head]!; if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - m_items[m_head] = default(T); - } + { + m_items[m_head] = default!; + } m_head = (m_head + 1) % m_items.Length; m_size--; @@ -258,7 +260,7 @@ public int TryDrain(IList addTo) if (RuntimeHelpers.IsReferenceOrContainsReferences()) { - m_items[m_head] = default(T); + m_items[m_head] = default!; } m_head = (m_head + 1) % m_items.Length; m_size--; @@ -274,7 +276,7 @@ public int TryDrain(IList addTo) /// /// Returns default(T) if queue is empty /// - public T TryPeek(int offset) + public T? TryPeek(int offset) { if (m_size == 0) return default(T); @@ -310,7 +312,7 @@ public bool Contains(T item) } else { - if (m_items[ptr].Equals(item)) + if (EqualityComparer.Default.Equals(m_items[ptr], item)) return true; } ptr = (ptr + 1) % m_items.Length; @@ -358,7 +360,7 @@ public void Clear() if (RuntimeHelpers.IsReferenceOrContainsReferences()) { for (int i = 0; i < m_items.Length; i++) - m_items[i] = default(T); + m_items[i] = default!; } m_head = 0; m_size = 0; diff --git a/Lidgren.Network/NetRandom.Implementations.cs b/Lidgren.Network/NetRandom.Implementations.cs index 92842a5c..e500dbac 100644 --- a/Lidgren.Network/NetRandom.Implementations.cs +++ b/Lidgren.Network/NetRandom.Implementations.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography; namespace Lidgren.Network @@ -147,9 +148,9 @@ public sealed class MersenneTwisterRandom : NetRandom private const int TEMPER5 = 15; private const int TEMPER6 = 18; - private UInt32[] mt; + private uint[] mt; private int mti; - private UInt32[] mag01; + private uint[] mag01; private const double c_realUnitInt = 1.0 / ((double)int.MaxValue + 1.0); @@ -174,14 +175,17 @@ public MersenneTwisterRandom(uint seed) /// (Re)initialize this instance with provided 32 bit seed /// [CLSCompliant(false)] + [MemberNotNull(nameof(mt))] + [MemberNotNull(nameof(mti))] + [MemberNotNull(nameof(mag01))] public override void Initialize(uint seed) { - mt = new UInt32[N]; + mt = new uint[N]; mti = N + 1; - mag01 = new UInt32[] { 0x0U, MATRIX_A }; + mag01 = new uint[] { 0x0U, MATRIX_A }; mt[0] = seed; for (int i = 1; i < N; i++) - mt[i] = (UInt32)(1812433253 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i); + mt[i] = (uint)(1812433253 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i); } /// @@ -190,7 +194,7 @@ public override void Initialize(uint seed) [CLSCompliant(false)] public override uint NextUInt32() { - UInt32 y; + uint y; if (mti >= N) { GenRandAll(); @@ -207,8 +211,8 @@ public override uint NextUInt32() private void GenRandAll() { int kk = 1; - UInt32 y; - UInt32 p; + uint y; + uint p; y = mt[0] & UPPER_MASK; do { diff --git a/Lidgren.Network/NetRandomSeed.cs b/Lidgren.Network/NetRandomSeed.cs index 9b868fba..375cbc13 100644 --- a/Lidgren.Network/NetRandomSeed.cs +++ b/Lidgren.Network/NetRandomSeed.cs @@ -8,7 +8,7 @@ namespace Lidgren.Network /// public static class NetRandomSeed { - private static int m_seedIncrement = -1640531527; + private static readonly int m_seedIncrement = -1640531527; /// /// Generates a 32 bit random seed diff --git a/Lidgren.Network/NetReliableOrderedReceiver.cs b/Lidgren.Network/NetReliableOrderedReceiver.cs index c82dfcb8..988b528f 100644 --- a/Lidgren.Network/NetReliableOrderedReceiver.cs +++ b/Lidgren.Network/NetReliableOrderedReceiver.cs @@ -5,9 +5,9 @@ namespace Lidgren.Network internal sealed class NetReliableOrderedReceiver : NetReceiverChannelBase { private int m_windowStart; - private int m_windowSize; - private NetBitVector m_earlyReceived; - internal NetIncomingMessage[] m_withheldMessages; + private readonly int m_windowSize; + private readonly NetBitVector m_earlyReceived; + internal NetIncomingMessage?[] m_withheldMessages; public NetReliableOrderedReceiver(NetConnection connection, int windowSize) : base(connection) @@ -47,8 +47,9 @@ internal override void ReceiveMessage(NetIncomingMessage message) while (m_earlyReceived[nextSeqNr % m_windowSize]) { - message = m_withheldMessages[nextSeqNr % m_windowSize]; - NetException.Assert(message != null); + var popMessage = m_withheldMessages[nextSeqNr % m_windowSize]; + NetException.Assert(popMessage != null); + message = popMessage; // remove it from withheld messages m_withheldMessages[nextSeqNr % m_windowSize] = null; diff --git a/Lidgren.Network/NetReliableSenderChannel.cs b/Lidgren.Network/NetReliableSenderChannel.cs index 0a05dd71..74b9b4b7 100644 --- a/Lidgren.Network/NetReliableSenderChannel.cs +++ b/Lidgren.Network/NetReliableSenderChannel.cs @@ -8,14 +8,14 @@ namespace Lidgren.Network /// internal sealed class NetReliableSenderChannel : NetSenderChannelBase { - private NetConnection m_connection; + private readonly NetConnection m_connection; private int m_windowStart; - private int m_windowSize; + private readonly int m_windowSize; private int m_sendStart; private bool m_anyStoredResends; - private NetBitVector m_receivedAcks; + private readonly NetBitVector m_receivedAcks; internal NetStoredReliableMessage[] m_storedMessages; internal double m_resendDelay; @@ -27,7 +27,7 @@ internal override bool NeedToSendMessages() return base.NeedToSendMessages() || m_anyStoredResends; } - internal NetReliableSenderChannel(NetConnection connection, int windowSize) + internal NetReliableSenderChannel(NetConnection connection, int windowSize) : base(new NetQueue(8)) { m_connection = connection; m_windowSize = windowSize; @@ -36,7 +36,6 @@ internal NetReliableSenderChannel(NetConnection connection, int windowSize) m_anyStoredResends = false; m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers); m_storedMessages = new NetStoredReliableMessage[m_windowSize]; - m_queuedSends = new NetQueue(8); m_resendDelay = m_connection.GetResendDelay(); } @@ -77,7 +76,7 @@ internal override void SendQueuedMessages(double now) for (int i = 0; i < m_storedMessages.Length; i++) { var storedMsg = m_storedMessages[i]; - NetOutgoingMessage om = storedMsg.Message; + NetOutgoingMessage? om = storedMsg.Message; if (om == null) continue; @@ -117,8 +116,7 @@ internal override void SendQueuedMessages(double now) // queued sends while (num > 0 && m_queuedSends.Count > 0) { - NetOutgoingMessage om; - if (m_queuedSends.TryDequeue(out om)) + if (m_queuedSends.TryDequeue(out NetOutgoingMessage? om)) ExecuteSend(now, om); num--; NetException.Assert(num == GetAllowedSends()); @@ -166,7 +164,7 @@ private void DestoreMessage(double now, int storeIndex, out bool resetTimeout) #endif // on each destore; reduce recyclingcount so that when all instances are destored, the outgoing message can be recycled Interlocked.Decrement(ref storedMessage.m_recyclingCount); - + if (storedMessage.m_recyclingCount <= 0) m_connection.m_peer.Recycle(storedMessage); @@ -196,9 +194,8 @@ internal override void ReceiveAcknowledge(double now, int seqNr) // ack arrived right on time NetException.Assert(seqNr == m_windowStart); - bool resetTimeout; m_receivedAcks[m_windowStart] = false; - DestoreMessage(now, m_windowStart % m_windowSize, out resetTimeout); + DestoreMessage(now, m_windowStart % m_windowSize, out bool resetTimeout); m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; // advance window if we already have early acks @@ -206,8 +203,7 @@ internal override void ReceiveAcknowledge(double now, int seqNr) { //m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "..."); m_receivedAcks[m_windowStart] = false; - bool rt; - DestoreMessage(now, m_windowStart % m_windowSize, out rt); + DestoreMessage(now, m_windowStart % m_windowSize, out bool rt); resetTimeout |= rt; NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored @@ -267,7 +263,9 @@ internal override void ReceiveAcknowledge(double now, int seqNr) if (m_storedMessages[slot].NumSent == 1) { // just sent once; resend immediately since we found gap in ack sequence - NetOutgoingMessage rmsg = m_storedMessages[slot].Message; + var rmsg = m_storedMessages[slot].Message; + NetException.Assert(rmsg != null); + //m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")"); if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35)) diff --git a/Lidgren.Network/NetReliableSequencedReceiver.cs b/Lidgren.Network/NetReliableSequencedReceiver.cs index 464f52c2..78e3ec6e 100644 --- a/Lidgren.Network/NetReliableSequencedReceiver.cs +++ b/Lidgren.Network/NetReliableSequencedReceiver.cs @@ -5,7 +5,7 @@ namespace Lidgren.Network internal sealed class NetReliableSequencedReceiver : NetReceiverChannelBase { private int m_windowStart; - private int m_windowSize; + private readonly int m_windowSize; public NetReliableSequencedReceiver(NetConnection connection, int windowSize) : base(connection) diff --git a/Lidgren.Network/NetReliableUnorderedReceiver.cs b/Lidgren.Network/NetReliableUnorderedReceiver.cs index 23b864b7..30182d0a 100644 --- a/Lidgren.Network/NetReliableUnorderedReceiver.cs +++ b/Lidgren.Network/NetReliableUnorderedReceiver.cs @@ -5,8 +5,8 @@ namespace Lidgren.Network internal sealed class NetReliableUnorderedReceiver : NetReceiverChannelBase { private int m_windowStart; - private int m_windowSize; - private NetBitVector m_earlyReceived; + private readonly int m_windowSize; + private readonly NetBitVector m_earlyReceived; public NetReliableUnorderedReceiver(NetConnection connection, int windowSize) : base(connection) diff --git a/Lidgren.Network/NetSenderChannelBase.cs b/Lidgren.Network/NetSenderChannelBase.cs index 8bbc7b80..7396d4e2 100644 --- a/Lidgren.Network/NetSenderChannelBase.cs +++ b/Lidgren.Network/NetSenderChannelBase.cs @@ -5,7 +5,12 @@ namespace Lidgren.Network internal abstract class NetSenderChannelBase { // access this directly to queue things in this channel - protected NetQueue m_queuedSends; + protected readonly NetQueue m_queuedSends; + + protected NetSenderChannelBase(NetQueue queuedSends) + { + m_queuedSends = queuedSends; + } internal abstract int WindowSize { get; } diff --git a/Lidgren.Network/NetStoredReliableMessage.cs b/Lidgren.Network/NetStoredReliableMessage.cs index 2f5fb480..3d5b3828 100644 --- a/Lidgren.Network/NetStoredReliableMessage.cs +++ b/Lidgren.Network/NetStoredReliableMessage.cs @@ -6,7 +6,7 @@ internal struct NetStoredReliableMessage { public int NumSent; public double LastSent; - public NetOutgoingMessage Message; + public NetOutgoingMessage? Message; public int SequenceNumber; public void Reset() @@ -16,4 +16,4 @@ public void Reset() Message = null; } } -} +} \ No newline at end of file diff --git a/Lidgren.Network/NetUPnP.cs b/Lidgren.Network/NetUPnP.cs index 9f527781..30b2b58a 100644 --- a/Lidgren.Network/NetUPnP.cs +++ b/Lidgren.Network/NetUPnP.cs @@ -7,6 +7,8 @@ using System.Net.Http; using System.Net.Sockets; using System.Threading; +using System.Linq; + #if !__NOIPENDPOINT__ using NetEndPoint = System.Net.IPEndPoint; @@ -42,16 +44,16 @@ public class NetUPnP { private const int c_discoveryTimeOutMillis = 1000; - private string m_serviceUrl; + private string? m_serviceUrl; private string m_serviceName = ""; - private NetPeer m_peer; - private ManualResetEvent m_discoveryComplete = new ManualResetEvent(false); + private readonly NetPeer m_peer; + private readonly ManualResetEvent m_discoveryComplete = new ManualResetEvent(false); internal double m_discoveryResponseDeadline; private UPnPStatus m_status; - private List m_candidates = new List(); + private readonly List m_candidates = new List(); /// /// Status of the UPnP capabilities of this NetPeer @@ -82,18 +84,24 @@ internal void Discover(NetPeer peer) byte[] arr = System.Text.Encoding.UTF8.GetBytes(str); m_peer.LogDebug("Attempting UPnP discovery"); + + NetException.Assert(peer.Socket != null); + IPAddress? broadcastAddress = NetUtility.GetBroadcastAddress(); + if (broadcastAddress == null) + throw new InvalidOperationException("Unable to determine broadcast address"); + peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); - peer.RawSend(arr, 0, arr.Length, new NetEndPoint(NetUtility.GetBroadcastAddress(), 1900)); + peer.RawSend(arr, 0, arr.Length, new NetEndPoint(broadcastAddress, 1900)); peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false); } - internal void CheckForDiscoveryTimeout() - { - if ((m_status != UPnPStatus.Discovering) || (NetTime.Now < m_discoveryResponseDeadline)) - return; + internal void CheckForDiscoveryTimeout() + { + if ((m_status != UPnPStatus.Discovering) || (NetTime.Now < m_discoveryResponseDeadline)) + return; - if (m_candidates.Count > 0) - { + if (m_candidates.Count > 0) + { var candidate = SelectUpnpCandidate(); m_serviceUrl = candidate.Url; m_serviceName = candidate.ServiceName; @@ -101,49 +109,38 @@ internal void CheckForDiscoveryTimeout() m_peer.LogDebug($"UPnP discovery complete, using {m_serviceUrl}"); m_peer.LogVerbose($"UPnP service name: {m_serviceName}"); m_discoveryComplete.Set(); - } - else - { - m_peer.LogDebug("UPnP discovery timed out"); - m_status = UPnPStatus.NotAvailable; - } - } - - private UpnpCandidate SelectUpnpCandidate() - { - Debug.Assert(m_candidates.Count > 0); - - m_peer.LogVerbose("Selecting UPnP IGD..."); - - var bestScore = 0; - UpnpCandidate? bestCandidate = null; - foreach (var candidate in m_candidates) - { - var score = CandidateScore(candidate); - m_peer.LogVerbose($"Candidate {candidate.Url} has score {score}"); - if (score > bestScore) - { - bestScore = score; - bestCandidate = candidate; - } - } - - // ReSharper disable once PossibleInvalidOperationException - return bestCandidate.Value; - - int CandidateScore(in UpnpCandidate candidate) - { - var status = GetConnectionStatus(candidate.Url, candidate.ServiceName); - if (status != "Connected" && status != "Up") - return 1; - - var externalIp = GetExternalIP(candidate.Url, candidate.ServiceName); - if (externalIp == null || NetReservedAddress.IsAddressReserved(externalIp)) - return 2; - - return 3; - } - } + } + else + { + m_peer.LogDebug("UPnP discovery timed out"); + m_status = UPnPStatus.NotAvailable; + } + } + + private UpnpCandidate SelectUpnpCandidate() + { + m_peer.LogVerbose("Selecting UPnP IGD..."); + + return m_candidates.OrderByDescending(c => + { + var score = CandidateScore(c); + m_peer.LogVerbose($"Candidate {c.Url} has score {score}"); + return score; + }).First(); + + int CandidateScore(in UpnpCandidate candidate) + { + var status = GetConnectionStatus(candidate.Url, candidate.ServiceName); + if (status != "Connected" && status != "Up") + return 1; + + var externalIp = GetExternalIP(candidate.Url, candidate.ServiceName); + if (externalIp == null || NetReservedAddress.IsAddressReserved(externalIp)) + return 2; + + return 3; + } + } internal void ExtractServiceUrl(string resp) { @@ -157,19 +154,27 @@ internal void ExtractServiceUrl(string resp) XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable); nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); - XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); - if (!typen.Value.Contains("InternetGatewayDevice")) + XmlNode? typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); + + if (typen?.Value?.Contains("InternetGatewayDevice") != true) + { + m_peer.LogWarning("Failed to resolve UPnP: couldn't find InternetGatewayDevice deviceType in the response"); return; + } var serviceName = "WANIPConnection"; - XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:" + serviceName + ":1\"]/tns:controlURL/text()", nsMgr); + XmlNode? node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:" + serviceName + ":1\"]/tns:controlURL/text()", nsMgr); if (node == null) { //try another service name serviceName = "WANPPPConnection"; node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:" + serviceName + ":1\"]/tns:controlURL/text()", nsMgr); - if (node == null) - return; + } + + if (node?.Value == null) + { + m_peer.LogWarning("Failed to resolve UPnP: there aren't any supported services (WANIPConnection or WANPPPConnection) in the response"); + return; } var serviceUrl = CombineUrls(resp, node.Value); @@ -214,129 +219,148 @@ private bool CheckAvailability() return false; } return false; - } - - /// - /// Add a forwarding rule to the router using UPnP - /// - /// The external, WAN facing, port - /// A description for the port forwarding rule - /// The port on the client machine to send traffic to (defaults to externalPort) - /// The protocol (defaults to UDP, but can be TCP) - public bool ForwardPort(int externalPort, string description, int internalPort = 0, string proto = "UDP") - { - if (!CheckAvailability()) - return false; - - IPAddress mask; - var client = NetUtility.GetMyAddress(out mask); - if (client == null) - return false; - - if (internalPort == 0) - internalPort = externalPort; - - try - { - SOAPRequest(m_serviceUrl, m_serviceName, - "" + - "" + - "" + externalPort.ToString() + "" + - "" + proto + "" + - "" + internalPort.ToString() + "" + - "" + client.ToString() + "" + - "1" + - "" + description + "" + - "0" + - "", - "AddPortMapping"); - - m_peer.LogDebug($"Sent UPnP port forward request. Ext port: {externalPort} int port: {internalPort} desc: {description} proto: {proto}"); - NetUtility.Sleep(50); - } - catch (Exception ex) - { - m_peer.LogWarning("UPnP port forward failed: " + ex.Message); - return false; - } - return true; - } - - /// - /// Delete a forwarding rule from the router using UPnP - /// - /// The external, 'internet facing', port - /// The protocol (defaults to UDP, but can be TCP) - public bool DeleteForwardingRule(int externalPort, string proto = "UDP") - { - if (!CheckAvailability()) - return false; - - try - { - SOAPRequest(m_serviceUrl, m_serviceName, - "" + - "" + - "" + - "" + externalPort + "" + - "" + proto + "" + - "", "DeletePortMapping"); - return true; - } - catch (Exception ex) - { - m_peer.LogWarning("UPnP delete forwarding rule failed: " + ex.Message); - return false; - } - } - - /// - /// Retrieve the extern ip using UPnP - /// - public IPAddress GetExternalIP() + } + + /// + /// Add a forwarding rule to the router using UPnP + /// + /// The external, WAN facing, port + /// A description for the port forwarding rule + /// The port on the client machine to send traffic to (defaults to externalPort) + /// The protocol (defaults to UDP, but can be TCP) + public bool ForwardPort(int externalPort, string description, int internalPort = 0, string proto = "UDP") + { + if (!CheckAvailability()) + return false; + var client = NetUtility.GetMyAddress(out _); + if (client == null) + return false; + + if (internalPort == 0) + internalPort = externalPort; + + NetException.Assert(m_serviceUrl != null); + + try + { + SOAPRequest(m_serviceUrl, m_serviceName, + "" + + "" + + "" + externalPort.ToString() + "" + + "" + proto + "" + + "" + internalPort.ToString() + "" + + "" + client.ToString() + "" + + "1" + + "" + description + "" + + "0" + + "", + "AddPortMapping"); + + m_peer.LogDebug($"Sent UPnP port forward request. Ext port: {externalPort} int port: {internalPort} desc: {description} proto: {proto}"); + NetUtility.Sleep(50); + } + catch (Exception ex) + { + m_peer.LogWarning("UPnP port forward failed: " + ex.Message); + return false; + } + return true; + } + + /// + /// Delete a forwarding rule from the router using UPnP + /// + /// The external, 'internet facing', port + /// The protocol (defaults to UDP, but can be TCP) + public bool DeleteForwardingRule(int externalPort, string proto = "UDP") + { + if (!CheckAvailability()) + return false; + + NetException.Assert(m_serviceUrl != null); + + try + { + SOAPRequest(m_serviceUrl, m_serviceName, + "" + + "" + + "" + + "" + externalPort + "" + + "" + proto + "" + + "", "DeletePortMapping"); + return true; + } + catch (Exception ex) + { + m_peer.LogWarning("UPnP delete forwarding rule failed: " + ex.Message); + return false; + } + } + + /// + /// Retrieve the extern ip using UPnP + /// + public IPAddress? GetExternalIP() { if (!CheckAvailability()) return null; + NetException.Assert(m_serviceUrl != null); + return GetExternalIP(m_serviceUrl, m_serviceName); } - private IPAddress GetExternalIP(string url, string name) - { - try - { - XmlDocument xdoc = SOAPRequest(url, name, "" + - "", "GetExternalIPAddress"); - XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); - nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); - string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value; - return IPAddress.Parse(IP); - } - catch (Exception ex) - { - m_peer.LogWarning("Failed to get external IP: " + ex.Message); - return null; - } - } - - private string GetConnectionStatus(string url, string name) - { - try - { - XmlDocument xdoc = SOAPRequest( - url, name, - $"", - "GetStatusInfo"); - XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); - nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); - return xdoc.SelectSingleNode("//NewConnectionStatus/text()", nsMgr).Value; - } - catch (Exception ex) - { - m_peer.LogWarning("Failed to get connection status: " + ex.Message); - return null; - } - } + private IPAddress? GetExternalIP(string url, string name) + { + try + { + XmlDocument xdoc = SOAPRequest(url, name, "" + + "", "GetExternalIPAddress"); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); + nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); + string? IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr)?.Value; + + if (IP == null) + { + m_peer.LogWarning("Failed to read external IP from the UPnP response"); + return null; + } + + return IPAddress.Parse(IP); + } + catch (Exception ex) + { + m_peer.LogWarning("Failed to get external IP: " + ex.Message); + return null; + } + } + + private string? GetConnectionStatus(string url, string name) + { + try + { + XmlDocument xdoc = SOAPRequest( + url, name, + $"", + "GetStatusInfo"); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); + nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); + + string? status = xdoc.SelectSingleNode("//NewConnectionStatus/text()", nsMgr)?.Value; + + if (status == null) + { + m_peer.LogWarning("Failed to read connection status from the UPnP response"); + } + + return status; + } + catch (Exception ex) + { + m_peer.LogWarning("Failed to get connection status: " + ex.Message); + return null; + } + } private XmlDocument SOAPRequest(string url, string serviceName, string soap, string function) { @@ -346,10 +370,11 @@ private XmlDocument SOAPRequest(string url, string serviceName, string soap, str soap + "" + ""; - - var request = new HttpRequestMessage(HttpMethod.Post, url); + + using var request = new HttpRequestMessage(HttpMethod.Post, url); request.Headers.Add("SOAPACTION", $"\"urn:schemas-upnp-org:service:{serviceName}:1#{function}\""); - request.Content = new StringContent(req, null, "text/xml"); + using var content = new StringContent(req, null, "text/xml"); + request.Content = content; using var httpClient = new HttpClient(); return SyncSend(httpClient, request); @@ -364,7 +389,7 @@ private struct UpnpCandidate private static XmlDocument SyncSend(HttpClient httpClient, HttpRequestMessage request) { var xmlDoc = new XmlDocument(); - + #if NET5_0_OR_GREATER using var response = httpClient.Send(request); response.EnsureSuccessStatusCode(); @@ -377,7 +402,7 @@ private static XmlDocument SyncSend(HttpClient httpClient, HttpRequestMessage re xmlDoc.Load(await response.Content.ReadAsStreamAsync()); }).Wait(); #endif - + return xmlDoc; } } diff --git a/Lidgren.Network/NetUnreliableSenderChannel.cs b/Lidgren.Network/NetUnreliableSenderChannel.cs index b857ca64..2c23f7f5 100644 --- a/Lidgren.Network/NetUnreliableSenderChannel.cs +++ b/Lidgren.Network/NetUnreliableSenderChannel.cs @@ -8,24 +8,23 @@ namespace Lidgren.Network /// internal sealed class NetUnreliableSenderChannel : NetSenderChannelBase { - private NetConnection m_connection; + private readonly NetConnection m_connection; private int m_windowStart; - private int m_windowSize; + private readonly int m_windowSize; private int m_sendStart; - private bool m_doFlowControl; + private readonly bool m_doFlowControl; - private NetBitVector m_receivedAcks; + private readonly NetBitVector m_receivedAcks; internal override int WindowSize { get { return m_windowSize; } } - internal NetUnreliableSenderChannel(NetConnection connection, int windowSize, NetDeliveryMethod method) + internal NetUnreliableSenderChannel(NetConnection connection, int windowSize, NetDeliveryMethod method) : base(new NetQueue(8)) { m_connection = connection; m_windowSize = windowSize; m_windowStart = 0; m_sendStart = 0; m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers); - m_queuedSends = new NetQueue(8); m_doFlowControl = true; if (method == NetDeliveryMethod.Unreliable && connection.Peer.Configuration.SuppressUnreliableUnorderedAcks == true) @@ -60,14 +59,14 @@ internal override NetSendResult Enqueue(NetOutgoingMessage message) } if (message.LengthBits >= ushort.MaxValue - && m_connection.m_peerConfiguration.UnreliableSizeBehaviour == NetUnreliableSizeBehaviour.IgnoreMTU) + && m_connection.m_peerConfiguration.UnreliableSizeBehaviour == NetUnreliableSizeBehaviour.IgnoreMTU) { - // drop message - this.m_connection.m_peer.LogError( - string.Format("Unreliable message max size exceeded: {0} bits (max {1})", - message.LengthBits, - ushort.MaxValue)); - return NetSendResult.Dropped; + // drop message + this.m_connection.m_peer.LogError( + string.Format("Unreliable message max size exceeded: {0} bits (max {1})", + message.LengthBits, + ushort.MaxValue)); + return NetSendResult.Dropped; } m_queuedSends.Enqueue(message); @@ -85,8 +84,7 @@ internal override void SendQueuedMessages(double now) // queued sends while (num > 0 && m_queuedSends.Count > 0) { - NetOutgoingMessage om; - if (m_queuedSends.TryDequeue(out om)) + if (m_queuedSends.TryDequeue(out NetOutgoingMessage? om)) ExecuteSend(om); num--; } @@ -106,7 +104,7 @@ private void ExecuteSend(NetOutgoingMessage message) return; } - + // remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received internal override void ReceiveAcknowledge(double now, int seqNr) diff --git a/Lidgren.Network/NetUnreliableUnorderedReceiver.cs b/Lidgren.Network/NetUnreliableUnorderedReceiver.cs index 87578a2c..295fef7a 100644 --- a/Lidgren.Network/NetUnreliableUnorderedReceiver.cs +++ b/Lidgren.Network/NetUnreliableUnorderedReceiver.cs @@ -4,7 +4,7 @@ namespace Lidgren.Network { internal sealed class NetUnreliableUnorderedReceiver : NetReceiverChannelBase { - private bool m_doFlowControl; + private readonly bool m_doFlowControl; public NetUnreliableUnorderedReceiver(NetConnection connection) : base(connection) diff --git a/Lidgren.Network/NetUtility.Dns.cs b/Lidgren.Network/NetUtility.Dns.cs index ca0f4e2d..49380ab5 100644 --- a/Lidgren.Network/NetUtility.Dns.cs +++ b/Lidgren.Network/NetUtility.Dns.cs @@ -7,272 +7,263 @@ using System.Net; using System.Net.Sockets; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; namespace Lidgren.Network { - public static partial class NetUtility - { - /// - /// Resolve endpoint callback - /// - public delegate void ResolveEndPointCallback(NetEndPoint endPoint); - - /// - /// Resolve address callback - /// - public delegate void ResolveAddressCallback(NetAddress adr); - - /// - /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) - /// - public static void ResolveAsync(string ipOrHost, int port, ResolveEndPointCallback callback) - { - ResolveAsync(ipOrHost, port, null, callback); - } - - public static void ResolveAsync(string ipOrHost, int port, AddressFamily? allowedFamily, - ResolveEndPointCallback callback) - { - ResolveAsync(ipOrHost, allowedFamily, delegate(NetAddress adr) - { - if (adr == null) - { - callback(null); - } - else - { - callback(new NetEndPoint(adr, port)); - } - }); - } - - /// - /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname - /// - public static NetEndPoint Resolve(string ipOrHost, int port) - { - return Resolve(ipOrHost, port, null); - } - - public static NetEndPoint Resolve(string ipOrHost, int port, AddressFamily? allowedFamily) - { - var adr = Resolve(ipOrHost, allowedFamily); - return adr == null ? null : new NetEndPoint(adr, port); - } - - /// - /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) - /// - public static void ResolveAsync(string ipOrHost, ResolveAddressCallback callback) - { - ResolveAsync(ipOrHost, null, callback); - } - - /// - /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) - /// - public static void ResolveAsync(string ipOrHost, AddressFamily? allowedFamily, ResolveAddressCallback callback) - { - if (ResolveHead(ref ipOrHost, allowedFamily, out var resolve)) - { - callback(resolve); - return; - } - - // ok must be a host name - IPHostEntry entry; - try - { - Dns.BeginGetHostEntry(ipOrHost, delegate(IAsyncResult result) - { - try - { - entry = Dns.EndGetHostEntry(result); - } - catch (SocketException ex) - { - if (ex.SocketErrorCode == SocketError.HostNotFound) - { - //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); - callback(null); - return; - } - else - { - throw; - } - } - - if (entry == null) - { - callback(null); - return; - } - - // check each entry for a valid IP address - ResolveFilter(allowedFamily, entry.AddressList); - }, null); - } - catch (SocketException ex) - { - if (ex.SocketErrorCode == SocketError.HostNotFound) - { - //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); - callback(null); - } - else - { - throw; - } - } - } - - public static async Task ResolveAsync(string ipOrHost, int port) - { - return await ResolveAsync(ipOrHost, port, (AddressFamily?) null); - } - - public static async Task ResolveAsync(string ipOrHost, int port, AddressFamily? allowedFamily) - { - var adr = await ResolveAsync(ipOrHost, allowedFamily); - return adr == null ? null : new NetEndPoint(adr, port); - } - - public static async Task ResolveAsync(string ipOrHost, AddressFamily? allowedFamily = null) - { - if (ResolveHead(ref ipOrHost, allowedFamily, out var resolve)) - { - return resolve; - } - - // ok must be a host name - try - { - var addresses = await Dns.GetHostAddressesAsync(ipOrHost); - if (addresses == null) - return null; - return ResolveFilter(allowedFamily, addresses); - } - catch (SocketException ex) - { - if (ex.SocketErrorCode == SocketError.HostNotFound) - { - //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); - return null; - } - else - { - throw; - } - } - - } - - /// - /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname - /// - public static NetAddress Resolve(string ipOrHost) - { - return Resolve(ipOrHost, null); - } - - - /// - /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname, - /// taking in an allowed address family to filter resolved addresses by. - /// - /// - /// If is not null, the address returned will only be of the specified family. - /// - /// The hostname or IP address to parse. - /// If not null, the allowed address family to return. - /// - /// A resolved address matching the specified filter if it exists, - /// null if no such address exists or a lookup error occured. - /// - /// - /// is null or empty OR - /// is not one of null, - /// or - /// - public static NetAddress Resolve(string ipOrHost, AddressFamily? allowedFamily) - { - if (ResolveHead(ref ipOrHost, allowedFamily, out var resolve)) - { - return resolve; - } - - // ok must be a host name - try - { - var addresses = Dns.GetHostAddresses(ipOrHost); - if (addresses == null) - return null; - return ResolveFilter(allowedFamily, addresses); - } - catch (SocketException ex) - { - if (ex.SocketErrorCode == SocketError.HostNotFound) - { - //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); - return null; - } - else - { - throw; - } - } - } - - private static IPAddress ResolveFilter(AddressFamily? allowedFamily, IPAddress[] addresses) - { - foreach (var address in addresses) - { - var family = address.AddressFamily; - if (family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6) - continue; - if (allowedFamily == null || allowedFamily == family) - return address; - } - - return null; - } - - private static bool ResolveHead(ref string ipOrHost, AddressFamily? allowedFamily, out IPAddress resolve) - { - if (string.IsNullOrEmpty(ipOrHost)) - throw new ArgumentException("Supplied string must not be empty", "ipOrHost"); - - if (allowedFamily != null && allowedFamily != AddressFamily.InterNetwork - && allowedFamily != AddressFamily.InterNetworkV6) - { - throw new ArgumentException("Address family must be either InterNetwork, InterNetworkV6 or null", - nameof(allowedFamily)); - } - - ipOrHost = ipOrHost.Trim(); - - NetAddress ipAddress = null; - if (NetAddress.TryParse(ipOrHost, out ipAddress)) - { - if (allowedFamily != null && ipAddress.AddressFamily != allowedFamily) - { - resolve = null; - return true; - } - - if (ipAddress.AddressFamily == AddressFamily.InterNetwork || - ipAddress.AddressFamily == AddressFamily.InterNetworkV6) - { - resolve = ipAddress; - return true; - } - - throw new ArgumentException("This method will not currently resolve other than IPv4 or IPv6 addresses"); - } - - resolve = null; - return false; - } - } + public static partial class NetUtility + { + /// + /// Resolve endpoint callback + /// + public delegate void ResolveEndPointCallback(NetEndPoint? endPoint); + + /// + /// Resolve address callback + /// + public delegate void ResolveAddressCallback(NetAddress? adr); + + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) + /// + public static void ResolveAsync(string ipOrHost, int port, ResolveEndPointCallback callback) + { + ResolveAsync(ipOrHost, port, null, callback); + } + + public static void ResolveAsync(string ipOrHost, int port, AddressFamily? allowedFamily, + ResolveEndPointCallback callback) + { + ResolveAsync(ipOrHost, allowedFamily, adr => + { + if (adr == null) + { + callback(null); + } + else + { + callback(new NetEndPoint(adr, port)); + } + }); + } + + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname + /// + public static NetEndPoint? Resolve(string ipOrHost, int port) + { + return Resolve(ipOrHost, port, null); + } + + public static NetEndPoint? Resolve(string ipOrHost, int port, AddressFamily? allowedFamily) + { + var adr = Resolve(ipOrHost, allowedFamily); + return adr == null ? null : new NetEndPoint(adr, port); + } + + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) + /// + public static void ResolveAsync(string ipOrHost, ResolveAddressCallback callback) + { + ResolveAsync(ipOrHost, null, callback); + } + + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname (asynchronous version) + /// + public static void ResolveAsync(string ipOrHost, AddressFamily? allowedFamily, ResolveAddressCallback callback) + { + if (ResolveHead(ref ipOrHost, allowedFamily, out var resolve)) + { + callback(resolve); + return; + } + + // ok must be a host name + IPHostEntry entry; + try + { + Dns.BeginGetHostEntry(ipOrHost, delegate (IAsyncResult result) + { + try + { + entry = Dns.EndGetHostEntry(result); + } + catch (SocketException ex) + { + if (ex.SocketErrorCode == SocketError.HostNotFound) + { + //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); + callback(null); + return; + } + else + { + throw; + } + } + + if (entry == null) + { + callback(null); + return; + } + + // check each entry for a valid IP address + ResolveFilter(allowedFamily, entry.AddressList); + }, null); + } + catch (SocketException ex) + { + if (ex.SocketErrorCode == SocketError.HostNotFound) + { + //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); + callback(null); + } + else + { + throw; + } + } + } + + public static async Task ResolveAsync(string ipOrHost, int port) + { + return await ResolveAsync(ipOrHost, port, (AddressFamily?)null); + } + + public static async Task ResolveAsync(string ipOrHost, int port, AddressFamily? allowedFamily) + { + var adr = await ResolveAsync(ipOrHost, allowedFamily); + return adr == null ? null : new NetEndPoint(adr, port); + } + + public static async Task ResolveAsync(string ipOrHost, AddressFamily? allowedFamily = null) + { + if (ResolveHead(ref ipOrHost, allowedFamily, out var resolve)) + { + return resolve; + } + + // ok must be a host name + try + { + var addresses = await Dns.GetHostAddressesAsync(ipOrHost); + + return ResolveFilter(allowedFamily, addresses); + } + catch (SocketException ex) + { + if (ex.SocketErrorCode == SocketError.HostNotFound) + { + //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); + return null; + } + else + { + throw; + } + } + + } + + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname + /// + public static NetAddress? Resolve(string ipOrHost) + { + return Resolve(ipOrHost, null); + } + + + /// + /// Get IPv4 or IPv6 address from notation (xxx.xxx.xxx.xxx or xxxx:xxxx:...:xxxx) or hostname, + /// taking in an allowed address family to filter resolved addresses by. + /// + /// + /// If is not null, the address returned will only be of the specified family. + /// + /// The hostname or IP address to parse. + /// If not null, the allowed address family to return. + /// + /// A resolved address matching the specified filter if it exists, + /// null if no such address exists or a lookup error occured. + /// + /// + /// is null or empty OR + /// is not one of null, + /// or + /// + public static NetAddress? Resolve(string ipOrHost, AddressFamily? allowedFamily) + { + if (ResolveHead(ref ipOrHost, allowedFamily, out var resolve)) + { + return resolve; + } + + // ok must be a host name + try + { + var addresses = Dns.GetHostAddresses(ipOrHost); + + return ResolveFilter(allowedFamily, addresses); + } + catch (SocketException ex) when (ex.SocketErrorCode == SocketError.HostNotFound) + { + //LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost)); + return null; + } + } + + private static IPAddress? ResolveFilter(AddressFamily? allowedFamily, IPAddress[] addresses) + { + foreach (var address in addresses) + { + var family = address.AddressFamily; + if (family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6) + continue; + if (allowedFamily == null || allowedFamily == family) + return address; + } + + return null; + } + + private static bool ResolveHead(ref string ipOrHost, AddressFamily? allowedFamily, out IPAddress? resolve) + { + if (string.IsNullOrEmpty(ipOrHost)) + throw new ArgumentException("Supplied string must not be empty", "ipOrHost"); + + if (allowedFamily != null && allowedFamily != AddressFamily.InterNetwork + && allowedFamily != AddressFamily.InterNetworkV6) + { + throw new ArgumentException("Address family must be either InterNetwork, InterNetworkV6 or null", + nameof(allowedFamily)); + } + + ipOrHost = ipOrHost.Trim(); + + if (NetAddress.TryParse(ipOrHost, out NetAddress? ipAddress)) + { + if (allowedFamily != null && ipAddress.AddressFamily != allowedFamily) + { + resolve = null; + return true; + } + + if (ipAddress.AddressFamily == AddressFamily.InterNetwork || + ipAddress.AddressFamily == AddressFamily.InterNetworkV6) + { + resolve = ipAddress; + return true; + } + + throw new ArgumentException("This method will not currently resolve other than IPv4 or IPv6 addresses"); + } + + resolve = null; + return false; + } + } } \ No newline at end of file diff --git a/Lidgren.Network/NetUtility.cs b/Lidgren.Network/NetUtility.cs index 5e417957..e1ebb40c 100644 --- a/Lidgren.Network/NetUtility.cs +++ b/Lidgren.Network/NetUtility.cs @@ -43,8 +43,8 @@ public static partial class NetUtility { private static readonly bool IsMono = Type.GetType("Mono.Runtime") != null; - private static IPAddress s_broadcastAddress; - public static IPAddress GetCachedBroadcastAddress() + private static IPAddress? s_broadcastAddress; + public static IPAddress? GetCachedBroadcastAddress() { if (s_broadcastAddress == null) s_broadcastAddress = GetBroadcastAddress(); @@ -71,7 +71,7 @@ public static string ToHexString(byte[] data, int offset, int length) { return ToHexString(data.AsSpan(offset, length)); } - + /// /// Create a hex string from an array of bytes /// @@ -104,11 +104,12 @@ public static bool IsLocal(NetEndPoint endPoint) /// public static bool IsLocal(NetAddress remote) { - NetAddress mask; - var local = GetMyAddress(out mask); + var local = GetMyAddress(out NetAddress? mask); - if (mask == null) - return false; + if (local == null) + throw new InvalidOperationException("Unable to determine local address"); + + NetException.Assert(mask != null); uint maskBits = BitConverter.ToUInt32(mask.GetAddressBytes(), 0); uint remoteBits = BitConverter.ToUInt32(remote.GetAddressBytes(), 0); @@ -171,7 +172,7 @@ internal static bool CompareElements(byte[] one, byte[] two) /// /// Convert a hexadecimal string to a byte array /// - public static byte[] ToByteArray(String hexString) + public static byte[] ToByteArray(string hexString) { byte[] retval = new byte[hexString.Length / 2]; for (int i = 0; i < hexString.Length; i += 2) @@ -267,25 +268,24 @@ internal static void SortMembersList(System.Reflection.MemberInfo[] list) internal static NetDeliveryMethod GetDeliveryMethod(NetMessageType mtp) { - if (mtp >= NetMessageType.UserReliableOrdered1) - return NetDeliveryMethod.ReliableOrdered; - else if (mtp >= NetMessageType.UserReliableSequenced1) - return NetDeliveryMethod.ReliableSequenced; - else if (mtp >= NetMessageType.UserReliableUnordered) - return NetDeliveryMethod.ReliableUnordered; - else if (mtp >= NetMessageType.UserSequenced1) - return NetDeliveryMethod.UnreliableSequenced; - return NetDeliveryMethod.Unreliable; + return mtp switch + { + >= NetMessageType.UserReliableOrdered1 => NetDeliveryMethod.ReliableOrdered, + >= NetMessageType.UserReliableSequenced1 => NetDeliveryMethod.ReliableSequenced, + >= NetMessageType.UserReliableUnordered => NetDeliveryMethod.ReliableUnordered, + >= NetMessageType.UserSequenced1 => NetDeliveryMethod.UnreliableSequenced, + _ => NetDeliveryMethod.Unreliable, + }; } /// /// Creates a comma delimited string from a lite of items /// - public static string MakeCommaDelimitedList(IList list) + public static string MakeCommaDelimitedList(IReadOnlyList list) where T : notnull { var cnt = list.Count; StringBuilder bdr = new StringBuilder(cnt * 5); // educated guess - for(int i=0;i - /// Copies from to . Maps to an IPv6 address - /// - /// Source. - /// Destination. - internal static void CopyEndpoint(IPEndPoint src, IPEndPoint dst) - { - dst.Port = src.Port; - if (src.AddressFamily == AddressFamily.InterNetwork) - dst.Address = src.Address.MapToIPv6(); - else - dst.Address = src.Address; - } - - /// - /// Maps the IPEndPoint object to an IPv6 address. Has allocation - /// - internal static IPEndPoint MapToIPv6(IPEndPoint endPoint) - { - if (endPoint.AddressFamily == AddressFamily.InterNetwork) - return new IPEndPoint(endPoint.Address.MapToIPv6(), endPoint.Port); - return endPoint; - } - - // MemoryMarshal.Read and MemoryMarshal.Write are not GUARANTEED to allow unaligned reads/writes. - // The current CoreCLR implementation does but that's an implementation detail. - // These are basically MemoryMarshal.Read/Write but well, guaranteed to allow unaligned access. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static T ReadUnaligned(ReadOnlySpan source) where T : unmanaged - { - if (Unsafe.SizeOf() > source.Length) - { - throw new ArgumentOutOfRangeException(); - } - - return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(source)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void WriteUnaligned(Span destination, ref T value) where T : unmanaged - { - if ((uint)Unsafe.SizeOf() > (uint)destination.Length) - { - throw new ArgumentOutOfRangeException(); - } - - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); - } - } + /// + /// Copies from to . Maps to an IPv6 address + /// + /// Source. + /// Destination. + internal static void CopyEndpoint(IPEndPoint src, IPEndPoint dst) + { + dst.Port = src.Port; + if (src.AddressFamily == AddressFamily.InterNetwork) + dst.Address = src.Address.MapToIPv6(); + else + dst.Address = src.Address; + } + + /// + /// Maps the IPEndPoint object to an IPv6 address. Has allocation + /// + internal static IPEndPoint MapToIPv6(IPEndPoint endPoint) + { + if (endPoint.AddressFamily == AddressFamily.InterNetwork) + return new IPEndPoint(endPoint.Address.MapToIPv6(), endPoint.Port); + return endPoint; + } + + // MemoryMarshal.Read and MemoryMarshal.Write are not GUARANTEED to allow unaligned reads/writes. + // The current CoreCLR implementation does but that's an implementation detail. + // These are basically MemoryMarshal.Read/Write but well, guaranteed to allow unaligned access. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static T ReadUnaligned(ReadOnlySpan source) where T : unmanaged + { + if (Unsafe.SizeOf() > source.Length) + { + throw new ArgumentOutOfRangeException(); + } + + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(source)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void WriteUnaligned(Span destination, ref T value) where T : unmanaged + { + if ((uint)Unsafe.SizeOf() > (uint)destination.Length) + { + throw new ArgumentOutOfRangeException(); + } + + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(destination), value); + } + } } diff --git a/Lidgren.Network/Platform/PlatformWin32.cs b/Lidgren.Network/Platform/PlatformWin32.cs index 4d8f6602..80e36858 100644 --- a/Lidgren.Network/Platform/PlatformWin32.cs +++ b/Lidgren.Network/Platform/PlatformWin32.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Net.NetworkInformation; @@ -24,7 +25,7 @@ public static ulong GetPlatformSeed(int seedInc) public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } } - private static NetworkInterface GetNetworkInterface() + private static NetworkInterface? GetNetworkInterface() { var defaultAddress = ProbeDefaultRouteAddress(); @@ -60,7 +61,7 @@ private static NetworkInterface GetNetworkInterface() .FirstOrDefault(); } - private static IPAddress ProbeDefaultRouteAddress() + private static IPAddress? ProbeDefaultRouteAddress() { try { @@ -71,7 +72,7 @@ private static IPAddress ProbeDefaultRouteAddress() // This basically gets us the network interface address "that goes to the router". using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.Connect(new IPAddress(new byte[] { 1, 1, 1, 1 }), 12345); - return ((IPEndPoint)socket.LocalEndPoint).Address; + return ((IPEndPoint)socket.LocalEndPoint!).Address; } catch { @@ -83,7 +84,7 @@ private static IPAddress ProbeDefaultRouteAddress() /// /// If available, returns the bytes of the physical (MAC) address for the first usable network interface /// - public static byte[] GetMacAddressBytes() + public static byte[]? GetMacAddressBytes() { var ni = GetNetworkInterface(); if (ni == null) @@ -91,7 +92,7 @@ public static byte[] GetMacAddressBytes() return ni.GetPhysicalAddress().GetAddressBytes(); } - public static IPAddress GetBroadcastAddress() + public static IPAddress? GetBroadcastAddress() { var ni = GetNetworkInterface(); if (ni == null) @@ -123,7 +124,7 @@ public static IPAddress GetBroadcastAddress() /// /// Gets my local IPv4 address (not necessarily external) and subnet mask /// - public static IPAddress GetMyAddress(out IPAddress mask) + public static IPAddress? GetMyAddress(out IPAddress? mask) { var ni = GetNetworkInterface(); if (ni == null) diff --git a/Lidgren.Network/Sockets/NetSocketAddress.cs b/Lidgren.Network/Sockets/NetSocketAddress.cs index 9d54ff9f..601b060a 100644 --- a/Lidgren.Network/Sockets/NetSocketAddress.cs +++ b/Lidgren.Network/Sockets/NetSocketAddress.cs @@ -151,7 +151,7 @@ public bool Equals(NetSocketAddressV4 other) return Port == other.Port && Address.Equals(other.Address); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is NetSocketAddressV4 other && Equals(other); } @@ -196,7 +196,7 @@ public bool Equals(NetSocketAddressV6 other) return Port == other.Port && Address.Equals(other.Address) && ScopeId == other.ScopeId; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is NetSocketAddressV6 other && Equals(other); } diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 14f90974..b78c8338 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -2,6 +2,10 @@ ## Master +- All APIs now use Nullable Reference Types. +- `NetQueue.Contains(T)` now uses `EqualityComparer.Default` instead of direct `object.Equals()`. +- `NetException.Assert()` now specifies `[DoesNotReturnIf(false)]`. + ## 0.2.6 - Fixed a possible `NullReferenceException` in `NetReliableSenderChannel`. diff --git a/UnitTests/NetQueueTests.cs b/UnitTests/NetQueueTests.cs index d1e6df38..5d647dce 100644 --- a/UnitTests/NetQueueTests.cs +++ b/UnitTests/NetQueueTests.cs @@ -56,11 +56,11 @@ public static void Run() if (ok == false || a != 3) throw new Exception("NetQueue failed"); - ok = queue.TryDequeue(out a); + ok = queue.TryDequeue(out _); if (ok == true) throw new Exception("NetQueue failed"); - ok = queue.TryDequeue(out a); + ok = queue.TryDequeue(out _); if (ok == true) throw new Exception("NetQueue failed"); diff --git a/UnitTests/NetUtilityTests.cs b/UnitTests/NetUtilityTests.cs index 43304d75..a9d6e71e 100644 --- a/UnitTests/NetUtilityTests.cs +++ b/UnitTests/NetUtilityTests.cs @@ -63,7 +63,7 @@ public void TestResolveAllowed(AddressFamily family) var addr = NetUtility.Resolve("example.com", family); - Assert.That(addr.AddressFamily, Is.EqualTo(family)); + Assert.That(addr?.AddressFamily, Is.EqualTo(family)); } [Test] @@ -100,7 +100,7 @@ public async Task TestResolveAsyncAllowed(AddressFamily family) var addr = await NetUtility.ResolveAsync("example.com", family); - Assert.That(addr.AddressFamily, Is.EqualTo(family)); + Assert.That(addr?.AddressFamily, Is.EqualTo(family)); } [Test] diff --git a/UnitTests/PeerTestBase.cs b/UnitTests/PeerTestBase.cs index 43bcf10c..b93aacdd 100644 --- a/UnitTests/PeerTestBase.cs +++ b/UnitTests/PeerTestBase.cs @@ -6,75 +6,74 @@ namespace UnitTests { - public abstract class PeerTestBase - { - protected NetPeerConfiguration Config; - protected NetPeer Peer; + public abstract class PeerTestBase + { + protected NetPeerConfiguration Config; + protected NetPeer Peer; - [SetUp] - public void Setup() - { - Config = new NetPeerConfiguration(GetType().Name) - { - EnableUPnP = true - }; - Peer = new NetPeer(Config); - Peer.LogEvent += PeerOnLogEvent; - Peer.Start(); + [SetUp] + public void Setup() + { + Config = new NetPeerConfiguration(GetType().Name) + { + EnableUPnP = true + }; + Peer = new NetPeer(Config); + Peer.LogEvent += PeerOnLogEvent; + Peer.Start(); - TestContext.Out.WriteLine("Unique identifier is " + NetUtility.ToHexString(Peer.UniqueIdentifier)); - } + TestContext.Out.WriteLine("Unique identifier is " + NetUtility.ToHexString(Peer.UniqueIdentifier)); + } - private static void PeerOnLogEvent(NetIncomingMessageType type, string text) - { - TestContext.Out.WriteLine($"{type}: {text}"); - } + private static void PeerOnLogEvent(NetIncomingMessageType type, string text) + { + TestContext.Out.WriteLine($"{type}: {text}"); + } - [TearDown] - public void TearDown() - { - Peer.Shutdown("Unit test teardown."); - } + [TearDown] + public void TearDown() + { + Peer.Shutdown("Unit test teardown."); + } - protected static NetIncomingMessage CreateIncomingMessage(byte[] fromData, int bitLength) - { - var inc = (NetIncomingMessage) Activator.CreateInstance(typeof(NetIncomingMessage), true); - typeof(NetIncomingMessage).GetField("m_data", BindingFlags.NonPublic | BindingFlags.Instance) - .SetValue(inc, fromData); - typeof(NetIncomingMessage).GetField("m_bitLength", BindingFlags.NonPublic | BindingFlags.Instance) - .SetValue(inc, bitLength); - return inc; - } + protected static NetIncomingMessage CreateIncomingMessage(byte[] fromData, int bitLength) + { + return new NetIncomingMessage + { + m_data = fromData, + m_bitLength = bitLength + }; + } - protected static string ToBinaryString(ulong value, int bits, bool includeSpaces) - { - var numSpaces = Math.Max(0, bits / 8 - 1); - if (includeSpaces == false) - { - numSpaces = 0; - } + protected static string ToBinaryString(ulong value, int bits, bool includeSpaces) + { + var numSpaces = Math.Max(0, bits / 8 - 1); + if (includeSpaces == false) + { + numSpaces = 0; + } - var bdr = new StringBuilder(bits + numSpaces); - for (var i = 0; i < bits + numSpaces; i++) - { - bdr.Append(' '); - } + var bdr = new StringBuilder(bits + numSpaces); + for (var i = 0; i < bits + numSpaces; i++) + { + bdr.Append(' '); + } - for (var i = 0; i < bits; i++) - { - var shifted = value >> i; - var isSet = (shifted & 1) != 0; + for (var i = 0; i < bits; i++) + { + var shifted = value >> i; + var isSet = (shifted & 1) != 0; - var pos = bits - 1 - i; - if (includeSpaces) - { - pos += Math.Max(0, pos / 8); - } + var pos = bits - 1 - i; + if (includeSpaces) + { + pos += Math.Max(0, pos / 8); + } - bdr[pos] = isSet ? '1' : '0'; - } + bdr[pos] = isSet ? '1' : '0'; + } - return bdr.ToString(); - } - } + return bdr.ToString(); + } + } } \ No newline at end of file diff --git a/UnitTests/ReadWriteTests.cs b/UnitTests/ReadWriteTests.cs index d9dd61e2..37b05144 100644 --- a/UnitTests/ReadWriteTests.cs +++ b/UnitTests/ReadWriteTests.cs @@ -239,7 +239,7 @@ public class TestItemBase public class TestItem : TestItemBase { public float Age; - public string Name; + public string? Name; } } } diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 1a598126..9bb1cf32 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -3,17 +3,22 @@ netcoreapp3.1;net5.0;net6.0;net7.0 false + enable + 11.0 false - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all