void ReadClientInfo <TInputStream>(ref TInputStream input) where TInputStream : NetworkCompression.IInputStream
        {
            var newClientId = (int)input.ReadRawBits(8);

            serverTickRate = (int)input.ReadRawBits(8); // TODO (petera) remove this from here. This should only be handshake code. tickrate updated by other means like configvar
            uint serverProtocol = input.ReadRawBits(8);

            int modelSize = (int)input.ReadRawBits(16);

            if (modelSize > modelData.Length)
            {
                modelData = new byte[modelSize];
            }
            for (int i = 0; i < modelSize; i++)
            {
                modelData[i] = (byte)input.ReadRawBits(8);
            }

            if (connectionState == ConnectionState.Connected)
            {
                GameDebug.Log("Ignoring ClientInfo as we are already connected");
                return;
            }

            uint ourProtocol = NetworkConfig.protocolVersion;

            GameDebug.Log("Client protocol id: " + ourProtocol);
            GameDebug.Log("Server protocol id: " + serverProtocol);
            if (ourProtocol != serverProtocol)
            {
                if (clientVerifyProtocol.IntValue > 0)
                {
                    GameDebug.LogError("Protocol mismatch. Server is: " + serverProtocol + " and we are: " + ourProtocol);
                    connectionState = ConnectionState.Disconnected;
                    return;
                }
                GameDebug.Log("Ignoring protocol difference client.verifyprotocol is 0");
            }

            GameDebug.Assert(clientId == -1 || newClientId == clientId, "Repeated client info didn't match existing client id");

            compressionModel = new NetworkCompressionModel(modelData);

            clientId = newClientId;

            connectionState = ConnectionState.Connected;

            if (clientDebug.IntValue > 0)
            {
                GameDebug.Log(string.Format("ReadClientInfo: clientId {0}  serverTickRate {1}", newClientId, serverTickRate));
            }
        }
        public void ReadPackage <TInputStream>(byte[] packageData, int packageSize, NetworkCompressionModel compressionModel, INetworkClientCallbacks networkClientConsumer, ISnapshotConsumer snapshotConsumer) where TInputStream : struct, NetworkCompression.IInputStream
        {
            counters.bytesIn += packageSize;

            NetworkMessage content;

            byte[] assembledData;
            int    assembledSize;
            int    headerSize;
            var    packageSequence = ProcessPackageHeader(packageData, packageSize, out content, out assembledData, out assembledSize, out headerSize);

            // Reset stats
            counters.ClearSectionStats();
            counters.AddSectionStats("header", headerSize * 8, Color.white);

            // The package was dropped (duplicate or too old) or if it was a fragment not yet assembled, bail out here
            if (packageSequence == 0)
            {
                return;
            }

            var input = default(TInputStream); //  new TInputStream(); due to bug new generates garbage here

            input.Initialize(compressionModel, assembledData, headerSize);

            if ((content & NetworkMessage.ClientInfo) != 0)
            {
                ReadClientInfo(ref input);
            }
            counters.AddSectionStats("clientInfo", input.GetBitPosition2(), Color.green);

            if ((content & NetworkMessage.MapInfo) != 0)
            {
                ReadMapInfo(ref input);
            }
            counters.AddSectionStats("mapInfo", input.GetBitPosition2(), new Color(0.65f, 0.16f, 0.16f));

            // The package was received out of order but older than the last map reset,
            // so we ignore the remaining data
            if (mapInfo.ackSequence == 0 || packageSequence < mapInfo.ackSequence)
            {
                return;
            }

            if ((content & NetworkMessage.Snapshot) != 0)
            {
                ReadSnapshot(packageSequence, ref input, snapshotConsumer);

                // Make sure the callback actually picked up the snapshot data. It is important that
                // every snapshot gets processed by the game so that the spawns, despawns and updates lists
                // does not end up containing stuff from different snapshots
                GameDebug.Assert(spawns.Count == 0 && despawns.Count == 0 && updates.Count == 0, "Game did not consume snapshots");
            }

            // We have to skip this if we dropped snapshot as we will then be in the middle of the input stream
            if ((content & NetworkMessage.Events) != 0 && !m_DropSnapshots)
            {
                ReadEvents(ref input, networkClientConsumer);
            }
            counters.AddSectionStats("events", input.GetBitPosition2(), new Color(1.0f, 0.84f, 0.0f));

            counters.AddSectionStats("unknown", assembledSize * 8, Color.black);

            counters.packageContentStatsPackageSequence = packageSequence;
        }