123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #include "../../idlib/precompiled.h"
- #pragma hdrstop
- #include "MsgChannel.h"
- /*
- packet header
- -------------
- 2 bytes id
- 4 bytes outgoing sequence. high bit will be set if this is a fragmented message.
- 2 bytes optional fragment start byte if fragment bit is set.
- 2 bytes optional fragment length if fragment bit is set. if < FRAGMENT_SIZE, this is the last fragment.
- If the id is -1, the packet should be handled as an out-of-band
- message instead of as part of the message channel.
- All fragments will have the same sequence numbers.
- */
- #define MAX_PACKETLEN 1400 // max size of a network packet
- #define FRAGMENT_SIZE (MAX_PACKETLEN - 100)
- #define FRAGMENT_BIT (1<<31)
- idCVar net_channelShowPackets( "net_channelShowPackets", "0", CVAR_SYSTEM | CVAR_BOOL, "show all packets" );
- idCVar net_channelShowDrop( "net_channelShowDrop", "0", CVAR_SYSTEM | CVAR_BOOL, "show dropped packets" );
- /*
- ===============
- idMsgQueue::idMsgQueue
- ===============
- */
- idMsgQueue::idMsgQueue( void ) {
- Init( 0 );
- }
- /*
- ===============
- idMsgQueue::Init
- ===============
- */
- void idMsgQueue::Init( int sequence ) {
- first = last = sequence;
- startIndex = endIndex = 0;
- }
- /*
- ===============
- idMsgQueue::Add
- ===============
- */
- bool idMsgQueue::Add( const byte *data, const int size ) {
- if ( GetSpaceLeft() < size + 8 ) {
- return false;
- }
- int sequence = last;
- WriteShort( size );
- WriteLong( sequence );
- WriteData( data, size );
- last++;
- return true;
- }
- /*
- ===============
- idMsgQueue::Get
- ===============
- */
- bool idMsgQueue::Get( byte *data, int &size ) {
- if ( first == last ) {
- size = 0;
- return false;
- }
- int sequence;
- size = ReadShort();
- sequence = ReadLong();
- ReadData( data, size );
- assert( sequence == first );
- first++;
- return true;
- }
- /*
- ===============
- idMsgQueue::GetTotalSize
- ===============
- */
- int idMsgQueue::GetTotalSize( void ) const {
- if ( startIndex <= endIndex ) {
- return ( endIndex - startIndex );
- } else {
- return ( sizeof( buffer ) - startIndex + endIndex );
- }
- }
- /*
- ===============
- idMsgQueue::GetSpaceLeft
- ===============
- */
- int idMsgQueue::GetSpaceLeft( void ) const {
- if ( startIndex <= endIndex ) {
- return sizeof( buffer ) - ( endIndex - startIndex ) - 1;
- } else {
- return ( startIndex - endIndex ) - 1;
- }
- }
- /*
- ===============
- idMsgQueue::CopyToBuffer
- ===============
- */
- void idMsgQueue::CopyToBuffer( byte *buf ) const {
- if ( startIndex <= endIndex ) {
- memcpy( buf, buffer + startIndex, endIndex - startIndex );
- } else {
- memcpy( buf, buffer + startIndex, sizeof( buffer ) - startIndex );
- memcpy( buf + sizeof( buffer ) - startIndex, buffer, endIndex );
- }
- }
- /*
- ===============
- idMsgQueue::WriteByte
- ===============
- */
- void idMsgQueue::WriteByte( byte b ) {
- buffer[endIndex] = b;
- endIndex = ( endIndex + 1 ) & ( MAX_MSG_QUEUE_SIZE - 1 );
- }
- /*
- ===============
- idMsgQueue::ReadByte
- ===============
- */
- byte idMsgQueue::ReadByte( void ) {
- byte b = buffer[startIndex];
- startIndex = ( startIndex + 1 ) & ( MAX_MSG_QUEUE_SIZE - 1 );
- return b;
- }
- /*
- ===============
- idMsgQueue::WriteShort
- ===============
- */
- void idMsgQueue::WriteShort( int s ) {
- WriteByte( ( s >> 0 ) & 255 );
- WriteByte( ( s >> 8 ) & 255 );
- }
- /*
- ===============
- idMsgQueue::ReadShort
- ===============
- */
- int idMsgQueue::ReadShort( void ) {
- return ReadByte() | ( ReadByte() << 8 );
- }
- /*
- ===============
- idMsgQueue::WriteLong
- ===============
- */
- void idMsgQueue::WriteLong( int l ) {
- WriteByte( ( l >> 0 ) & 255 );
- WriteByte( ( l >> 8 ) & 255 );
- WriteByte( ( l >> 16 ) & 255 );
- WriteByte( ( l >> 24 ) & 255 );
- }
- /*
- ===============
- idMsgQueue::ReadLong
- ===============
- */
- int idMsgQueue::ReadLong( void ) {
- return ReadByte() | ( ReadByte() << 8 ) | ( ReadByte() << 16 ) | ( ReadByte() << 24 );
- }
- /*
- ===============
- idMsgQueue::WriteData
- ===============
- */
- void idMsgQueue::WriteData( const byte *data, const int size ) {
- for ( int i = 0; i < size; i++ ) {
- WriteByte( data[i] );
- }
- }
- /*
- ===============
- idMsgQueue::ReadData
- ===============
- */
- void idMsgQueue::ReadData( byte *data, const int size ) {
- if ( data ) {
- for ( int i = 0; i < size; i++ ) {
- data[i] = ReadByte();
- }
- } else {
- for ( int i = 0; i < size; i++ ) {
- ReadByte();
- }
- }
- }
- /*
- ===============
- idMsgChannel::idMsgChannel
- ===============
- */
- idMsgChannel::idMsgChannel() {
- id = -1;
- }
- /*
- ==============
- idMsgChannel::Init
- Opens a channel to a remote system.
- ==============
- */
- void idMsgChannel::Init( const netadr_t adr, const int id ) {
- this->remoteAddress = adr;
- this->id = id;
- this->maxRate = 50000;
- this->compressor = idCompressor::AllocRunLength_ZeroBased();
- lastSendTime = 0;
- lastDataBytes = 0;
- outgoingRateTime = 0;
- outgoingRateBytes = 0;
- incomingRateTime = 0;
- incomingRateBytes = 0;
- incomingReceivedPackets = 0.0f;
- incomingDroppedPackets = 0.0f;
- incomingPacketLossTime = 0;
- outgoingCompression = 0.0f;
- incomingCompression = 0.0f;
- outgoingSequence = 1;
- incomingSequence = 0;
- unsentFragments = false;
- unsentFragmentStart = 0;
- fragmentSequence = 0;
- fragmentLength = 0;
- reliableSend.Init( 1 );
- reliableReceive.Init( 0 );
- }
- /*
- ===============
- idMsgChannel::Shutdown
- ================
- */
- void idMsgChannel::Shutdown( void ) {
- delete compressor;
- compressor = NULL;
- }
- /*
- =================
- idMsgChannel::ResetRate
- =================
- */
- void idMsgChannel::ResetRate( void ) {
- lastSendTime = 0;
- lastDataBytes = 0;
- outgoingRateTime = 0;
- outgoingRateBytes = 0;
- incomingRateTime = 0;
- incomingRateBytes = 0;
- }
- /*
- =================
- idMsgChannel::ReadyToSend
- =================
- */
- bool idMsgChannel::ReadyToSend( const int time ) const {
- int deltaTime;
- if ( !maxRate ) {
- return true;
- }
- deltaTime = time - lastSendTime;
- if ( deltaTime > 1000 ) {
- return true;
- }
- return ( ( lastDataBytes - ( deltaTime * maxRate ) / 1000 ) <= 0 );
- }
- /*
- ===============
- idMsgChannel::WriteMessageData
- ================
- */
- void idMsgChannel::WriteMessageData( idBitMsg &out, const idBitMsg &msg ) {
- idBitMsg tmp;
- byte tmpBuf[MAX_MESSAGE_SIZE];
- tmp.Init( tmpBuf, sizeof( tmpBuf ) );
- // write acknowledgement of last received reliable message
- tmp.WriteLong( reliableReceive.GetLast() );
- // write reliable messages
- reliableSend.CopyToBuffer( tmp.GetData() + tmp.GetSize() );
- tmp.SetSize( tmp.GetSize() + reliableSend.GetTotalSize() );
- tmp.WriteShort( 0 );
- // write data
- tmp.WriteData( msg.GetData(), msg.GetSize() );
- // write message size
- out.WriteShort( tmp.GetSize() );
- // compress message
- idFile_BitMsg file( out );
- compressor->Init( &file, true, 3 );
- compressor->Write( tmp.GetData(), tmp.GetSize() );
- compressor->FinishCompress();
- outgoingCompression = compressor->GetCompressionRatio();
- }
- /*
- ===============
- idMsgChannel::ReadMessageData
- ================
- */
- bool idMsgChannel::ReadMessageData( idBitMsg &out, const idBitMsg &msg ) {
- int reliableAcknowledge, reliableMessageSize, reliableSequence;
- // read message size
- out.SetSize( msg.ReadShort() );
- // decompress message
- idFile_BitMsg file( msg );
- compressor->Init( &file, false, 3 );
- compressor->Read( out.GetData(), out.GetSize() );
- incomingCompression = compressor->GetCompressionRatio();
- out.BeginReading();
- // read acknowledgement of sent reliable messages
- reliableAcknowledge = out.ReadLong();
- // remove acknowledged reliable messages
- while( reliableSend.GetFirst() <= reliableAcknowledge ) {
- if ( !reliableSend.Get( NULL, reliableMessageSize ) ) {
- break;
- }
- }
- // read reliable messages
- reliableMessageSize = out.ReadShort();
- while( reliableMessageSize != 0 ) {
- if ( reliableMessageSize <= 0 || reliableMessageSize > out.GetSize() - out.GetReadCount() ) {
- common->Printf( "%s: bad reliable message\n", Sys_NetAdrToString( remoteAddress ) );
- return false;
- }
- reliableSequence = out.ReadLong();
- if ( reliableSequence == reliableReceive.GetLast() + 1 ) {
- reliableReceive.Add( out.GetData() + out.GetReadCount(), reliableMessageSize );
- }
- out.ReadData( NULL, reliableMessageSize );
- reliableMessageSize = out.ReadShort();
- }
- return true;
- }
- /*
- =================
- idMsgChannel::SendNextFragment
- Sends one fragment of the current message.
- =================
- */
- void idMsgChannel::SendNextFragment( idPort &port, const int time ) {
- idBitMsg msg;
- byte msgBuf[MAX_PACKETLEN];
- int fragLength;
- if ( remoteAddress.type == NA_BAD ) {
- return;
- }
- if ( !unsentFragments ) {
- return;
- }
- // write the packet
- msg.Init( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( id );
- msg.WriteLong( outgoingSequence | FRAGMENT_BIT );
- fragLength = FRAGMENT_SIZE;
- if ( unsentFragmentStart + fragLength > unsentMsg.GetSize() ) {
- fragLength = unsentMsg.GetSize() - unsentFragmentStart;
- }
- msg.WriteShort( unsentFragmentStart );
- msg.WriteShort( fragLength );
- msg.WriteData( unsentMsg.GetData() + unsentFragmentStart, fragLength );
- // send the packet
- port.SendPacket( remoteAddress, msg.GetData(), msg.GetSize() );
- // update rate control variables
- UpdateOutgoingRate( time, msg.GetSize() );
- if ( net_channelShowPackets.GetBool() ) {
- common->Printf( "%d send %4i : s = %i fragment = %i,%i\n", id, msg.GetSize(), outgoingSequence - 1, unsentFragmentStart, fragLength );
- }
- unsentFragmentStart += fragLength;
- // this exit condition is a little tricky, because a packet
- // that is exactly the fragment length still needs to send
- // a second packet of zero length so that the other side
- // can tell there aren't more to follow
- if ( unsentFragmentStart == unsentMsg.GetSize() && fragLength != FRAGMENT_SIZE ) {
- outgoingSequence++;
- unsentFragments = false;
- }
- }
- /*
- ===============
- idMsgChannel::SendMessage
- Sends a message to a connection, fragmenting if necessary
- A 0 length will still generate a packet.
- ================
- */
- int idMsgChannel::SendMessage( idPort &port, const int time, const idBitMsg &msg ) {
- int totalLength;
- if ( remoteAddress.type == NA_BAD ) {
- return -1;
- }
- if ( unsentFragments ) {
- common->Error( "idMsgChannel::SendMessage: called with unsent fragments left" );
- return -1;
- }
- totalLength = 4 + reliableSend.GetTotalSize() + 4 + msg.GetSize();
- if ( totalLength > MAX_MESSAGE_SIZE ) {
- common->Printf( "idMsgChannel::SendMessage: message too large, length = %i\n", totalLength );
- return -1;
- }
- unsentMsg.Init( unsentBuffer, sizeof( unsentBuffer ) );
- unsentMsg.BeginWriting();
- // fragment large messages
- if ( totalLength >= FRAGMENT_SIZE ) {
- unsentFragments = true;
- unsentFragmentStart = 0;
- // write out the message data
- WriteMessageData( unsentMsg, msg );
- // send the first fragment now
- SendNextFragment( port, time );
- return outgoingSequence;
- }
- // write the header
- unsentMsg.WriteShort( id );
- unsentMsg.WriteLong( outgoingSequence );
- // write out the message data
- WriteMessageData( unsentMsg, msg );
- // send the packet
- port.SendPacket( remoteAddress, unsentMsg.GetData(), unsentMsg.GetSize() );
- // update rate control variables
- UpdateOutgoingRate( time, unsentMsg.GetSize() );
- if ( net_channelShowPackets.GetBool() ) {
- common->Printf( "%d send %4i : s = %i ack = %i\n", id, unsentMsg.GetSize(), outgoingSequence - 1, incomingSequence );
- }
- outgoingSequence++;
- return ( outgoingSequence - 1 );
- }
- /*
- =================
- idMsgChannel::Process
- Returns false if the message should not be processed due to being out of order or a fragment.
- msg must be large enough to hold MAX_MESSAGE_SIZE, because if this is the final
- fragment of a multi-part message, the entire thing will be copied out.
- =================
- */
- bool idMsgChannel::Process( const netadr_t from, int time, idBitMsg &msg, int &sequence ) {
- int fragStart, fragLength, dropped;
- bool fragmented;
- idBitMsg fragMsg;
- // the IP port can't be used to differentiate them, because
- // some address translating routers periodically change UDP
- // port assignments
- if ( remoteAddress.port != from.port ) {
- common->Printf( "idMsgChannel::Process: fixing up a translated port\n" );
- remoteAddress.port = from.port;
- }
- // update incoming rate
- UpdateIncomingRate( time, msg.GetSize() );
- // get sequence numbers
- sequence = msg.ReadLong();
- // check for fragment information
- if ( sequence & FRAGMENT_BIT ) {
- sequence &= ~FRAGMENT_BIT;
- fragmented = true;
- } else {
- fragmented = false;
- }
- // read the fragment information
- if ( fragmented ) {
- fragStart = msg.ReadShort();
- fragLength = msg.ReadShort();
- } else {
- fragStart = 0; // stop warning message
- fragLength = 0;
- }
- if ( net_channelShowPackets.GetBool() ) {
- if ( fragmented ) {
- common->Printf( "%d recv %4i : s = %i fragment = %i,%i\n", id, msg.GetSize(), sequence, fragStart, fragLength );
- } else {
- common->Printf( "%d recv %4i : s = %i\n", id, msg.GetSize(), sequence );
- }
- }
- //
- // discard out of order or duplicated packets
- //
- if ( sequence <= incomingSequence ) {
- if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
- common->Printf( "%s: out of order packet %i at %i\n", Sys_NetAdrToString( remoteAddress ), sequence, incomingSequence );
- }
- return false;
- }
- //
- // dropped packets don't keep this message from being used
- //
- dropped = sequence - (incomingSequence+1);
- if ( dropped > 0 ) {
- if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
- common->Printf( "%s: dropped %i packets at %i\n", Sys_NetAdrToString( remoteAddress ), dropped, sequence );
- }
- UpdatePacketLoss( time, 0, dropped );
- }
- //
- // if the message is fragmented
- //
- if ( fragmented ) {
- // make sure we have the correct sequence number
- if ( sequence != fragmentSequence ) {
- fragmentSequence = sequence;
- fragmentLength = 0;
- }
- // if we missed a fragment, dump the message
- if ( fragStart != fragmentLength ) {
- if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
- common->Printf( "%s: dropped a message fragment at seq %d\n", Sys_NetAdrToString( remoteAddress ), sequence );
- }
- // we can still keep the part that we have so far,
- // so we don't need to clear fragmentLength
- UpdatePacketLoss( time, 0, 1 );
- return false;
- }
- // copy the fragment to the fragment buffer
- if ( fragLength < 0 || fragLength > msg.GetRemaingData() || fragmentLength + fragLength > sizeof( fragmentBuffer ) ) {
- if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
- common->Printf( "%s: illegal fragment length\n", Sys_NetAdrToString( remoteAddress ) );
- }
- UpdatePacketLoss( time, 0, 1 );
- return false;
- }
- memcpy( fragmentBuffer + fragmentLength, msg.GetData() + msg.GetReadCount(), fragLength );
- fragmentLength += fragLength;
- UpdatePacketLoss( time, 1, 0 );
- // if this wasn't the last fragment, don't process anything
- if ( fragLength == FRAGMENT_SIZE ) {
- return false;
- }
- } else {
- memcpy( fragmentBuffer, msg.GetData() + msg.GetReadCount(), msg.GetRemaingData() );
- fragmentLength = msg.GetRemaingData();
- UpdatePacketLoss( time, 1, 0 );
- }
- fragMsg.Init( fragmentBuffer, fragmentLength );
- fragMsg.SetSize( fragmentLength );
- fragMsg.BeginReading();
- incomingSequence = sequence;
- // read the message data
- if ( !ReadMessageData( msg, fragMsg ) ) {
- return false;
- }
- return true;
- }
- /*
- =================
- idMsgChannel::SendReliableMessage
- =================
- */
- bool idMsgChannel::SendReliableMessage( const idBitMsg &msg ) {
- bool result;
- assert( remoteAddress.type != NA_BAD );
- if ( remoteAddress.type == NA_BAD ) {
- return false;
- }
- result = reliableSend.Add( msg.GetData(), msg.GetSize() );
- if ( !result ) {
- common->Warning( "idMsgChannel::SendReliableMessage: overflowed" );
- return false;
- }
- return result;
- }
- /*
- =================
- idMsgChannel::GetReliableMessage
- =================
- */
- bool idMsgChannel::GetReliableMessage( idBitMsg &msg ) {
- int size;
- bool result;
- result = reliableReceive.Get( msg.GetData(), size );
- msg.SetSize( size );
- msg.BeginReading();
- return result;
- }
- /*
- ===============
- idMsgChannel::ClearReliableMessages
- ================
- */
- void idMsgChannel::ClearReliableMessages( void ) {
- reliableSend.Init( 1 );
- reliableReceive.Init( 0 );
- }
- /*
- =================
- idMsgChannel::UpdateOutgoingRate
- =================
- */
- void idMsgChannel::UpdateOutgoingRate( const int time, const int size ) {
- // update the outgoing rate control variables
- int deltaTime = time - lastSendTime;
- if ( deltaTime > 1000 ) {
- lastDataBytes = 0;
- } else {
- lastDataBytes -= ( deltaTime * maxRate ) / 1000;
- if ( lastDataBytes < 0 ) {
- lastDataBytes = 0;
- }
- }
- lastDataBytes += size;
- lastSendTime = time;
- // update outgoing rate variables
- if ( time - outgoingRateTime > 1000 ) {
- outgoingRateBytes -= outgoingRateBytes * ( time - outgoingRateTime - 1000 ) / 1000;
- if ( outgoingRateBytes < 0 ) {
- outgoingRateBytes = 0;
- }
- }
- outgoingRateTime = time - 1000;
- outgoingRateBytes += size;
- }
- /*
- =================
- idMsgChannel::UpdateIncomingRate
- =================
- */
- void idMsgChannel::UpdateIncomingRate( const int time, const int size ) {
- // update incoming rate variables
- if ( time - incomingRateTime > 1000 ) {
- incomingRateBytes -= incomingRateBytes * ( time - incomingRateTime - 1000 ) / 1000;
- if ( incomingRateBytes < 0 ) {
- incomingRateBytes = 0;
- }
- }
- incomingRateTime = time - 1000;
- incomingRateBytes += size;
- }
- /*
- =================
- idMsgChannel::UpdatePacketLoss
- =================
- */
- void idMsgChannel::UpdatePacketLoss( const int time, const int numReceived, const int numDropped ) {
- // update incoming packet loss variables
- if ( time - incomingPacketLossTime > 5000 ) {
- float scale = ( time - incomingPacketLossTime - 5000 ) * ( 1.0f / 5000.0f );
- incomingReceivedPackets -= incomingReceivedPackets * scale;
- if ( incomingReceivedPackets < 0.0f ) {
- incomingReceivedPackets = 0.0f;
- }
- incomingDroppedPackets -= incomingDroppedPackets * scale;
- if ( incomingDroppedPackets < 0.0f ) {
- incomingDroppedPackets = 0.0f;
- }
- }
- incomingPacketLossTime = time - 5000;
- incomingReceivedPackets += numReceived;
- incomingDroppedPackets += numDropped;
- }
- /*
- =================
- idMsgChannel::GetIncomingPacketLoss
- =================
- */
- float idMsgChannel::GetIncomingPacketLoss( void ) const {
- if ( incomingReceivedPackets == 0.0f && incomingDroppedPackets == 0.0f ) {
- return 0.0f;
- }
- return incomingDroppedPackets * 100.0f / ( incomingReceivedPackets + incomingDroppedPackets );
- }
|