void CSteamNetworkConnectionBase::SNP_ReceiveUnreliableSegment( int64 nMsgNum, int nOffset, const void *pSegmentData, int cbSegmentSize, bool bLastSegmentInMessage, SteamNetworkingMicroseconds usecNow )
{
	SpewDebugGroup( m_connectionConfig.m_LogLevel_PacketDecode.Get(), "[%s] RX msg %lld offset %d+%d=%d %02x ... %02x\n", GetDescription(), nMsgNum, nOffset, cbSegmentSize, nOffset+cbSegmentSize, ((byte*)pSegmentData)[0], ((byte*)pSegmentData)[cbSegmentSize-1] );

	// Ignore data segments when we are not going to process them (e.g. linger)
	if ( GetState() != k_ESteamNetworkingConnectionState_Connected )
	{
		SpewDebugGroup( m_connectionConfig.m_LogLevel_PacketDecode.Get(), "[%s] discarding msg %lld [%d,%d) as connection is in state %d\n",
			GetDescription(),
			nMsgNum,
			nOffset, nOffset+cbSegmentSize,
			(int)GetState() );
		return;
	}

	// Check for a common special case: non-fragmented message.
	if ( nOffset == 0 && bLastSegmentInMessage )
	{

		// Deliver it immediately, don't go through the fragmentation assembly process below.
		// (Although that would work.)
		ReceivedMessage( pSegmentData, cbSegmentSize, nMsgNum, k_nSteamNetworkingSend_Unreliable, usecNow );
		return;
	}

	// Limit number of unreliable segments we store.  We just use a fixed
	// limit, rather than trying to be smart by expiring based on time or whatever.
	if ( len( m_receiverState.m_mapUnreliableSegments ) > k_nMaxBufferedUnreliableSegments )
	{
		auto itDelete = m_receiverState.m_mapUnreliableSegments.begin();

		// If we're going to delete some, go ahead and delete all of them for this
		// message.
		int64 nDeleteMsgNum = itDelete->first.m_nMsgNum;
		do {
			itDelete = m_receiverState.m_mapUnreliableSegments.erase( itDelete );
		} while ( itDelete != m_receiverState.m_mapUnreliableSegments.end() && itDelete->first.m_nMsgNum == nDeleteMsgNum );

		// Warn if the message we are receiving is older (or the same) than the one
		// we are deleting.  If sender is legit, then it probably means that we have
		// something tuned badly.
		if ( nDeleteMsgNum >= nMsgNum )
		{
			// Spew, but rate limit in case of malicious sender
			SpewWarningRateLimited( usecNow, "[%s] SNP expiring unreliable segments for msg %lld, while receiving unreliable segments for msg %lld\n",
				GetDescription(), (long long)nDeleteMsgNum, (long long)nMsgNum );
		}
	}

	// Message fragment.  Find/insert the entry in our reassembly queue
	// I really hate this syntax and interface.
	SSNPRecvUnreliableSegmentKey key;
	key.m_nMsgNum = nMsgNum;
	key.m_nOffset = nOffset;
	SSNPRecvUnreliableSegmentData &data = m_receiverState.m_mapUnreliableSegments[ key ];
	if ( data.m_cbSegSize >= 0 )
	{
		// We got another segment starting at the same offset.  This is weird, since they shouldn't
		// be doing.  But remember that we're working on top of UDP, which could deliver packets
		// multiple times.  We'll spew about it, just in case it indicates a bug in this code or the sender.
		SpewWarningRateLimited( usecNow, "[%s] Received unreliable msg %lld segment offset %d twice.  Sizes %d,%d, last=%d,%d\n",
			GetDescription(), nMsgNum, nOffset, data.m_cbSegSize, cbSegmentSize, (int)data.m_bLast, (int)bLastSegmentInMessage );

		// Just drop the segment.  Note that the sender might have sent a longer segment from the previous
		// one, in which case this segment contains new data, and is not therefore redundant.  That seems
		// "legal", but very weird, and not worth handling.  If senders do retransmit unreliable segments
		// (perhaps FEC?) then they need to retransmit the exact same segments.
		return;
	}

	// Segment in the map either just got inserted, or is a subset of the segment
	// we just received.  Replace it.
	data.m_cbSegSize = cbSegmentSize;
	Assert( !data.m_bLast );
	data.m_bLast = bLastSegmentInMessage;
	memcpy( data.m_buf, pSegmentData, cbSegmentSize );

	// Now check if that completed the message
	key.m_nOffset = 0;
	auto itMsgStart = m_receiverState.m_mapUnreliableSegments.lower_bound( key );
	auto end = m_receiverState.m_mapUnreliableSegments.end();
	Assert( itMsgStart != end );
	auto itMsgLast = itMsgStart;
	int cbMessageSize = 0;
	for (;;)
	{
		// Is this the thing we expected?
		if ( itMsgLast->first.m_nMsgNum != nMsgNum || itMsgLast->first.m_nOffset > cbMessageSize )
			return; // We've got a gap.

		// Update.  This code looks more complicated than strictly necessary, but it works
		// if we have overlapping segments.
		cbMessageSize = std::max( cbMessageSize, itMsgLast->first.m_nOffset + itMsgLast->second.m_cbSegSize );

		// Is that the end?
		if ( itMsgLast->second.m_bLast )
			break;

		// Still looking for the end
		++itMsgLast;
		if ( itMsgLast == end )
			return;
	}

	CSteamNetworkingMessage *pMsg = CSteamNetworkingMessage::New( this, cbMessageSize, nMsgNum, k_nSteamNetworkingSend_Unreliable, usecNow );
	if ( !pMsg )
		return;

	// OK, we have the complete message!  Gather the
	// segments into a contiguous buffer
	for (;;)
	{
		Assert( itMsgStart->first.m_nMsgNum == nMsgNum );
		memcpy( (char *)pMsg->m_pData + itMsgStart->first.m_nOffset, itMsgStart->second.m_buf, itMsgStart->second.m_cbSegSize );

		// Done?
		if ( itMsgStart->second.m_bLast )
			break;

		// Remove entry from list, and move onto the next entry
		itMsgStart = m_receiverState.m_mapUnreliableSegments.erase( itMsgStart );
	}

	// Erase the last segment, and anything else we might have hanging around
	// for this message (???)
	do {
		itMsgStart = m_receiverState.m_mapUnreliableSegments.erase( itMsgStart );
	} while ( itMsgStart != end && itMsgStart->first.m_nMsgNum == nMsgNum );

	// Deliver the message.
	ReceivedMessage( pMsg );
}
void CSteamNetworkConnectionBase::SNP_ReceiveUnreliableSegment( int64 nMsgNum, int nOffset, const void *pSegmentData, int cbSegmentSize, bool bLastSegmentInMessage, SteamNetworkingMicroseconds usecNow )
{
	SpewDebugGroup( m_connectionConfig.m_LogLevel_PacketDecode.Get(), "[%s] RX msg %lld offset %d+%d=%d %02x ... %02x\n", GetDescription(), nMsgNum, nOffset, cbSegmentSize, nOffset+cbSegmentSize, ((byte*)pSegmentData)[0], ((byte*)pSegmentData)[cbSegmentSize-1] );

	 
	if ( GetState() != k_ESteamNetworkingConnectionState_Connected )
	{
		SpewDebugGroup( m_connectionConfig.m_LogLevel_PacketDecode.Get(), "[%s] discarding msg %lld [%d,%d) as connection is in state %d\n",
			GetDescription(),
			nMsgNum,
			nOffset, nOffset+cbSegmentSize,
			(int)GetState() );
		return;
	}

	 
	if ( nOffset == 0 && bLastSegmentInMessage )
	{

		 
		 
		ReceivedMessage( pSegmentData, cbSegmentSize, nMsgNum, k_nSteamNetworkingSend_Unreliable, usecNow );
		return;
	}

	 
	 
	if ( len( m_receiverState.m_mapUnreliableSegments ) > k_nMaxBufferedUnreliableSegments )
	{
		auto itDelete = m_receiverState.m_mapUnreliableSegments.begin();

		 
		 
		int64 nDeleteMsgNum = itDelete->first.m_nMsgNum;
		do {
			itDelete = m_receiverState.m_mapUnreliableSegments.erase( itDelete );
		} while ( itDelete != m_receiverState.m_mapUnreliableSegments.end() && itDelete->first.m_nMsgNum == nDeleteMsgNum );

		 
		 
		 
		if ( nDeleteMsgNum >= nMsgNum )
		{
			 
			SpewWarningRateLimited( usecNow, "[%s] SNP expiring unreliable segments for msg %lld, while receiving unreliable segments for msg %lld\n",
				GetDescription(), (long long)nDeleteMsgNum, (long long)nMsgNum );
		}
	}

	 
	 
	SSNPRecvUnreliableSegmentKey key;
	key.m_nMsgNum = nMsgNum;
	key.m_nOffset = nOffset;
	SSNPRecvUnreliableSegmentData &data = m_receiverState.m_mapUnreliableSegments[ key ];
	if ( data.m_cbSegSize >= 0 )
	{
		 
		 
		 
		SpewWarningRateLimited( usecNow, "[%s] Received unreliable msg %lld segment offset %d twice.  Sizes %d,%d, last=%d,%d\n",
			GetDescription(), nMsgNum, nOffset, data.m_cbSegSize, cbSegmentSize, (int)data.m_bLast, (int)bLastSegmentInMessage );

		 
		 
		 
		 
		return;
	}

	 
	 
	data.m_cbSegSize = cbSegmentSize;
	Assert( !data.m_bLast );
	data.m_bLast = bLastSegmentInMessage;
	memcpy( data.m_buf, pSegmentData, cbSegmentSize );

	 
	key.m_nOffset = 0;
	auto itMsgStart = m_receiverState.m_mapUnreliableSegments.lower_bound( key );
	auto end = m_receiverState.m_mapUnreliableSegments.end();
	Assert( itMsgStart != end );
	auto itMsgLast = itMsgStart;
	int cbMessageSize = 0;
	for (;;)
	{
		 
		if ( itMsgLast->first.m_nMsgNum != nMsgNum || itMsgLast->first.m_nOffset > cbMessageSize )
			return;  

		 
		 
		cbMessageSize = std::max( cbMessageSize, itMsgLast->first.m_nOffset + itMsgLast->second.m_cbSegSize );

		 
		if ( itMsgLast->second.m_bLast )
			break;

		 
		++itMsgLast;
		if ( itMsgLast == end )
			return;
	}

	CSteamNetworkingMessage *pMsg = CSteamNetworkingMessage::New( this, cbMessageSize, nMsgNum, k_nSteamNetworkingSend_Unreliable, usecNow );
	if ( !pMsg )
		return;

	 
	 
	for (;;)
	{
		Assert( itMsgStart->first.m_nMsgNum == nMsgNum );
		memcpy( (char *)pMsg->m_pData + itMsgStart->first.m_nOffset, itMsgStart->second.m_buf, itMsgStart->second.m_cbSegSize );

		 
		if ( itMsgStart->second.m_bLast )
			break;

		 
		itMsgStart = m_receiverState.m_mapUnreliableSegments.erase( itMsgStart );
	}

	 
	 
	do {
		itMsgStart = m_receiverState.m_mapUnreliableSegments.erase( itMsgStart );
	} while ( itMsgStart != end && itMsgStart->first.m_nMsgNum == nMsgNum );

	 
	ReceivedMessage( pMsg );
}