public void CopyByteArray(ref BitInputStream input) { var count = (int)input.ReadUIntPacked(); WriteUIntPacked((uint)count); if (count > 0) { Align(); input.Align(); input.ReadBytes(m_Buffer, m_CurrentByteIdx, count); m_CurrentByteIdx += count; } }
public void BitStream_Align() { var random = new Random(1293); var numbers = new int[1024]; var buffer = new byte[1024 * 64]; for (int runs = 0; runs < 1000; ++runs) { for (int i = 0; i < 1024; ++i) { numbers[i] = random.Next(1, 33); } var output = new BitOutputStream(buffer); for (int i = 0; i < 1024; ++i) { output.WriteBits((uint)numbers[i], numbers[i]); if (i % 3 == 0) { output.Align(); } } var input = new BitInputStream(buffer); for (int i = 0; i < 1024; ++i) { var value = input.ReadBits(numbers[i]); Assert.AreEqual((uint)numbers[i], value); if (i % 3 == 0) { input.Align(); } } } }
// Returns the 'wide' packageSequenceNumber (i.e. 32 bit reconstructed from the 16bits sent over wire) protected int ProcessPackageHeader(byte[] packageData, int packageSize, out NetworkMessage content, out byte[] assembledData, out int assembledSize, out int headerSize) { counters.packagesIn++; assembledData = packageData; assembledSize = packageSize; headerSize = 0; var input = new BitInputStream(packageData); int headerStartInBits = input.GetBitPosition(); content = (NetworkMessage)input.ReadBits(8); // TODO: Possible improvement is to ack on individual fragments not just entire message if ((content & NetworkMessage.FRAGMENT) != 0) { // Package fragment var fragmentPackageSequence = Sequence.FromUInt16((ushort)input.ReadBits(16), inSequence); var numFragments = (int)input.ReadBits(8); var fragmentIndex = (int)input.ReadBits(8); var fragmentSize = (int)input.ReadBits(16); FragmentReassemblyInfo assembly; if (!m_FragmentReassembly.TryGetValue(fragmentPackageSequence, out assembly)) { // If we run out of room in the reassembly buffer we will not be able to reassemble this package if (!m_FragmentReassembly.Available(fragmentPackageSequence)) { counters.fragmentedPackagesLostIn++; } GameDebug.Assert(numFragments <= NetworkConfig.maxFragments); assembly = m_FragmentReassembly.Acquire(fragmentPackageSequence); assembly.numFragments = numFragments; assembly.receivedMask = 0; assembly.receivedCount = 0; } GameDebug.Assert(assembly.numFragments == numFragments); GameDebug.Assert(fragmentIndex < assembly.numFragments); counters.headerBitsIn += input.GetBitPosition() - headerStartInBits; if ((assembly.receivedMask & (1U << fragmentIndex)) != 0) { // Duplicate package fragment counters.packagesDuplicateIn++; return(0); } assembly.receivedMask |= 1U << fragmentIndex; assembly.receivedCount++; input.ReadBytes(assembly.data, fragmentIndex * NetworkConfig.packageFragmentSize, fragmentSize); if (assembly.receivedCount < assembly.numFragments) { return(0); // Not fully assembled } // Continue processing package as we have now reassembled the package assembledData = assembly.data; assembledSize = fragmentIndex * NetworkConfig.packageFragmentSize + fragmentSize; input.Initialize(assembledData); headerStartInBits = 0; content = (NetworkMessage)input.ReadBits(8); } var inSequenceNew = Sequence.FromUInt16((ushort)input.ReadBits(16), inSequence); var outSequenceAckNew = Sequence.FromUInt16((ushort)input.ReadBits(16), outSequenceAck); var outSequenceAckMaskNew = (ushort)input.ReadBits(16); if (inSequenceNew > inSequence) { // If we have a hole in the package sequence that will fall off the ack mask that // means the package (inSequenceNew-15 and before) will be considered lost (either it will never come or we will // reject it as being stale if we get it at a later point in time) var distance = inSequenceNew - inSequence; for (var i = 0; i < Math.Min(distance, 15); ++i) // TODO : Fix this contant { if ((inSequenceAckMask & 1 << (15 - i)) == 0) { counters.packagesLostIn++; } } // If there is a really big hole then those packages are considered lost as well // Update the incoming ack mask. if (distance > 15) { counters.packagesLostIn += distance - 15; inSequenceAckMask = 1; // all is lost except current package } else { inSequenceAckMask <<= distance; inSequenceAckMask |= 1; } inSequence = inSequenceNew; inSequenceTime = NetworkUtils.stopwatch.ElapsedMilliseconds; } else if (inSequenceNew < inSequence) { // Package is out of order // Check if the package is stale // NOTE : We rely on the fact that we will reject packages that we cannot ack due to the size // of the ack mask, so we don't have to worry about resending messages as long as we do that // after the original package has fallen off the ack mask. var distance = inSequence - inSequenceNew; if (distance > 15) // TODO : Fix this constant { counters.packagesStaleIn++; return(0); } // Check if the package is a duplicate var ackBit = 1 << distance; if ((ackBit & inSequenceAckMask) != 0) { // Duplicate package counters.packagesDuplicateIn++; return(0); } // Accept the package out of order counters.packagesOutOfOrderIn++; inSequenceAckMask |= (ushort)ackBit; } else { // Duplicate package counters.packagesDuplicateIn++; return(0); } if (inSequenceNew % 3 == 0) { var timeOnServer = (ushort)input.ReadBits(8); TPackageInfo info; if (outstandingPackages.TryGetValue(outSequenceAckNew, out info)) { var now = NetworkUtils.stopwatch.ElapsedMilliseconds; rtt = (int)(now - info.sentTime - timeOnServer); } } // If the ack sequence is not higher we have nothing new to do if (outSequenceAckNew <= outSequenceAck) { headerSize = input.Align(); return(inSequenceNew); } // Find the sequence numbers that we have to consider lost var seqsBeforeThisAlreadyNotifedAsLost = outSequenceAck - 15; var seqsBeforeThisAreLost = outSequenceAckNew - 15; for (int sequence = seqsBeforeThisAlreadyNotifedAsLost; sequence <= seqsBeforeThisAreLost; ++sequence) { // Handle conditions before first 15 packets if (sequence < 0) { continue; } // If seqence covered by old ack mask, we may already have received it (and notified) int bitnum = outSequenceAck - sequence; var ackBit = bitnum >= 0 ? 1 << bitnum : 0; var notNotified = (ackBit & outSequenceAckMask) == 0; if (outstandingPackages.Exists(sequence) && notNotified) { var info = outstandingPackages[sequence]; NotifyDelivered(sequence, info, false); counters.packagesLostOut++; if (info.fragmented) { counters.fragmentedPackagesLostOut++; } info.Reset(); outstandingPackages.Remove(sequence); } } outSequenceAck = outSequenceAckNew; outSequenceAckMask = outSequenceAckMaskNew; // Ack packages if they haven't been acked already for (var sequence = Math.Max(outSequenceAck - 15, 0); sequence <= outSequenceAck; ++sequence) { var ackBit = 1 << outSequenceAck - sequence; if (outstandingPackages.Exists(sequence) && (ackBit & outSequenceAckMask) != 0) { var info = outstandingPackages[sequence]; NotifyDelivered(sequence, info, true); info.Reset(); outstandingPackages.Remove(sequence); } } counters.headerBitsIn += input.GetBitPosition() - headerStartInBits; headerSize = input.Align(); return(inSequenceNew); }
public int ProcessPackageHeader(byte[] packageData, out NetworkMessage content, out int headerSize) { counters.packagesIn++; var input = new BitInputStream(packageData); headerSize = 0; int headerStartInBits = input.GetBitPosition(); content = (NetworkMessage)input.ReadBits(8); var inSequenceNew = Sequence.FromUInt16((ushort)input.ReadBits(16), inSequence); var outSequenceAckNew = Sequence.FromUInt16((ushort)input.ReadBits(16), outSequenceAck); var outSequenceAckMaskNew = (ushort)input.ReadBits(16); if (inSequenceNew > inSequence) { // If we have a hole in the package sequence that will fall off the ack mask that // means the package (inSequenceNew-15 and before) will be considered lost (either it will never come or we will // reject it as being stale if we get it at a later point in time) var distance = inSequenceNew - inSequence; for (var i = 0; i < Math.Min(distance, 15); ++i) // TODO : Fix this contant { if ((inSequenceAckMask & 1 << (15 - i)) == 0) { counters.packagesLostIn++; } } // If there is a really big hole then those packages are considered lost as well // Update the incoming ack mask. if (distance > 15) { counters.packagesLostIn += distance - 15; inSequenceAckMask = 1; // all is lost except current package } else { inSequenceAckMask <<= distance; inSequenceAckMask |= 1; } inSequence = inSequenceNew; inSequenceTime = NetworkUtils.stopwatch.ElapsedMilliseconds; } else if (inSequenceNew < inSequence) { // Package is out of order // Check if the package is stale var distance = inSequence - inSequenceNew; if (distance > 15) // TODO : Fix this constant { counters.packagesStaleIn++; return(0); } // Check if the package is a duplicate var ackBit = 1 << distance; if ((ackBit & inSequenceAckMask) != 0) { // Duplicate package counters.packagesDuplicateIn++; return(0); } // Accept the package out of order //counters.packagesOutOfOrderIn++; inSequenceAckMask |= (ushort)ackBit; } else { // Duplicate package counters.packagesDuplicateIn++; return(0); } if (inSequenceNew % 3 == 0) { var timeOnServer = (ushort)input.ReadBits(8); TPackageInfo info; if (outstandingPackages.TryGetValue(outSequenceAckNew, out info)) { var now = NetworkUtils.stopwatch.ElapsedMilliseconds; rtt = (int)(now - info.SentTime - timeOnServer); } } // If the ack sequence is not higher we have nothing new to do if (outSequenceAckNew <= outSequenceAck) { headerSize = input.Align(); return(inSequenceNew); } // Find the sequence numbers that we have to consider lost var seqsBeforeThisAlreadyNotifedAsLost = outSequenceAck - 15; var seqsBeforeThisAreLost = outSequenceAckNew - 15; for (int sequence = seqsBeforeThisAlreadyNotifedAsLost; sequence <= seqsBeforeThisAreLost; ++sequence) { // Handle conditions before first 15 packets if (sequence < 0) { continue; } // If seqence covered by old ack mask, we may already have received it (and notified) int bitnum = outSequenceAck - sequence; var ackBit = bitnum >= 0 ? 1 << bitnum : 0; var notNotified = (ackBit & outSequenceAckMask) == 0; if (outstandingPackages.Exists(sequence) && notNotified) { var info = outstandingPackages[sequence]; NotifyDelivered(sequence, info, false); counters.packagesLostOut++; info.Reset(); outstandingPackages.Remove(sequence); } } outSequenceAck = outSequenceAckNew; outSequenceAckMask = outSequenceAckMaskNew; // Ack packages if they haven't been acked already for (var sequence = Math.Max(outSequenceAck - 15, 0); sequence <= outSequenceAck; ++sequence) { var ackBit = 1 << outSequenceAck - sequence; if (outstandingPackages.Exists(sequence) && (ackBit & outSequenceAckMask) != 0) { var info = outstandingPackages[sequence]; NotifyDelivered(sequence, info, true); info.Reset(); outstandingPackages.Remove(sequence); } } headerSize = input.Align(); return(inSequenceNew); }