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