Example #1
0
void NetBase::CheckResendPkts()
{
    // NOTE: Globaliterators on csHash do not retrieve keys contiguously.
    csHash<csRef<psNetPacketEntry> , PacketKey>::GlobalIterator it(awaitingack.GetIterator());
    csRef<psNetPacketEntry> pkt;
    csArray<csRef<psNetPacketEntry> > pkts;
    csArray<Connection*> resentConnections;

    csTicks currenttime = csGetTicks();
    unsigned int resentCount = 0;

    while(it.HasNext())
    {
        pkt = it.Next();
        // Check the connection packet timeout
        if (pkt->timestamp + csMin((csTicks)PKTMAXRTO, pkt->RTO) < currenttime)
            pkts.Push(pkt);
    }
    for (size_t i = 0; i < pkts.GetSize(); i++)
    {
        pkt = pkts.Get(i);
#ifdef PACKETDEBUG
        Debug2(LOG_NET,0,"Resending nonacked HIGH packet (ID %d).\n", pkt->packet->pktid);
#endif
        Connection* connection = GetConnByNum(pkt->clientnum);
        if (connection)
        {
            if (resentConnections.Find(connection) == csArrayItemNotFound)
                resentConnections.Push(connection);

            // This indicates a bug in the netcode.
            if (pkt->RTO == 0)
            {
                Error1("Unexpected 0 packet RTO.");
                abort();
            }

            pkt->RTO *= 2;
            connection->resends++;
        }
        resentCount++;
        
        pkt->timestamp = currenttime;   // update stamp on packet
        pkt->retransmitted = true;
        
        // re-add to send queue
        if(NetworkQueue->Add(pkt))
        {
            //printf("pkt=%p, pkt->packet=%p\n",pkt,pkt->packet);
            // take out of awaiting ack pool.
            // This does NOT delete the pkt mem block itself.
            if (!awaitingack.Delete(PacketKey(pkt->clientnum, pkt->packet->pktid), pkt))
            {
#ifdef PACKETDEBUG
                Debug2(LOG_NET,0,"No packet in ack queue :%d\n", pkt->packet->pktid);
#endif
            }
            else if(connection)
            {
                connection->RemoveFromWindow(pkt->packet->GetPacketSize());
            }
        }
    }

    if(resentCount > 0)
    {
        resends[resendIndex] = resentCount;
        resendIndex = (resendIndex + 1) % RESENDAVGCOUNT;
        
        csTicks timeTaken = csGetTicks() - currenttime;
        if(resentCount > 300 || resendIndex == 1 || timeTaken > 50)
        {
            size_t peakResend = 0;
            float resendAvg = 0.0f;
            // Calculate averages data here
            for(int i = 0; i < RESENDAVGCOUNT; i++)
            {
                resendAvg += resends[i];
                peakResend = csMax(peakResend, resends[i]);
            }
            resendAvg /= RESENDAVGCOUNT;
            csString status;
            if(timeTaken > 50)
            {
                status.Format("Resending high priority packets has taken %u time to process, for %u packets.", timeTaken, resentCount);
                CPrintf(CON_WARNING, "%s\n", (const char *) status.GetData());
            }
            status.AppendFmt("Resending non-acked packet statistics: %g average resends, peak of %zu resent packets", resendAvg, peakResend);
 
            if(LogCSV::GetSingletonPtr())
                LogCSV::GetSingleton().Write(CSV_STATUS, status);
        }
        
    }
}
Example #2
0
void NetManager::CheckResendPkts()
{
    // NOTE: Globaliterators on csHash do not retrieve keys contiguously.
    csHash<csRef<psNetPacketEntry>, PacketKey>::GlobalIterator it (awaitingack.GetIterator());
    csRef<psNetPacketEntry> pkt;
    csArray<csRef<psNetPacketEntry> > pkts;
    csArray<Connection*> resentConnections;

    // Connections that should be avoided because we know they are full
    csArray<Connection*> fullConnections;

    csTicks currenttime = csGetTicks();
    unsigned int resentCount = 0;

    while(it.HasNext())
    {
        pkt = it.Next();
        // Check the connection packet timeout
        if (pkt->timestamp + MIN(PKTMAXRTO, pkt->RTO) < currenttime)
            pkts.Push(pkt);
    }
    for (size_t i = 0; i < pkts.GetSize(); i++)
    {
#ifdef PACKETDEBUG
        Debug2(LOG_NET,"Resending nonacked HIGH packet (ID %d).\n", pkt->packet->pktid);
#endif
        pkt = pkts.Get(i);

        // re-add to send queue
        csRef<NetPacketQueueRefCount> outqueue = clients.FindQueueAny(pkt->clientnum);
        if (!outqueue)
        {
            awaitingack.Delete(PacketKey(pkt->clientnum, pkt->packet->pktid), pkt);
            continue;
        }

        Connection* connection = GetConnByNum(pkt->clientnum);
        if (connection)
        {
            if (resentConnections.Find(connection) == csArrayItemNotFound)
                resentConnections.Push(connection);
            if (fullConnections.Find(connection) != csArrayItemNotFound)
                continue;
            // This indicates a bug in the netcode.
            if (pkt->RTO == 0)
            {
                Error1("Unexpected 0 packet RTO.");
                abort();
            }
            pkt->RTO *= 2;
            connection->resends++;
        }
        resentCount++;

        pkt->timestamp = currenttime;   // update stamp on packet
        pkt->retransmitted = true;

        /*  The proper way to send a message is to add it to the queue, and then add the queue to the senders.
        *  If you do it the other way around the net thread may remove the queue from the senders before you add the packet.
        *   Yes - this has actually happened!
        */

        /* We store the result here to return as the result of this function to maintain historic functionality.
        *  In actuality a false response does not actually mean no data was added to the queue, just that
        *  not all of the data could be added.
        */
        if (!outqueue->Add(pkt))
        {
            psNetPacket* packet = pkt->packet;
            int type = 0;

            if (packet->offset == 0)
            {
                psMessageBytes* msg = (psMessageBytes*) packet->data;
                type = msg->type;
            }
            Error4("Queue full. Could not add packet with clientnum %d type %s ID %d.\n", pkt->clientnum, type == 0 ? "Fragment" : (const char *)  GetMsgTypeName(type), pkt->packet->pktid);
            fullConnections.Push(connection);
            continue;
        }

        /**
        * The senders list is a list of busy queues.  The SendOut() function
        * in NetBase clears this list each time through.  This saves having
        * to check every single connection each time through.
        */

        if (!senders.Add (outqueue))
            Error1("Senderlist Full!");

        //printf("pkt=%p, pkt->packet=%p\n",pkt,pkt->packet);
        // take out of awaiting ack pool.
        // This does NOT delete the pkt mem block itself.
        if (!awaitingack.Delete(PacketKey(pkt->clientnum, pkt->packet->pktid), pkt))
        {
#ifdef PACKETDEBUG
            Debug2(LOG_NET,"No packet in ack queue :%d\n", pkt->packet->pktid);
#endif
        }
        else if(connection)
            connection->RemoveFromWindow(pkt->packet->GetPacketSize());
    }
    if(resentCount > 0)
    {
        resends[resendIndex] = resentCount;
        resendIndex = (resendIndex + 1) % RESENDAVGCOUNT;

        csTicks timeTaken = csGetTicks() - currenttime;
        if(resendIndex == 1 || timeTaken > 50)
        {
            unsigned int peakResend = 0;
            float resendAvg = 0.0f;
            // Calculate averages data here
            for(int i = 0; i < RESENDAVGCOUNT; i++)
            {
                resendAvg += resends[i];
                peakResend = MAX(peakResend, resends[i]);
            }
            resendAvg /= RESENDAVGCOUNT;
            csString status;
            if(timeTaken > 50 || pkts.GetSize() > 300)
            {
                status.Format("Resending high priority packets has taken %u time to process, for %u packets on %zu unique connections %zu full connections (Sample clientnum %u/RTO %u. ", timeTaken, resentCount, resentConnections.GetSize(),fullConnections.GetSize(), resentConnections[0]->clientnum, resentConnections[0]->RTO);
                CPrintf(CON_WARNING, "%s\n", (const char *) status.GetData());
            }
            status.AppendFmt("Resending non-acked packet statistics: %g average resends, peak of %u resent packets", resendAvg, peakResend);

            if(LogCSV::GetSingletonPtr())
                LogCSV::GetSingleton().Write(CSV_STATUS, status);
        }

    }

}