Serious-Engine/Sources/Engine/Network/ClientInterface.cpp

705 lines
24 KiB
C++
Raw Normal View History

2016-03-12 01:20:51 +01:00
/* 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. */
2016-03-11 14:57:17 +01:00
#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;
2016-03-12 01:20:51 +01:00
};