private void HandleReleasedFragment(NetIncomingMessage im)
        {
            VerifyNetworkThread();

            //
            // 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
                );

            NetException.Assert(im.LengthBytes > ptr);

            NetException.Assert(group > 0);
            NetException.Assert(totalBits > 0);
            NetException.Assert(chunkByteSize > 0);

            int totalBytes     = NetUtility.BytesToHoldBits((int)totalBits);
            int totalNumChunks = totalBytes / chunkByteSize;

            if (totalNumChunks * chunkByteSize < totalBytes)
            {
                totalNumChunks++;
            }

            NetException.Assert(chunkNumber < totalNumChunks);

            if (chunkNumber >= totalNumChunks)
            {
                LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
                return;
            }

            Dictionary <int, ReceivedFragmentGroup> groups;

            if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups))
            {
                groups = new Dictionary <int, ReceivedFragmentGroup>();
                m_receivedFragmentGroups[im.SenderConnection] = groups;
            }

            ReceivedFragmentGroup info;

            if (!groups.TryGetValue(group, out info))
            {
                info                = new ReceivedFragmentGroup();
                info.Data           = new byte[totalBytes];
                info.ReceivedChunks = new NetBitVector(totalNumChunks);
                groups[group]       = info;
            }

            info.ReceivedChunks[chunkNumber] = true;
            //info.LastReceived = (float)NetTime.Now;

            // copy to data
            int offset = (chunkNumber * chunkByteSize);

            Buffer.BlockCopy(im.m_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("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");

            if (info.ReceivedChunks.Count() == totalNumChunks)
            {
                // Done! Transform this incoming message
                im.m_data       = info.Data;
                im.m_bitLength  = (int)totalBits;
                im.m_isFragment = false;

                LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");
                groups.Remove(group);

                ReleaseMessage(im);
            }
            else
            {
                // data has been copied; recycle this incoming message
                Recycle(im);
            }

            return;
        }
		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
			);

			NetException.Assert(im.LengthBytes > ptr);

			NetException.Assert(group > 0);
			NetException.Assert(totalBits > 0);
			NetException.Assert(chunkByteSize > 0);
			
			int totalBytes = NetUtility.BytesToHoldBits((int)totalBits);
			int totalNumChunks = totalBytes / chunkByteSize;
			if (totalNumChunks * chunkByteSize < totalBytes)
				totalNumChunks++;

			NetException.Assert(chunkNumber < totalNumChunks);

			if (chunkNumber >= totalNumChunks)
			{
				LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
				return;
			}

			Dictionary<int, ReceivedFragmentGroup> groups;
			if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups))
			{
				groups = new Dictionary<int, ReceivedFragmentGroup>();
				m_receivedFragmentGroups[im.SenderConnection] = groups;
			}

			ReceivedFragmentGroup info;
			if (!groups.TryGetValue(group, out info))
			{
				info = new ReceivedFragmentGroup();
				info.Data = new byte[totalBytes];
				info.ReceivedChunks = new NetBitVector(totalNumChunks);
				groups[group] = info;
			}

			info.ReceivedChunks[chunkNumber] = true;
			info.LastReceived = (float)NetTime.Now;

			// copy to data
			int offset = (chunkNumber * chunkByteSize);
			Buffer.BlockCopy(im.m_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("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");

			if (info.ReceivedChunks.Count() == totalNumChunks)
			{
				// Done! Transform this incoming message
				im.m_data = info.Data;
				im.m_bitLength = (int)totalBits;
				im.m_isFragment = false;

				LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");

				ReleaseMessage(im);
			}
			else
			{
				// data has been copied; recycle this incoming message
				Recycle(im);
			}

			return;
		}