123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705 |
- /* Copyright (c) 2002-2012 Croteam Ltd.
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as published by
- the Free Software Foundation
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
- #include "stdh.h"
- #include <Engine/Base/CTString.h>
- #include <Engine/Base/Console.h>
- #include <Engine/Base/ErrorReporting.h>
- #include <Engine/Base/ErrorTable.h>
- #include <Engine/Base/Lists.h>
- #include <Engine/Base/Stream.h>
- #include <Engine/Base/Translation.h>
- #include <Engine/Network/ClientInterface.h>
- #include <Engine/Network/CPacket.h>
- #include <Engine/Base/Listiterator.inl>
- // how many acknowledges can fit into one UDP packet
- #define MAX_ACKS_PER_PACKET (MAX_UDP_BLOCK_SIZE/sizeof(ULONG))
- extern FLOAT net_fDropPackets;
- extern INDEX net_bReportPackets;
- CClientInterface::CClientInterface(void)
- {
- Clear();
- };
- CClientInterface::~CClientInterface(void)
- {
- Clear();
- };
- void CClientInterface::Clear(void)
- {
- ci_bUsed = FALSE;
- ci_bReliableComplete = FALSE;
- ci_pbInputBuffer.Clear();
- ci_pbOutputBuffer.Clear();
- ci_pbReliableInputBuffer.Clear();
- ci_pbWaitAckBuffer.Clear();
- ci_adrAddress.Clear();
- ci_strAddress = "";
-
- ci_pciOther = NULL;
- ci_ulSequence = 0;
- };
- // mark the client interface as local for this computer
- void CClientInterface::SetLocal(CClientInterface *pciOther)
- {
- Clear();
- ci_bUsed = TRUE;
- ci_bClientLocal = TRUE;
- ci_pciOther = pciOther;
- if (pciOther!=NULL) {
- pciOther->ci_pciOther = this;
- }
- ci_adrAddress.Clear();
-
- };
- // send a message through this client interface - reliable messages are not limited in size
- void CClientInterface::Send(const void *pvSend, SLONG slSize,BOOL bReliable)
- {
- ASSERT (ci_bUsed == TRUE);
- ASSERT(pvSend != NULL && slSize>0);
- // unreliable messages must fit within one UDP packet
- ASSERT(bReliable != UDP_PACKET_UNRELIABLE || slSize < MAX_UDP_BLOCK_SIZE);
- UBYTE ubPacketReliable;
- UBYTE* pubData;
- SLONG slSizeToSend;
- SLONG slTransferSize;
- ULONG ulSequence;
- CPacket* ppaNewPacket;
- //if the message is reliable, make sure the first packet is marked as a head of the message
- if (bReliable) {
- ubPacketReliable = UDP_PACKET_RELIABLE | UDP_PACKET_RELIABLE_HEAD;
- if (slSize <= MAX_UDP_BLOCK_SIZE) {
- ubPacketReliable |= UDP_PACKET_RELIABLE_TAIL;
- }
- } else {
- ubPacketReliable = UDP_PACKET_UNRELIABLE;
- }
- pubData = (UBYTE*) pvSend;
- slSizeToSend = slSize;
- slTransferSize = slSizeToSend;
-
-
- // split large reliable messages into packets, and put them in the output buffer
- while (slSizeToSend>MAX_UDP_BLOCK_SIZE) {
- ppaNewPacket = new CPacket;
- // for each packet, increment the sequence (very important)
- ulSequence = (++ci_ulSequence);
- ppaNewPacket->WriteToPacket(pubData,MAX_UDP_BLOCK_SIZE,ubPacketReliable,ulSequence,ci_adrAddress.adr_uwID,slTransferSize);
- ppaNewPacket->pa_adrAddress.adr_ulAddress = ci_adrAddress.adr_ulAddress;
- ppaNewPacket->pa_adrAddress.adr_uwPort = ci_adrAddress.adr_uwPort;
- ppaNewPacket->pa_adrAddress.adr_uwID = ci_adrAddress.adr_uwID;
- ci_pbOutputBuffer.AppendPacket(*ppaNewPacket,TRUE);
- // turn off udp head flag, if exists (since we just put a packet in the output buffer, the next
- // packet cannot be the head
- ubPacketReliable &= UDP_PACKET_RELIABLE;
- slSizeToSend -= MAX_UDP_BLOCK_SIZE;
- pubData += MAX_UDP_BLOCK_SIZE;
- }
- // what remains is a tail of a reliable message, or an unreliable packet
- if (ubPacketReliable != UDP_PACKET_UNRELIABLE) {
- ubPacketReliable |= UDP_PACKET_RELIABLE_TAIL;
- }
- // so send it
- ppaNewPacket = new CPacket;
- ulSequence = (++ci_ulSequence);
- ppaNewPacket->WriteToPacket(pubData,slSizeToSend,ubPacketReliable,ulSequence,ci_adrAddress.adr_uwID,slTransferSize);
- ppaNewPacket->pa_adrAddress.adr_ulAddress = ci_adrAddress.adr_ulAddress;
- ppaNewPacket->pa_adrAddress.adr_uwPort = ci_adrAddress.adr_uwPort;
- ppaNewPacket->pa_adrAddress.adr_uwID = ci_adrAddress.adr_uwID;
- ci_pbOutputBuffer.AppendPacket(*ppaNewPacket,TRUE);
-
- };
- // send a message through this client interface, to the provided address
- void CClientInterface::SendTo(const void *pvSend, SLONG slSize,const CAddress adrAdress,BOOL bReliable)
- {
- ASSERT (ci_bUsed);
- ASSERT(pvSend != NULL && slSize>0);
- // unreliable packets must fit within one UDP packet
- ASSERT(bReliable != UDP_PACKET_UNRELIABLE || slSize < MAX_UDP_BLOCK_SIZE);
- UBYTE ubPacketReliable;
- UBYTE* pubData;
- SLONG slSizeToSend;
- SLONG slTransferSize;
- ULONG ulSequence;
- CPacket* ppaNewPacket;
- //if the message is reliable, make sure the first packet is marked as a head of the message
- if (bReliable) {
- ubPacketReliable = UDP_PACKET_RELIABLE | UDP_PACKET_RELIABLE_HEAD;
- if (slSize <= MAX_UDP_BLOCK_SIZE) {
- ubPacketReliable |= UDP_PACKET_RELIABLE_TAIL;
- }
- } else {
- ubPacketReliable = UDP_PACKET_UNRELIABLE;
- }
- pubData = (UBYTE*) pvSend;
- slSizeToSend = slSize;
- slTransferSize = slSizeToSend;
-
- // split large reliable messages into packets, and put them in the output buffer
- while (slSizeToSend>MAX_UDP_BLOCK_SIZE) {
- ppaNewPacket = new CPacket;
- // for each packet, increment the sequence (very important)
- ulSequence = (++ci_ulSequence);
- ppaNewPacket->WriteToPacket(pubData,MAX_UDP_BLOCK_SIZE,ubPacketReliable,ulSequence,adrAdress.adr_uwID,slTransferSize);
- ppaNewPacket->pa_adrAddress.adr_ulAddress = adrAdress.adr_ulAddress;
- ppaNewPacket->pa_adrAddress.adr_uwPort = adrAdress.adr_uwPort;
- ppaNewPacket->pa_adrAddress.adr_uwID = adrAdress.adr_uwID;
- ci_pbOutputBuffer.AppendPacket(*ppaNewPacket,TRUE);
- // turn off udp head flag, if exists (since we just put a packet in the output buffer, the next
- // packet cannot be the head
- ubPacketReliable &= UDP_PACKET_RELIABLE;
- slSizeToSend -= MAX_UDP_BLOCK_SIZE;
- pubData += MAX_UDP_BLOCK_SIZE;
- }
- // what remains is a tail of a reliable message, or an unreliable packet
- if (ubPacketReliable != UDP_PACKET_UNRELIABLE) {
- ubPacketReliable |= UDP_PACKET_RELIABLE_TAIL;
- }
- ppaNewPacket = new CPacket;
- ulSequence = (++ci_ulSequence);
- ppaNewPacket->WriteToPacket(pubData,slSizeToSend,ubPacketReliable,ulSequence,adrAdress.adr_uwID,slTransferSize);
- ppaNewPacket->pa_adrAddress.adr_ulAddress = adrAdress.adr_ulAddress;
- ppaNewPacket->pa_adrAddress.adr_uwPort = adrAdress.adr_uwPort;
- ppaNewPacket->pa_adrAddress.adr_uwID = adrAdress.adr_uwID;
- ci_pbOutputBuffer.AppendPacket(*ppaNewPacket,TRUE);
- };
- // receive a message through the interface, discard originating address
- BOOL CClientInterface::Receive(void *pvReceive, SLONG &slSize,BOOL bReliable)
- {
- ASSERT (slSize>0);
- ASSERT (pvReceive != NULL);
- // we'll use the other receive procedure, and tell it to ignore the address
- return ReceiveFrom(pvReceive,slSize,NULL,bReliable);
- };
- // receive a message through the interface, and fill in the originating address
- BOOL CClientInterface::ReceiveFrom(void *pvReceive, SLONG &slSize, CAddress *padrAdress,BOOL bReliable)
- {
- CPacket* ppaPacket;
- UBYTE* pubData = (UBYTE*) pvReceive;
- SLONG slDummySize;
- UBYTE ubReliable;
- // if a reliable message is requested
- if (bReliable) {
- // if there is no complete reliable message ready
- if (ci_pbReliableInputBuffer.CheckSequence(slDummySize) == FALSE) {
- return FALSE;
- // if the ready message is longer than the expected size
- } else if ( GetCurrentReliableSize() > slSize) {
- return FALSE;
- // if everything is ok, compose the message and kill the packets
- } else {
- // fill in the originating address (if necessary)
- if (padrAdress != NULL) {
- ppaPacket = ci_pbReliableInputBuffer.PeekFirstPacket();
- padrAdress->adr_ulAddress = ppaPacket->pa_adrAddress.adr_ulAddress;
- padrAdress->adr_uwPort = ppaPacket->pa_adrAddress.adr_uwPort;
- padrAdress->adr_uwID = ppaPacket->pa_adrAddress.adr_uwID;
- }
- slSize = 0;
- do {
- ppaPacket = ci_pbReliableInputBuffer.GetFirstPacket();
- ubReliable = ppaPacket->pa_ubReliable;
- slDummySize = ppaPacket->pa_slSize - MAX_HEADER_SIZE;
- ppaPacket->ReadFromPacket(pubData,slDummySize);
- pubData += slDummySize;
- slSize += slDummySize;
- delete ppaPacket;
- } while (!(ubReliable & UDP_PACKET_RELIABLE_TAIL));
- return TRUE;
- }
- // if an unreliable message is requested
- } else {
- // if there are no packets in the input buffer, return
- if (ci_pbInputBuffer.pb_ulNumOfPackets == 0) {
- return FALSE;
- }
- ppaPacket = ci_pbInputBuffer.PeekFirstPacket();
- // if the reliable buffer is not empty, nothing can be accepted from the input buffer
- // because it would be accepted out-of order (before earlier sequences have been read)
- if (ci_pbReliableInputBuffer.pb_ulNumOfPackets != 0) {
- return FALSE;
- // if the first packet in the input buffer is not unreliable
- } else if (ppaPacket->pa_ubReliable != UDP_PACKET_UNRELIABLE) {
- return FALSE;
- // if the ready message is longer than the expected size
- } else if ( ppaPacket->pa_slTransferSize > slSize) {
- return FALSE;
- // if everything is ok, read the packet data, and kill the packet
- } else {
- // fill in the originating address (if necessary)
- if (padrAdress != NULL) {
- padrAdress->adr_ulAddress = ppaPacket->pa_adrAddress.adr_ulAddress;
- padrAdress->adr_uwPort = ppaPacket->pa_adrAddress.adr_uwPort;
- padrAdress->adr_uwID = ppaPacket->pa_adrAddress.adr_uwID;
- }
- slSize = ppaPacket->pa_slSize - MAX_HEADER_SIZE;
- ppaPacket->ReadFromPacket(pubData,slSize);
- // remove the packet from the buffer, and delete it from memory
- ci_pbInputBuffer.RemoveFirstPacket(TRUE);
- return TRUE;
- }
-
- }
- return FALSE;
- };
- // receive a message through the interface, discard originating address
- BOOL CClientInterface::Receive(CTStream &strmReceive,UBYTE bReliable)
- {
- CPacket* ppaPacket;
- UBYTE ubReliable;
- SLONG slDummySize;
- // if a reliable message is requested
- if (bReliable) {
- // if there is no complete reliable message ready
- if (ci_pbReliableInputBuffer.CheckSequence(slDummySize) == FALSE) {
- return FALSE;
- // if everything is ok, compose the message and kill the packets
- } else {
- do {
- ppaPacket = ci_pbReliableInputBuffer.GetFirstPacket();
- ubReliable = ppaPacket->pa_ubReliable;
- strmReceive.Write_t(ppaPacket->pa_pubPacketData + MAX_HEADER_SIZE,ppaPacket->pa_slSize - MAX_HEADER_SIZE);
- if (ci_pbInputBuffer.pb_ulLastSequenceOut < ppaPacket->pa_ulSequence) {
- ci_pbInputBuffer.pb_ulLastSequenceOut = ppaPacket->pa_ulSequence;
- }
- delete ppaPacket;
- } while (!(ubReliable & UDP_PACKET_RELIABLE_TAIL));
- return TRUE;
- }
- // if an unreliable message is requested
- } else {
- ppaPacket = ci_pbInputBuffer.PeekFirstPacket();
- // if the reliable buffer is not empty, nothing can be accepted from the input buffer
- // because it would be accepted out-of order (before earlier sequences have been read)
- if (ci_pbReliableInputBuffer.pb_ulNumOfPackets != 0) {
- return FALSE;
- // if the first packet in the input buffer is not unreliable
- } else if (ppaPacket->pa_ubReliable != UDP_PACKET_RELIABLE) {
- return FALSE;
- // if everything is ok, read the packet data, and kill the packet
- } else {
- strmReceive.Write_t(ppaPacket->pa_pubPacketData + MAX_HEADER_SIZE,ppaPacket->pa_slSize - MAX_HEADER_SIZE);
- // remove the packet from the buffer, and delete it from memory
- if (ci_pbInputBuffer.pb_ulLastSequenceOut < ppaPacket->pa_ulSequence) {
- ci_pbInputBuffer.pb_ulLastSequenceOut = ppaPacket->pa_ulSequence;
- }
- ci_pbInputBuffer.RemoveFirstPacket(TRUE);
- return TRUE;
- }
-
- }
- return FALSE;
- };
- // exchanges packets beetween this socket and it's local partner
- // from output of this buffet to the input of the other and vice versa
- void CClientInterface::ExchangeBuffers(void)
- {
- ASSERT (ci_pciOther != NULL);
- CPacket* ppaPacket;
- CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
- // take the output from this interface and give it to it's partner socket
- while (ci_pbOutputBuffer.pb_ulNumOfPackets > 0) {
- ppaPacket = ci_pbOutputBuffer.PeekFirstPacket();
- if (ppaPacket->pa_tvSendWhen < tvNow) {
- ci_pbOutputBuffer.RemoveFirstPacket(FALSE);
- if (ci_pciOther->ci_pbInputBuffer.InsertPacket(*ppaPacket,FALSE) == FALSE) {
- delete ppaPacket;
- }
- } else {
- break;
- }
- }
- // and the other way around
- while (ci_pciOther->ci_pbOutputBuffer.pb_ulNumOfPackets > 0) {
- ppaPacket = ci_pciOther->ci_pbOutputBuffer.PeekFirstPacket();
- if (ppaPacket->pa_tvSendWhen < tvNow) {
- ppaPacket = ci_pciOther->ci_pbOutputBuffer.GetFirstPacket();
- if (ci_pbInputBuffer.InsertPacket(*ppaPacket,FALSE) == FALSE) {
- delete ppaPacket;
- };
- } else {
- break;
- }
- }
- };
- // update interface's input buffer (transfer from input buffer to the reliable buffer...),
- // for incoming acknowledge packets, remove acknowledged packets from the output buffers,
- // and generate acknowledge messages for incoming reliable packets
- BOOL CClientInterface::UpdateInputBuffers(void)
- {
- BOOL bSomethingDone;
- ULONG pulGenAck[MAX_ACKS_PER_PACKET];
- ULONG ulAckCount=0;
- CTimerValue tvNow;
-
- // if there are packets in the input buffer, process them
- FORDELETELIST(CPacket,pa_lnListNode,ci_pbInputBuffer.pb_lhPacketStorage,ppaPacket) {
- CPacket &paPacket = *ppaPacket;
-
- // if it's an acknowledge packet, remove the acknowledged packets from the wait acknowledge buffer
- if (ppaPacket->pa_ubReliable & UDP_PACKET_ACKNOWLEDGE) {
- ULONG *pulAck;
- SLONG slSize;
- ULONG ulSequence;
- slSize = ppaPacket->pa_slSize - MAX_HEADER_SIZE;
- // if slSize isn't rounded to the size of ulSequence, abort
- ASSERT (slSize % sizeof(ULONG) == 0);
- // get the pointer to the start of acknowledged sequences
- pulAck = (ULONG*) (ppaPacket->pa_pubPacketData + MAX_HEADER_SIZE);
- // for each acknowledged sequence number
- while (slSize>0) {
- ulSequence = *pulAck;
-
- // report the packet info to the console
- if (net_bReportPackets == TRUE) {
- tvNow = _pTimer->GetHighPrecisionTimer();
- CPrintF("%lu: Received acknowledge for packet sequence %d\n",(ULONG) tvNow.GetMilliseconds(),ulSequence);
- }
-
- // remove the matching packet from the wait acknowledge buffer
- ci_pbWaitAckBuffer.RemovePacket(ulSequence,TRUE);
- // if the packet is waiting to be resent it's in the outgoing buffer, so remove it
- ci_pbOutputBuffer.RemovePacket(ulSequence,TRUE);
- pulAck++;
- slSize -= sizeof(ULONG);
- }
- // take this packet out of the input buffer and kill it
- ci_pbInputBuffer.RemovePacket(ppaPacket->pa_ulSequence,FALSE);
- delete ppaPacket;
-
- bSomethingDone = TRUE;
- // if the packet is reliable
- } else if (ppaPacket->pa_ubReliable & UDP_PACKET_RELIABLE) {
-
- // generate packet acknowledge
- // if the packet is from the broadcast address, send the acknowledge for that packet only
- if (ppaPacket->pa_adrAddress.adr_uwID == '//' || ppaPacket->pa_adrAddress.adr_uwID == 0) {
- CPacket *ppaAckPacket = new CPacket;
- ppaAckPacket->pa_adrAddress.adr_ulAddress = ppaPacket->pa_adrAddress.adr_ulAddress;
- ppaAckPacket->pa_adrAddress.adr_uwPort = ppaPacket->pa_adrAddress.adr_uwPort;
- ppaAckPacket->WriteToPacket(&(ppaPacket->pa_ulSequence),sizeof(ULONG),UDP_PACKET_ACKNOWLEDGE,++ci_ulSequence,'//',sizeof(ULONG));
- ci_pbOutputBuffer.AppendPacket(*ppaAckPacket,TRUE);
- if (net_bReportPackets == TRUE) {
- CPrintF("Acknowledging broadcast packet sequence %d\n",ppaPacket->pa_ulSequence);
- }
- ci_pbInputBuffer.RemovePacket(ppaPacket->pa_ulSequence,FALSE);
- } else {
- // if we have filled the packet to the maximum with acknowledges (an extremely rare event)
- // finish this packet and start the next one
- if (ulAckCount == MAX_ACKS_PER_PACKET) {
- CPacket *ppaAckPacket = new CPacket;
- ppaAckPacket->pa_adrAddress.adr_ulAddress = ci_adrAddress.adr_ulAddress;
- ppaAckPacket->pa_adrAddress.adr_uwPort = ci_adrAddress.adr_uwPort;
- ppaAckPacket->WriteToPacket(pulGenAck,ulAckCount*sizeof(ULONG),UDP_PACKET_ACKNOWLEDGE,++ci_ulSequence,ci_adrAddress.adr_uwID,ulAckCount*sizeof(ULONG));
- ci_pbOutputBuffer.AppendPacket(*ppaAckPacket,TRUE);
- ulAckCount = 0;
- }
- // add the acknowledge for this packet
- pulGenAck[ulAckCount] = ppaPacket->pa_ulSequence;
- // report the packet info to the console
- if (net_bReportPackets == TRUE) {
- tvNow = _pTimer->GetHighPrecisionTimer();
- CPrintF("%lu: Acknowledging packet sequence %d\n",(ULONG) tvNow.GetMilliseconds(),ppaPacket->pa_ulSequence);
- }
- ulAckCount++;
- }
-
- // take this packet out of the input buffer
- ci_pbInputBuffer.RemovePacket(ppaPacket->pa_ulSequence,FALSE);
- if (ppaPacket->pa_ulSequence == 8) {
- ppaPacket->pa_ulSequence = 8;
- }
- // a packet can be accepted from the broadcast ID only if it is an acknowledge packet or
- // if it is a connection confirmation response packet and the client isn't already connected
- if (ppaPacket->pa_adrAddress.adr_uwID == '//' || ppaPacket->pa_adrAddress.adr_uwID == 0) {
- if (((!ci_bUsed) && (ppaPacket->pa_ubReliable & UDP_PACKET_CONNECT_RESPONSE)) ||
- (ppaPacket->pa_ubReliable & UDP_PACKET_ACKNOWLEDGE) || ci_bClientLocal) {
- /* if (ci_pbReliableInputBuffer.pb_ulLastSequenceOut >= ppaPacket->pa_ulSequence) {
- delete ppaPacket;
- } else*/
- ppaPacket->pa_ulSequence = 0;
- if (ci_pbReliableInputBuffer.InsertPacket(*ppaPacket,FALSE) == FALSE) {
- delete ppaPacket;
- }
- } else {
- delete ppaPacket;
- }
- // reject duplicates
- } else if (ppaPacket->pa_ulSequence > ci_pbReliableInputBuffer.pb_ulLastSequenceOut &&
- !(ci_pbReliableInputBuffer.IsSequenceInBuffer(ppaPacket->pa_ulSequence))) {
- if (ci_pbReliableInputBuffer.InsertPacket(*ppaPacket,FALSE) == FALSE) {
- delete ppaPacket;
- }
- } else {
- delete ppaPacket;
- }
- // if the packet is unreliable, leave it in the input buffer
- // when it is needed, the message will be pulled from there
- } else {
-
- // reject duplicates
- ci_pbInputBuffer.RemovePacket(ppaPacket->pa_ulSequence,FALSE);
- if (ppaPacket->pa_ulSequence > ci_pbInputBuffer.pb_ulLastSequenceOut &&
- !(ci_pbReliableInputBuffer.IsSequenceInBuffer(ppaPacket->pa_ulSequence))) {
- if (ci_pbInputBuffer.InsertPacket(*ppaPacket,FALSE) == FALSE) {
- delete ppaPacket;
- }
- } else {
- delete ppaPacket;
- }
- }
- }
- // if there are any remaining unsent acknowldges, put them into a packet and send it
- if (ulAckCount >0) {
- CPacket *ppaAckPacket = new CPacket;
- ppaAckPacket->pa_adrAddress.adr_ulAddress = ci_adrAddress.adr_ulAddress;
- ppaAckPacket->pa_adrAddress.adr_uwPort = ci_adrAddress.adr_uwPort;
- ppaAckPacket->WriteToPacket(pulGenAck,ulAckCount*sizeof(ULONG),UDP_PACKET_ACKNOWLEDGE,++ci_ulSequence,ci_adrAddress.adr_uwID,ulAckCount*sizeof(ULONG));
- ci_pbOutputBuffer.AppendPacket(*ppaAckPacket,TRUE);
- }
- return TRUE;
- };
- // update socket input buffer (transfer from input buffer to the reliable buffer...),
- // for incoming acknowledge packets, remove acknowledged packets from the output buffers,
- // and generate acknowledge messages for incoming reliable packets
- // this method is different than the previous becoause it sends acknowledges for each
- // packet separately, instead of grouping them together
- BOOL CClientInterface::UpdateInputBuffersBroadcast(void)
- {
- BOOL bSomethingDone;
- CTimerValue tvNow;
-
- // if there are packets in the input buffer, process them
- FORDELETELIST(CPacket,pa_lnListNode,ci_pbInputBuffer.pb_lhPacketStorage,ppaPacket) {
- CPacket &paPacket = *ppaPacket;
- // if it's an acknowledge packet, remove the acknowledged packets from the wait acknowledge buffer
- if (ppaPacket->pa_ubReliable & UDP_PACKET_ACKNOWLEDGE) {
- ULONG *pulAck;
- SLONG slSize;
- ULONG ulSequence;
- slSize = ppaPacket->pa_slSize - MAX_HEADER_SIZE;
- // if slSize isn't rounded to the size of ulSequence, abort
- ASSERT (slSize % sizeof(ULONG) == 0);
- // get the pointer to the start of acknowledged sequences
- pulAck = (ULONG*) (ppaPacket->pa_pubPacketData + MAX_HEADER_SIZE);
- // for each acknowledged sequence number
- while (slSize>0) {
- ulSequence = *pulAck;
- // report the packet info to the console
- if (net_bReportPackets == TRUE) {
- tvNow = _pTimer->GetHighPrecisionTimer();
- CPrintF("%lu: Received acknowledge for broadcast packet sequence %d\n",(ULONG) tvNow.GetMilliseconds(),ulSequence);
- }
- // remove the matching packet from the wait acknowledge buffer
- ci_pbWaitAckBuffer.RemovePacket(ulSequence,TRUE);
- // if the packet is waiting to be resent it's in the outgoing buffer, so remove it
- ci_pbOutputBuffer.RemovePacket(ulSequence,TRUE);
- pulAck++;
- slSize -= sizeof(ULONG);
- }
- ci_pbInputBuffer.RemovePacket(ppaPacket->pa_ulSequence,FALSE);
- bSomethingDone = TRUE;
- delete ppaPacket;
- // if the packet is reliable
- } else if (ppaPacket->pa_ubReliable & UDP_PACKET_RELIABLE) {
-
- // generate packet acknowledge (each reliable broadcast packet is acknowledged separately
- // because the broadcast interface can receive packets from any number of different addresses
- CPacket *ppaAckPacket = new CPacket;
- ppaAckPacket->pa_adrAddress.adr_ulAddress = ppaPacket->pa_adrAddress.adr_ulAddress;
- ppaAckPacket->pa_adrAddress.adr_uwPort = ppaPacket->pa_adrAddress.adr_uwPort;
- ppaAckPacket->WriteToPacket(&(ppaPacket->pa_ulSequence),sizeof(ULONG),UDP_PACKET_ACKNOWLEDGE,ci_ulSequence++,ppaPacket->pa_adrAddress.adr_uwID,sizeof(ULONG));
- ci_pbOutputBuffer.AppendPacket(*ppaAckPacket,TRUE);
- // report the packet info to the console
- if (net_bReportPackets == TRUE) {
- tvNow = _pTimer->GetHighPrecisionTimer();
- CPrintF("%lu: Acknowledging broadcast packet sequence %d\n",(ULONG) tvNow.GetMilliseconds(),ppaPacket->pa_ulSequence);
- }
- ci_pbInputBuffer.RemovePacket(ppaPacket->pa_ulSequence,FALSE);
- if (ci_pbReliableInputBuffer.InsertPacket(*ppaPacket,FALSE) == FALSE) {
- delete ppaPacket;
- }
- } else {
-
- // if the packet is unreliable, leave it in the input buffer
- // when it is needed, the message will be pulled from there
- // have to check for duplicates
-
- ci_pbInputBuffer.RemovePacket(ppaPacket->pa_ulSequence,FALSE);
- if (ppaPacket->pa_ulSequence > ci_pbInputBuffer.pb_ulLastSequenceOut &&
- !(ci_pbReliableInputBuffer.IsSequenceInBuffer(ppaPacket->pa_ulSequence))) {
- if (ci_pbInputBuffer.InsertPacket(*ppaPacket,FALSE) == FALSE) {
- delete ppaPacket;
- }
- } else {
- delete ppaPacket;
- }
- }
- }
- return TRUE;
-
- };
- // take a look at the wait acknowledge buffer and resend any packets that heve reached the timeout
- // if there is a packet that can't be sent sucessfully (RS_NOTATALL), signal it
- BOOL CClientInterface::UpdateOutputBuffers(void)
- {
- CPacket* ppaPacket;
- UBYTE ubRetry;
- // handle resends
- while (ci_pbWaitAckBuffer.pb_ulNumOfPackets > 0) {
- ppaPacket = ci_pbWaitAckBuffer.PeekFirstPacket();
-
- ubRetry = ppaPacket->CanRetry();
- switch (ubRetry) {
- // if it's time to retry sending the packet
- case RS_NOW: { ci_pbWaitAckBuffer.RemoveFirstPacket(FALSE);
- ci_pbOutputBuffer.Retry(*ppaPacket);
- break;
- }
- // if the packet cannot be sent now, no other packets can be sent, so exit
- case RS_NOTNOW: { return TRUE; }
- // if the packet has reached the retry limit - close the client's connection
- case RS_NOTATALL: { Clear();
- return FALSE;
- }
-
- }
- }
- return TRUE;
- };
- // get the next available packet from the output buffer
- CPacket* CClientInterface::GetPendingPacket(void)
- {
- CTimerValue tvNow = _pTimer->GetHighPrecisionTimer();
- if (ci_pbOutputBuffer.pb_ulNumOfPackets == 0) {
- return NULL;
- }
- CPacket* ppaPacket = ci_pbOutputBuffer.PeekFirstPacket();
-
- // if it's time to send the packet
- if (ppaPacket->pa_tvSendWhen <= tvNow) {
- ci_pbOutputBuffer.RemoveFirstPacket(FALSE);
- return ppaPacket;
- }
- return NULL;
- };
- // reads the expected size of current realiable message in the reliable input buffer
- SLONG CClientInterface::GetExpectedReliableSize(void)
- {
- if (ci_pbReliableInputBuffer.pb_ulNumOfPackets == 0) {
- return 0;
- }
- CPacket* ppaPacket = ci_pbReliableInputBuffer.PeekFirstPacket();
- return ppaPacket->pa_slTransferSize;
- };
- // reads the expected size of current realiable message in the reliable input buffer
- SLONG CClientInterface::GetCurrentReliableSize(void)
- {
- SLONG slSize;
- ci_pbReliableInputBuffer.CheckSequence(slSize);
- return slSize;
- };
|