/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */ #include "stdh.h" #include #include #include #include #include #include #include // should the packet transfers in/out of the buffer be reported to the console extern INDEX net_bReportPackets; extern INDEX net_iMaxSendRetries; extern FLOAT net_fSendRetryWait; #define MAX_RETRIES 10 #define RETRY_INTERVAL 3.0f // make the address broadcast void CAddress::MakeBroadcast(void) { adr_ulAddress = INADDR_BROADCAST; extern INDEX net_iPort; adr_uwPort = net_iPort; adr_uwID = 0; } /* * * CPacket class implementation * */ // copy constructor CPacket::CPacket(CPacket &paOriginal) { ASSERT(paOriginal.pa_pubPacketData != NULL && paOriginal.pa_slSize > 0); pa_slSize = paOriginal.pa_slSize; pa_slTransferSize = paOriginal.pa_slTransferSize; pa_ulSequence = paOriginal.pa_ulSequence; pa_ubReliable = paOriginal.pa_ubReliable; pa_tvSendWhen = paOriginal.pa_tvSendWhen; pa_ubRetryNumber = paOriginal.pa_ubRetryNumber; pa_adrAddress.adr_ulAddress = paOriginal.pa_adrAddress.adr_ulAddress; pa_adrAddress.adr_uwPort = paOriginal.pa_adrAddress.adr_uwPort; pa_adrAddress.adr_uwID = paOriginal.pa_adrAddress.adr_uwID; memcpy(pa_pubPacketData,paOriginal.pa_pubPacketData,pa_slSize); }; // initialization of the packet - clear all data and remove the packet from any list (buffer) it is in void CPacket::Clear() { pa_slSize = 0; pa_slTransferSize = 0; pa_ubReliable = UDP_PACKET_UNRELIABLE; pa_ubRetryNumber = 0; pa_tvSendWhen = CTimerValue(0.0f); if(pa_lnListNode.IsLinked()) pa_lnListNode.Remove(); }; void CPacket::operator=(const CPacket &paOriginal) { ASSERT(paOriginal.pa_pubPacketData != NULL && paOriginal.pa_slSize > 0); pa_slSize = paOriginal.pa_slSize; pa_slTransferSize = paOriginal.pa_slTransferSize; pa_ulSequence = paOriginal.pa_ulSequence; pa_ubReliable = paOriginal.pa_ubReliable; pa_tvSendWhen = paOriginal.pa_tvSendWhen; pa_ubRetryNumber = paOriginal.pa_ubRetryNumber; pa_adrAddress.adr_ulAddress = paOriginal.pa_adrAddress.adr_ulAddress; pa_adrAddress.adr_uwPort = paOriginal.pa_adrAddress.adr_uwPort; pa_adrAddress.adr_uwID = paOriginal.pa_adrAddress.adr_uwID; memcpy(pa_pubPacketData,paOriginal.pa_pubPacketData,pa_slSize); }; // Takes data from a pointer, adds the packet header and copies the data to the packet BOOL CPacket::WriteToPacket(void* pv,SLONG slSize,UBYTE ubReliable,ULONG ulSequence,UWORD uwClientID,SLONG slTransferSize) { UBYTE* pubData; ASSERT(slSize <= MAX_UDP_BLOCK_SIZE && slSize > 0); ASSERT(pv != NULL); ASSERT(slTransferSize >= slSize); // set packet properties to values received as parameters pa_ubReliable = ubReliable; pa_adrAddress.adr_uwID = uwClientID; pa_ulSequence = ulSequence; pa_slSize = slSize + MAX_HEADER_SIZE; pa_slTransferSize = slTransferSize; // insert packet header to the beginning of the packet data pubData = pa_pubPacketData; *pubData = pa_ubReliable; pubData++; *(ULONG*)pubData = pa_ulSequence; pubData+=sizeof(pa_ulSequence); *(UWORD*)pubData = pa_adrAddress.adr_uwID; pubData+=sizeof(pa_adrAddress.adr_uwID); *(SLONG*)pubData = pa_slTransferSize; pubData+=sizeof(pa_slTransferSize); // copy the data the packet is to contain memcpy(pubData,pv,slSize); return TRUE; }; // Takes data from a pointer, reads the packet header and copies the data to the packet BOOL CPacket::WriteToPacketRaw(void* pv,SLONG slSize) { UBYTE* pubData; ASSERT(slSize <= MAX_PACKET_SIZE && slSize > 0); ASSERT(pv != NULL); // get the packet properties from the pointer, and set the values pubData = (UBYTE*)pv; pa_ubReliable = *pubData; pubData++; pa_ulSequence = *(ULONG*)pubData; pubData+=sizeof(pa_ulSequence); pa_adrAddress.adr_uwID = *(UWORD*)pubData; pubData+=sizeof(pa_adrAddress.adr_uwID); pa_slTransferSize = *(SLONG*)pubData; pubData+=sizeof(pa_slTransferSize); pa_slSize = slSize; // transfer the data to the packet memcpy(pa_pubPacketData,pv,pa_slSize); return TRUE; }; // Copies the data from the packet to the location specified by the *pv. // packet header data is skipped BOOL CPacket::ReadFromPacket(void* pv,SLONG &slExpectedSize) { UBYTE* pubData; ASSERT(slExpectedSize > 0); ASSERT(pv != NULL); ASSERT(pa_pubPacketData != NULL); if (slExpectedSize < (pa_slSize - MAX_HEADER_SIZE)) { return FALSE; } // how much data is actually returned slExpectedSize = pa_slSize - MAX_HEADER_SIZE; // skip the header data pubData = pa_pubPacketData + sizeof(pa_ubReliable) + sizeof(pa_ulSequence) + sizeof(pa_adrAddress.adr_uwID) + sizeof(pa_slTransferSize); memcpy(pv,pubData,slExpectedSize); return TRUE; }; // is the packet reliable? BOOL CPacket::IsReliable() { return pa_ubReliable; }; // is the packet a head of a reliable stream BOOL CPacket::IsReliableHead() { return pa_ubReliable & UDP_PACKET_RELIABLE_HEAD; }; // is the packet a tail of a reliable stream BOOL CPacket::IsReliableTail() { return pa_ubReliable & UDP_PACKET_RELIABLE_TAIL; }; // return the sequence of a packet ULONG CPacket::GetSequence() { ASSERT(pa_ubReliable); return pa_ulSequence; }; // return the retry status of a packet - can retry now, later or not at all UBYTE CPacket::CanRetry() { if (pa_ubRetryNumber >= net_iMaxSendRetries) { return RS_NOTATALL; } CTimerValue tvNow = _pTimer->GetHighPrecisionTimer(); if (tvNow < (pa_tvSendWhen + CTimerValue(net_fSendRetryWait * (pa_ubRetryNumber + 1)))) { return RS_NOTNOW; } return RS_NOW; }; //drop the packet from a list (buffer) void CPacket::Drop() { if (pa_lnListNode.IsLinked()) { pa_lnListNode.Remove(); } }; SLONG CPacket::GetTransferSize() { return pa_slTransferSize; }; BOOL CPacket::IsBroadcast() { if (pa_adrAddress.adr_uwID == '//' || pa_adrAddress.adr_uwID == 0) { return TRUE; } return FALSE; }; /* * * * CPacketBufferStats Class implementation * * */ // this class is used for MaxBPS limitation (prevets flooding the client) and for bandwidth limit and latency emulation void CPacketBufferStats::Clear(void) { pbs_fBandwidthLimit = 0; pbs_fLatencyLimit = 0.0f; pbs_fLatencyVariation = 0.0f; pbs_tvTimeNextPacketStart = _pTimer->GetHighPrecisionTimer(); }; // when can a certian ammount of data be sent? CTimerValue CPacketBufferStats::GetPacketSendTime(SLONG slSize) { CTimerValue tvNow = _pTimer->GetHighPrecisionTimer(); // calculate how much should the packet be delayed due to latency and due to bandwidth CTimerValue tvBandwidth; if (pbs_fBandwidthLimit<=0.0f) { tvBandwidth = CTimerValue(0.0); } else { tvBandwidth = CTimerValue(DOUBLE((slSize*8)/pbs_fBandwidthLimit)); } CTimerValue tvLatency; if (pbs_fLatencyLimit<=0.0f && pbs_fLatencyVariation<=0.0f) { tvLatency = CTimerValue(0.0); } else { tvLatency = CTimerValue(DOUBLE(pbs_fLatencyLimit+(pbs_fLatencyVariation*rand())/RAND_MAX)); } // time when the packet should be sent is max of CTimerValue tvStart( Max( // current time plus latency and (tvNow+tvLatency).tv_llValue, // next free point in time pbs_tvTimeNextPacketStart.tv_llValue)); // remember next free time and return it pbs_tvTimeNextPacketStart = tvStart+tvBandwidth; return pbs_tvTimeNextPacketStart; }; /* * * CPacketBuffer Class implementation * */ // Empty the packet buffer void CPacketBuffer::Clear() { pb_lhPacketStorage.Clear(); pb_ulNumOfPackets = 0; pb_ulNumOfReliablePackets = 0; pb_ulTotalSize = 0; pb_ulLastSequenceOut = 0; pb_pbsLimits.Clear(); }; // Is the packet buffer empty? BOOL CPacketBuffer::IsEmpty() { if (pb_ulNumOfPackets>0) { return FALSE; } return TRUE; }; // Calculate when the packet can be output from the buffer CTimerValue CPacketBuffer::GetPacketSendTime(SLONG slSize) { CTimerValue tvSendTime; // if traffic emulation is in use, use the time with the lower bandwidth limit if (pb_ppbsStats != NULL) { if (pb_ppbsStats->pbs_fBandwidthLimit > 0.0f && pb_ppbsStats->pbs_fBandwidthLimit < pb_pbsLimits.pbs_fBandwidthLimit) { tvSendTime = pb_ppbsStats->GetPacketSendTime(slSize); pb_pbsLimits.pbs_tvTimeNextPacketStart = tvSendTime; } else { tvSendTime = pb_pbsLimits.GetPacketSendTime(slSize); pb_ppbsStats->pbs_tvTimeNextPacketStart = tvSendTime; } // else just use the MaxBPS control } else { tvSendTime = pb_pbsLimits.GetPacketSendTime(slSize); } return tvSendTime; }; // Adds the packet to the end of the list BOOL CPacketBuffer::AppendPacket(CPacket &paPacket,BOOL bDelay) { // bDelay regulates if the packet should be delayed because of the bandwidth limits or not // internal buffers (reliable, waitack and master buffers) do not pay attention to bandwidth limits if (bDelay) { paPacket.pa_tvSendWhen = GetPacketSendTime(paPacket.pa_slSize); } else { paPacket.pa_tvSendWhen = _pTimer->GetHighPrecisionTimer(); } // Add the packet to the end of the list pb_lhPacketStorage.AddTail(paPacket.pa_lnListNode); pb_ulNumOfPackets++; // if the packet is reliable, bump up the number of reliable packets if (paPacket.pa_ubReliable & UDP_PACKET_RELIABLE) { pb_ulNumOfReliablePackets++; } // update the total size of data stored in the buffer pb_ulTotalSize += paPacket.pa_slSize - MAX_HEADER_SIZE; return TRUE; }; // Inserts the packet in the buffer, according to it's sequence number BOOL CPacketBuffer::InsertPacket(CPacket &paPacket,BOOL bDelay) { // find the right place to insert this packet (this is if this packet is out of sequence) FOREACHINLIST(CPacket,pa_lnListNode,pb_lhPacketStorage,litPacketIter) { // if there is a packet in the buffer with greater sequence number, insert this one before it if (paPacket.pa_ulSequence < litPacketIter->pa_ulSequence) { // bDelay regulates if the packet should be delayed because of the bandwidth limits or not // internal buffers (reliable, waitack and master buffers) do not pay attention to bandwidth limits if (bDelay) { paPacket.pa_tvSendWhen = GetPacketSendTime(paPacket.pa_slSize); } else { paPacket.pa_tvSendWhen = _pTimer->GetHighPrecisionTimer(); } litPacketIter.InsertBeforeCurrent(paPacket.pa_lnListNode); pb_ulNumOfPackets++; // if the packet is reliable, bump up the number of reliable packets if (paPacket.pa_ubReliable & UDP_PACKET_RELIABLE) { pb_ulNumOfReliablePackets++; } // update the total size of data stored in the buffer pb_ulTotalSize += paPacket.pa_slSize - MAX_HEADER_SIZE; return TRUE; // if there already is a packet in the buffer with the same sequence, do nothing } else if (paPacket.pa_ulSequence == litPacketIter->pa_ulSequence) { return FALSE; } } // if this packet has the greatest sequence number so far, add it to the end of the list pb_lhPacketStorage.AddTail(paPacket.pa_lnListNode); pb_ulNumOfPackets++; // if the packet is reliable, bump up the number of reliable packets if (paPacket.pa_ubReliable & UDP_PACKET_RELIABLE) { pb_ulNumOfReliablePackets++; } pb_ulTotalSize += paPacket.pa_slSize - MAX_HEADER_SIZE; return TRUE; }; // Bumps up the retry count and time, and appends the packet to the buffer BOOL CPacketBuffer::Retry(CPacket &paPacket) { paPacket.pa_ubRetryNumber++; if (net_bReportPackets == TRUE) { CPrintF("Retrying sequence: %d, reliable flag: %d\n",paPacket.pa_ulSequence,paPacket.pa_ubReliable); } return AppendPacket(paPacket,TRUE); }; // Reads the data from the first packet in the bufffer, but does not remove it CPacket* CPacketBuffer::PeekFirstPacket() { ASSERT(pb_ulNumOfPackets != 0); return LIST_HEAD(pb_lhPacketStorage,CPacket,pa_lnListNode); }; // Reads the first packet in the bufffer, and removes it from the buffer CPacket* CPacketBuffer::GetFirstPacket() { ASSERT(pb_ulNumOfPackets != 0); CPacket* ppaHead = LIST_HEAD(pb_lhPacketStorage,CPacket,pa_lnListNode); // remove the first packet from the start of the list pb_lhPacketStorage.RemHead(); pb_ulNumOfPackets--; if (ppaHead->pa_ubReliable & UDP_PACKET_RELIABLE) { pb_ulNumOfReliablePackets--; } // update the total size of data stored in the buffer pb_ulTotalSize -= (ppaHead->pa_slSize - MAX_HEADER_SIZE); // mark the last packet sequence that was output from the buffer - helps to prevent problems wit duplicated packets if (pb_ulLastSequenceOut < ppaHead->pa_ulSequence) { pb_ulLastSequenceOut = ppaHead->pa_ulSequence; } return ppaHead; }; // Reads the data from the packet with the requested sequence, but does not remove it CPacket* CPacketBuffer::PeekPacket(ULONG ulSequence) { FOREACHINLIST(CPacket,pa_lnListNode,pb_lhPacketStorage,litPacketIter) { if (litPacketIter->pa_ulSequence == ulSequence) { return litPacketIter; } } return NULL; }; // Returns te packet with the matching sequence from the buffer CPacket* CPacketBuffer::GetPacket(ULONG ulSequence) { FOREACHINLIST(CPacket,pa_lnListNode,pb_lhPacketStorage,litPacketIter) { if (litPacketIter->pa_ulSequence == ulSequence) { litPacketIter->pa_lnListNode.Remove(); pb_ulNumOfPackets--; if (litPacketIter->pa_ubReliable & UDP_PACKET_RELIABLE) { pb_ulNumOfReliablePackets--; } // update the total size of data stored in the buffer pb_ulTotalSize -= (litPacketIter->pa_slSize - MAX_HEADER_SIZE); return litPacketIter; } } return NULL; }; // Reads the first connection request packet from the buffer CPacket* CPacketBuffer::GetConnectRequestPacket() { FOREACHINLIST(CPacket,pa_lnListNode,pb_lhPacketStorage,litPacketIter) { if (litPacketIter->pa_ubReliable & UDP_PACKET_CONNECT_REQUEST) { litPacketIter->pa_lnListNode.Remove(); pb_ulNumOfPackets--; // connect request packets are allways reliable pb_ulNumOfReliablePackets--; // update the total size of data stored in the buffer pb_ulTotalSize -= (litPacketIter->pa_slSize - MAX_HEADER_SIZE); return litPacketIter; } } return NULL; }; // Removes the first packet from the buffer BOOL CPacketBuffer::RemoveFirstPacket(BOOL bDelete) { ASSERT(pb_ulNumOfPackets > 0); CPacket *lnHead = LIST_HEAD(pb_lhPacketStorage,CPacket,pa_lnListNode); pb_ulNumOfPackets--; if (lnHead->pa_ubReliable & UDP_PACKET_RELIABLE) { pb_ulNumOfReliablePackets--; } // update the total size of data stored in the buffer pb_ulTotalSize -= (lnHead->pa_slSize - MAX_HEADER_SIZE); if (pb_ulLastSequenceOut < lnHead->pa_ulSequence) { pb_ulLastSequenceOut = lnHead->pa_ulSequence; } pb_lhPacketStorage.RemHead(); if (bDelete) { delete lnHead; } return TRUE; }; // Removes the packet with the requested sequence from the buffer BOOL CPacketBuffer::RemovePacket(ULONG ulSequence,BOOL bDelete) { // ASSERT(pb_ulNumOfPackets > 0); FORDELETELIST(CPacket,pa_lnListNode,pb_lhPacketStorage,litPacketIter) { if (litPacketIter->pa_ulSequence == ulSequence) { litPacketIter->pa_lnListNode.Remove(); pb_ulNumOfPackets--; if (litPacketIter->pa_ubReliable & UDP_PACKET_RELIABLE) { pb_ulNumOfReliablePackets--; } // update the total size of data stored in the buffer pb_ulTotalSize -= (litPacketIter->pa_slSize - MAX_HEADER_SIZE); if (bDelete) { delete litPacketIter; } } } return FALSE; }; // Remove connect response packets from the buffer BOOL CPacketBuffer::RemoveConnectResponsePackets() { FORDELETELIST(CPacket,pa_lnListNode,pb_lhPacketStorage,litPacketIter) { if (litPacketIter->pa_ubReliable & UDP_PACKET_CONNECT_RESPONSE) { litPacketIter->pa_lnListNode.Remove(); pb_ulNumOfPackets--; // connect request packets are allways reliable pb_ulNumOfReliablePackets--; // update the total size of data stored in the buffer pb_ulTotalSize -= (litPacketIter->pa_slSize - MAX_HEADER_SIZE); delete litPacketIter; } } return NULL; }; // Gets the sequence number of the first packet in the buffer ULONG CPacketBuffer::GetFirstSequence() { ASSERT(pb_ulNumOfPackets > 0); return LIST_HEAD(pb_lhPacketStorage,CPacket,pa_lnListNode)->pa_ulSequence; }; // Gets the sequence number of the last packet in the buffer ULONG CPacketBuffer::GetLastSequence() { ASSERT(pb_ulNumOfPackets > 0); return LIST_TAIL(pb_lhPacketStorage,CPacket,pa_lnListNode)->pa_ulSequence; }; // Removes the packet with the requested sequence from the buffer BOOL CPacketBuffer::IsSequenceInBuffer(ULONG ulSequence) { FOREACHINLIST(CPacket,pa_lnListNode,pb_lhPacketStorage,litPacketIter) { if (litPacketIter->pa_ulSequence == ulSequence) { return TRUE; } } return FALSE; }; // Check if the buffer contains a complete sequence of reliable packets at the start of the buffer BOOL CPacketBuffer::CheckSequence(SLONG &slSize) { CPacket* paPacket; ULONG ulSequence; slSize=0; if (pb_ulNumOfPackets == 0) { return FALSE; } paPacket = LIST_HEAD(pb_lhPacketStorage,CPacket,pa_lnListNode); // if the first packet is not the head of the reliable packet transfer if (!(paPacket->pa_ubReliable & UDP_PACKET_RELIABLE_HEAD)) { return FALSE; } ulSequence = paPacket->pa_ulSequence; // for each packet in the buffer FOREACHINLIST(CPacket,pa_lnListNode,pb_lhPacketStorage,litPacketIter) { // if it's out of order (there is a gap in the reliable sequence), the message is not complete if (litPacketIter->pa_ulSequence != ulSequence) { return FALSE; } // if it's a tail of the reliable sequence the message is complete (all packets so far // have been in order) if (litPacketIter->pa_ubReliable & UDP_PACKET_RELIABLE_TAIL) { return TRUE; } slSize += litPacketIter->pa_slSize - MAX_HEADER_SIZE; ulSequence++; } // if the function hasn't exited while in the loop, the message is not complete // (all the packets are in sequence, but there is no tail) return FALSE; };