public void OnReceiveData(byte[] data, IPEndPoint iPEndPoint)
    {
        MemoryStream  memoryStream  = new MemoryStream(data);
        PacketWithCrc packetWithCrc = new PacketWithCrc();

        packetWithCrc.Deserialize(memoryStream);
        memoryStream.Close();

        if (crc32.IsDataCorrupted(packetWithCrc.data, packetWithCrc.crc))
        {
            UnityEngine.Debug.LogError("Received corrupted data from " + iPEndPoint);
            return;
        }
        memoryStream = new MemoryStream(packetWithCrc.data);
        PacketHeader packetHeader = new PacketHeader();

        packetHeader.Deserialize(memoryStream);

        if ((PacketType)packetHeader.packetTypeID == PacketType.User)
        {
            UserPacketHeader userPacketHeader = new UserPacketHeader();
            userPacketHeader.Deserialize(memoryStream);

            ReliablePacketHeader reliablePacketHeader = new ReliablePacketHeader();
            reliablePacketHeader.Deserialize(memoryStream);
            ProcessReliablePacketReceived(reliablePacketHeader);

            InvokeCallback(userPacketHeader, memoryStream);
        }
        else
        {
            NetworkManager.Instance.OnReceivePacket(iPEndPoint, (PacketType)packetHeader.packetTypeID, memoryStream);
        }
        memoryStream.Close();
    }
    public byte[] SerializePacket <T>(NetworkPacket <T> packetToSerialize, bool isReliable = false, uint objectID = 0)
    {
        MemoryStream memoryStream = new MemoryStream();
        PacketHeader packetHeader = new PacketHeader();

        packetHeader.packetTypeID = (uint)packetToSerialize.type;
        packetHeader.Serialize(memoryStream);

        if (packetToSerialize.type == PacketType.User)
        {
            UserPacketHeader userPacketHeader = new UserPacketHeader();

            userPacketHeader.packetTypeID = packetToSerialize.userPacketTypeID;
            userPacketHeader.packetID     = localSequence;
            userPacketHeader.senderID     = NetworkManager.Instance.clientID;
            userPacketHeader.objectID     = objectID;
            userPacketHeader.isReliable   = isReliable;

            userPacketHeader.Serialize(memoryStream);


            ReliablePacketHeader reliablePacketHeader = new ReliablePacketHeader();

            reliablePacketHeader.sequence = localSequence;
            reliablePacketHeader.ack      = remoteSequence;

            //UnityEngine.Debug.Log("Setting up ackbits for packet Nro." + localSequence);
            ackBitfields = 0;
            for (int i = 0; i <= numberOfBitsInAck; i++)
            {
                if (sequenceNumbersReceived.Contains((uint)(remoteSequence - i)))
                {
                    //UnityEngine.Debug.Log("Sequence number is contained: " + (int)(expectedSequence - i));
                    ackBitfields |= 1 << i;
                }
            }
            //UnityEngine.Debug.Log("Ackbits to send: " + Convert.ToString(ackBitfields, 2));
            reliablePacketHeader.ackBitfield = ackBitfields;

            reliablePacketHeader.Serialize(memoryStream);
        }
        packetToSerialize.Serialize(memoryStream);

        PacketWithCrc packetWithCrc = new PacketWithCrc();

        packetWithCrc.crc        = crc32.CalculateCrc(memoryStream.ToArray());
        packetWithCrc.byteLength = memoryStream.ToArray().Length;
        packetWithCrc.data       = memoryStream.ToArray();
        memoryStream.Close();

        memoryStream = new MemoryStream();
        packetWithCrc.Serialize(memoryStream);
        memoryStream.Close();

        if (isReliable)
        {
            if (!packetsPendingToBeAcked.ContainsKey(localSequence))
            {
                packetsPendingToBeAcked.Add(localSequence, memoryStream.ToArray());
            }
        }

        return(memoryStream.ToArray());
    }