]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/framework/async/MsgChannel.cpp
hello world
[icculus/iodoom3.git] / neo / framework / async / MsgChannel.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 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.
23
24 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.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "MsgChannel.h"
33
34 /*
35
36 packet header
37 -------------
38 2 bytes         id
39 4 bytes         outgoing sequence. high bit will be set if this is a fragmented message.
40 2 bytes         optional fragment start byte if fragment bit is set.
41 2 bytes         optional fragment length if fragment bit is set. if < FRAGMENT_SIZE, this is the last fragment.
42
43 If the id is -1, the packet should be handled as an out-of-band
44 message instead of as part of the message channel.
45
46 All fragments will have the same sequence numbers.
47
48 */
49
50
51 #define MAX_PACKETLEN                   1400            // max size of a network packet
52 #define FRAGMENT_SIZE                   (MAX_PACKETLEN - 100)
53 #define FRAGMENT_BIT                    (1<<31)
54
55 idCVar net_channelShowPackets( "net_channelShowPackets", "0", CVAR_SYSTEM | CVAR_BOOL, "show all packets" );
56 idCVar net_channelShowDrop( "net_channelShowDrop", "0", CVAR_SYSTEM | CVAR_BOOL, "show dropped packets" );
57
58 /*
59 ===============
60 idMsgQueue::idMsgQueue
61 ===============
62 */
63 idMsgQueue::idMsgQueue( void ) {
64         Init( 0 );
65 }
66
67 /*
68 ===============
69 idMsgQueue::Init
70 ===============
71 */
72 void idMsgQueue::Init( int sequence ) {
73         first = last = sequence;
74         startIndex = endIndex = 0;
75 }
76
77 /*
78 ===============
79 idMsgQueue::Add
80 ===============
81 */
82 bool idMsgQueue::Add( const byte *data, const int size ) {
83         if ( GetSpaceLeft() < size + 8 ) {
84                 return false;
85         }
86         int sequence = last;
87         WriteShort( size );
88         WriteLong( sequence );
89         WriteData( data, size );
90         last++;
91         return true;
92 }
93
94 /*
95 ===============
96 idMsgQueue::Get
97 ===============
98 */
99 bool idMsgQueue::Get( byte *data, int &size ) {
100         if ( first == last ) {
101                 size = 0;
102                 return false;
103         }
104         int sequence;
105         size = ReadShort();
106         sequence = ReadLong();
107         ReadData( data, size );
108         assert( sequence == first );
109         first++;
110         return true;
111 }
112
113 /*
114 ===============
115 idMsgQueue::GetTotalSize
116 ===============
117 */
118 int idMsgQueue::GetTotalSize( void ) const {
119         if ( startIndex <= endIndex ) {
120                 return ( endIndex - startIndex );
121         } else {
122                 return ( sizeof( buffer ) - startIndex + endIndex );
123         }
124 }
125
126 /*
127 ===============
128 idMsgQueue::GetSpaceLeft
129 ===============
130 */
131 int idMsgQueue::GetSpaceLeft( void ) const {
132         if ( startIndex <= endIndex ) {
133                 return sizeof( buffer ) - ( endIndex - startIndex ) - 1;
134         } else {
135                 return ( startIndex - endIndex ) - 1;
136         }
137 }
138
139 /*
140 ===============
141 idMsgQueue::CopyToBuffer
142 ===============
143 */
144 void idMsgQueue::CopyToBuffer( byte *buf ) const {
145         if ( startIndex <= endIndex ) {
146                 memcpy( buf, buffer + startIndex, endIndex - startIndex );
147         } else {
148                 memcpy( buf, buffer + startIndex, sizeof( buffer ) - startIndex );
149                 memcpy( buf + sizeof( buffer ) - startIndex, buffer, endIndex );
150         }
151 }
152
153 /*
154 ===============
155 idMsgQueue::WriteByte
156 ===============
157 */
158 void idMsgQueue::WriteByte( byte b ) {
159         buffer[endIndex] = b;
160         endIndex = ( endIndex + 1 ) & ( MAX_MSG_QUEUE_SIZE - 1 );
161 }
162
163 /*
164 ===============
165 idMsgQueue::ReadByte
166 ===============
167 */
168 byte idMsgQueue::ReadByte( void ) {
169         byte b = buffer[startIndex];
170         startIndex = ( startIndex + 1 ) & ( MAX_MSG_QUEUE_SIZE - 1 );
171         return b;
172 }
173
174 /*
175 ===============
176 idMsgQueue::WriteShort
177 ===============
178 */
179 void idMsgQueue::WriteShort( int s ) {
180         WriteByte( ( s >>  0 ) & 255 );
181         WriteByte( ( s >>  8 ) & 255 );
182 }
183
184 /*
185 ===============
186 idMsgQueue::ReadShort
187 ===============
188 */
189 int idMsgQueue::ReadShort( void ) {
190         return ReadByte() | ( ReadByte() << 8 );
191 }
192
193 /*
194 ===============
195 idMsgQueue::WriteLong
196 ===============
197 */
198 void idMsgQueue::WriteLong( int l ) {
199         WriteByte( ( l >>  0 ) & 255 );
200         WriteByte( ( l >>  8 ) & 255 );
201         WriteByte( ( l >> 16 ) & 255 );
202         WriteByte( ( l >> 24 ) & 255 );
203 }
204
205 /*
206 ===============
207 idMsgQueue::ReadLong
208 ===============
209 */
210 int idMsgQueue::ReadLong( void ) {
211         return ReadByte() | ( ReadByte() << 8 ) | ( ReadByte() << 16 ) | ( ReadByte() << 24 );
212 }
213
214 /*
215 ===============
216 idMsgQueue::WriteData
217 ===============
218 */
219 void idMsgQueue::WriteData( const byte *data, const int size ) {
220         for ( int i = 0; i < size; i++ ) {
221                 WriteByte( data[i] );
222         }
223 }
224
225 /*
226 ===============
227 idMsgQueue::ReadData
228 ===============
229 */
230 void idMsgQueue::ReadData( byte *data, const int size ) {
231         if ( data ) {
232                 for ( int i = 0; i < size; i++ ) {
233                         data[i] = ReadByte();
234                 }
235         } else {
236                 for ( int i = 0; i < size; i++ ) {
237                         ReadByte();
238                 }
239         }
240 }
241
242
243 /*
244 ===============
245 idMsgChannel::idMsgChannel
246 ===============
247 */
248 idMsgChannel::idMsgChannel() {
249         id = -1;
250 }
251
252 /*
253 ==============
254 idMsgChannel::Init
255
256   Opens a channel to a remote system.
257 ==============
258 */
259 void idMsgChannel::Init( const netadr_t adr, const int id ) {
260         this->remoteAddress = adr;
261         this->id = id;
262         this->maxRate = 50000;
263         this->compressor = idCompressor::AllocRunLength_ZeroBased();
264
265         lastSendTime = 0;
266         lastDataBytes = 0;
267         outgoingRateTime = 0;
268         outgoingRateBytes = 0;
269         incomingRateTime = 0;
270         incomingRateBytes = 0;
271         incomingReceivedPackets = 0.0f;
272         incomingDroppedPackets = 0.0f;
273         incomingPacketLossTime = 0;
274         outgoingCompression = 0.0f;
275         incomingCompression = 0.0f;
276         outgoingSequence = 1;
277         incomingSequence = 0;
278         unsentFragments = false;
279         unsentFragmentStart = 0;
280         fragmentSequence = 0;
281         fragmentLength = 0;
282         reliableSend.Init( 1 );
283         reliableReceive.Init( 0 );
284 }
285
286 /*
287 ===============
288 idMsgChannel::Shutdown
289 ================
290 */
291 void idMsgChannel::Shutdown( void ) {
292         delete compressor;
293         compressor = NULL;
294 }
295
296 /*
297 =================
298 idMsgChannel::ResetRate
299 =================
300 */
301 void idMsgChannel::ResetRate( void ) {
302         lastSendTime = 0;
303         lastDataBytes = 0;
304         outgoingRateTime = 0;
305         outgoingRateBytes = 0;
306         incomingRateTime = 0;
307         incomingRateBytes = 0;
308 }
309
310 /*
311 =================
312 idMsgChannel::ReadyToSend
313 =================
314 */
315 bool idMsgChannel::ReadyToSend( const int time ) const {
316         int deltaTime;
317
318         if ( !maxRate ) {
319                 return true;
320         }
321         deltaTime = time - lastSendTime;
322         if ( deltaTime > 1000 ) {
323                 return true;
324         }
325         return ( ( lastDataBytes - ( deltaTime * maxRate ) / 1000 ) <= 0 );
326 }
327
328 /*
329 ===============
330 idMsgChannel::WriteMessageData
331 ================
332 */
333 void idMsgChannel::WriteMessageData( idBitMsg &out, const idBitMsg &msg ) {
334         idBitMsg tmp;
335         byte tmpBuf[MAX_MESSAGE_SIZE];
336
337         tmp.Init( tmpBuf, sizeof( tmpBuf ) );
338
339         // write acknowledgement of last received reliable message
340         tmp.WriteLong( reliableReceive.GetLast() );
341
342         // write reliable messages
343         reliableSend.CopyToBuffer( tmp.GetData() + tmp.GetSize() );
344         tmp.SetSize( tmp.GetSize() + reliableSend.GetTotalSize() );
345         tmp.WriteShort( 0 );
346
347         // write data
348         tmp.WriteData( msg.GetData(), msg.GetSize() );
349
350         // write message size
351         out.WriteShort( tmp.GetSize() );
352
353         // compress message
354         idFile_BitMsg file( out );
355         compressor->Init( &file, true, 3 );
356         compressor->Write( tmp.GetData(), tmp.GetSize() );
357         compressor->FinishCompress();
358         outgoingCompression = compressor->GetCompressionRatio();
359 }
360
361 /*
362 ===============
363 idMsgChannel::ReadMessageData
364 ================
365 */
366 bool idMsgChannel::ReadMessageData( idBitMsg &out, const idBitMsg &msg ) {
367         int reliableAcknowledge, reliableMessageSize, reliableSequence;
368
369         // read message size
370         out.SetSize( msg.ReadShort() );
371
372         // decompress message
373         idFile_BitMsg file( msg );
374         compressor->Init( &file, false, 3 );
375         compressor->Read( out.GetData(), out.GetSize() );
376         incomingCompression = compressor->GetCompressionRatio();
377         out.BeginReading();
378
379         // read acknowledgement of sent reliable messages
380         reliableAcknowledge = out.ReadLong();
381
382         // remove acknowledged reliable messages
383         while( reliableSend.GetFirst() <= reliableAcknowledge ) {
384                 if ( !reliableSend.Get( NULL, reliableMessageSize ) ) {
385                         break;
386                 }
387         }
388
389         // read reliable messages
390         reliableMessageSize = out.ReadShort();
391         while( reliableMessageSize != 0 ) {
392                 if ( reliableMessageSize <= 0 || reliableMessageSize > out.GetSize() - out.GetReadCount() ) {
393                         common->Printf( "%s: bad reliable message\n", Sys_NetAdrToString( remoteAddress ) );
394                         return false;
395                 }
396                 reliableSequence = out.ReadLong();
397                 if ( reliableSequence == reliableReceive.GetLast() + 1 ) {
398                         reliableReceive.Add( out.GetData() + out.GetReadCount(), reliableMessageSize );
399                 }
400                 out.ReadData( NULL, reliableMessageSize );
401                 reliableMessageSize = out.ReadShort();
402         }
403
404         return true;
405 }
406
407 /*
408 =================
409 idMsgChannel::SendNextFragment
410
411   Sends one fragment of the current message.
412 =================
413 */
414 void idMsgChannel::SendNextFragment( idPort &port, const int time ) {
415         idBitMsg        msg;
416         byte            msgBuf[MAX_PACKETLEN];
417         int                     fragLength;
418
419         if ( remoteAddress.type == NA_BAD ) {
420                 return;
421         }
422
423         if ( !unsentFragments ) {
424                 return;
425         }
426
427         // write the packet
428         msg.Init( msgBuf, sizeof( msgBuf ) );
429         msg.WriteShort( id );
430         msg.WriteLong( outgoingSequence | FRAGMENT_BIT );
431
432         fragLength = FRAGMENT_SIZE;
433         if ( unsentFragmentStart + fragLength > unsentMsg.GetSize() ) {
434                 fragLength = unsentMsg.GetSize() - unsentFragmentStart;
435         }
436
437         msg.WriteShort( unsentFragmentStart );
438         msg.WriteShort( fragLength );
439         msg.WriteData( unsentMsg.GetData() + unsentFragmentStart, fragLength );
440
441         // send the packet
442         port.SendPacket( remoteAddress, msg.GetData(), msg.GetSize() );
443
444         // update rate control variables
445         UpdateOutgoingRate( time, msg.GetSize() );
446
447         if ( net_channelShowPackets.GetBool() ) {
448                 common->Printf( "%d send %4i : s = %i fragment = %i,%i\n", id, msg.GetSize(), outgoingSequence - 1, unsentFragmentStart, fragLength );
449         }
450
451         unsentFragmentStart += fragLength;
452
453         // this exit condition is a little tricky, because a packet
454         // that is exactly the fragment length still needs to send
455         // a second packet of zero length so that the other side
456         // can tell there aren't more to follow
457         if ( unsentFragmentStart == unsentMsg.GetSize() && fragLength != FRAGMENT_SIZE ) {
458                 outgoingSequence++;
459                 unsentFragments = false;
460         }
461 }
462
463 /*
464 ===============
465 idMsgChannel::SendMessage
466
467   Sends a message to a connection, fragmenting if necessary
468   A 0 length will still generate a packet.
469 ================
470 */
471 int idMsgChannel::SendMessage( idPort &port, const int time, const idBitMsg &msg ) {
472         int totalLength;
473
474         if ( remoteAddress.type == NA_BAD ) {
475                 return -1;
476         }
477
478         if ( unsentFragments ) {
479                 common->Error( "idMsgChannel::SendMessage: called with unsent fragments left" );
480                 return -1;
481         }
482
483         totalLength = 4 + reliableSend.GetTotalSize() + 4 + msg.GetSize();
484
485         if ( totalLength > MAX_MESSAGE_SIZE ) {
486                 common->Printf( "idMsgChannel::SendMessage: message too large, length = %i\n", totalLength );
487                 return -1;
488         }
489
490         unsentMsg.Init( unsentBuffer, sizeof( unsentBuffer ) );
491         unsentMsg.BeginWriting();
492
493         // fragment large messages
494         if ( totalLength >= FRAGMENT_SIZE ) {
495                 unsentFragments = true;
496                 unsentFragmentStart = 0;
497
498                 // write out the message data
499                 WriteMessageData( unsentMsg, msg );
500
501                 // send the first fragment now
502                 SendNextFragment( port, time );
503
504                 return outgoingSequence;
505         }
506
507         // write the header
508         unsentMsg.WriteShort( id );
509         unsentMsg.WriteLong( outgoingSequence );
510
511         // write out the message data
512         WriteMessageData( unsentMsg, msg );
513
514         // send the packet
515         port.SendPacket( remoteAddress, unsentMsg.GetData(), unsentMsg.GetSize() );
516
517         // update rate control variables
518         UpdateOutgoingRate( time, unsentMsg.GetSize() );
519
520         if ( net_channelShowPackets.GetBool() ) {
521                 common->Printf( "%d send %4i : s = %i ack = %i\n", id, unsentMsg.GetSize(), outgoingSequence - 1, incomingSequence );
522         }
523
524         outgoingSequence++;
525
526         return ( outgoingSequence - 1 );
527 }
528
529 /*
530 =================
531 idMsgChannel::Process
532
533   Returns false if the message should not be processed due to being out of order or a fragment.
534
535   msg must be large enough to hold MAX_MESSAGE_SIZE, because if this is the final
536   fragment of a multi-part message, the entire thing will be copied out.
537 =================
538 */
539 bool idMsgChannel::Process( const netadr_t from, int time, idBitMsg &msg, int &sequence ) {
540         int                     fragStart, fragLength, dropped;
541         bool            fragmented;
542         idBitMsg        fragMsg;
543
544         // the IP port can't be used to differentiate them, because
545         // some address translating routers periodically change UDP
546         // port assignments
547         if ( remoteAddress.port != from.port ) {
548                 common->Printf( "idMsgChannel::Process: fixing up a translated port\n" );
549                 remoteAddress.port = from.port;
550         }
551
552         // update incoming rate
553         UpdateIncomingRate( time, msg.GetSize() );
554
555         // get sequence numbers
556         sequence = msg.ReadLong();
557
558         // check for fragment information
559         if ( sequence & FRAGMENT_BIT ) {
560                 sequence &= ~FRAGMENT_BIT;
561                 fragmented = true;
562         } else {
563                 fragmented = false;
564         }
565
566         // read the fragment information
567         if ( fragmented ) {
568                 fragStart = msg.ReadShort();
569                 fragLength = msg.ReadShort();
570         } else {
571                 fragStart = 0;          // stop warning message
572                 fragLength = 0;
573         }
574
575         if ( net_channelShowPackets.GetBool() ) {
576                 if ( fragmented ) {
577                         common->Printf( "%d recv %4i : s = %i fragment = %i,%i\n", id, msg.GetSize(), sequence, fragStart, fragLength );
578                 } else {
579                         common->Printf( "%d recv %4i : s = %i\n", id, msg.GetSize(), sequence );
580                 }
581         }
582
583         //
584         // discard out of order or duplicated packets
585         //
586         if ( sequence <= incomingSequence ) {
587                 if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
588                         common->Printf( "%s: out of order packet %i at %i\n", Sys_NetAdrToString( remoteAddress ),  sequence, incomingSequence );
589                 }
590                 return false;
591         }
592
593         //
594         // dropped packets don't keep this message from being used
595         //
596         dropped = sequence - (incomingSequence+1);
597         if ( dropped > 0 ) {
598                 if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
599                         common->Printf( "%s: dropped %i packets at %i\n", Sys_NetAdrToString( remoteAddress ), dropped, sequence );
600                 }
601                 UpdatePacketLoss( time, 0, dropped );
602         }
603
604         //
605         // if the message is fragmented
606         //
607         if ( fragmented ) {
608                 // make sure we have the correct sequence number
609                 if ( sequence != fragmentSequence ) {
610                         fragmentSequence = sequence;
611                         fragmentLength = 0;
612                 }
613
614                 // if we missed a fragment, dump the message
615                 if ( fragStart != fragmentLength ) {
616                         if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
617                                 common->Printf( "%s: dropped a message fragment at seq %d\n", Sys_NetAdrToString( remoteAddress ), sequence );
618                         }
619                         // we can still keep the part that we have so far,
620                         // so we don't need to clear fragmentLength
621                         UpdatePacketLoss( time, 0, 1 );
622                         return false;
623                 }
624
625                 // copy the fragment to the fragment buffer
626                 if ( fragLength < 0 || fragLength > msg.GetRemaingData() || fragmentLength + fragLength > sizeof( fragmentBuffer ) ) {
627                         if ( net_channelShowDrop.GetBool() || net_channelShowPackets.GetBool() ) {
628                                 common->Printf( "%s: illegal fragment length\n", Sys_NetAdrToString( remoteAddress ) );
629                         }
630                         UpdatePacketLoss( time, 0, 1 );
631                         return false;
632                 }
633
634                 memcpy( fragmentBuffer + fragmentLength, msg.GetData() + msg.GetReadCount(), fragLength );
635
636                 fragmentLength += fragLength;
637
638                 UpdatePacketLoss( time, 1, 0 );
639
640                 // if this wasn't the last fragment, don't process anything
641                 if ( fragLength == FRAGMENT_SIZE ) {
642                         return false;
643                 }
644
645         } else {
646                 memcpy( fragmentBuffer, msg.GetData() + msg.GetReadCount(), msg.GetRemaingData() );
647                 fragmentLength = msg.GetRemaingData();
648                 UpdatePacketLoss( time, 1, 0 );
649         }
650
651         fragMsg.Init( fragmentBuffer, fragmentLength );
652         fragMsg.SetSize( fragmentLength );
653         fragMsg.BeginReading();
654
655         incomingSequence = sequence;
656
657         // read the message data
658         if ( !ReadMessageData( msg, fragMsg ) ) {
659                 return false;
660         }
661
662         return true;
663 }
664
665 /*
666 =================
667 idMsgChannel::SendReliableMessage
668 =================
669 */
670 bool idMsgChannel::SendReliableMessage( const idBitMsg &msg ) {
671         bool result;
672
673         assert( remoteAddress.type != NA_BAD );
674         if ( remoteAddress.type == NA_BAD ) {
675                 return false;
676         }
677         result = reliableSend.Add( msg.GetData(), msg.GetSize() );
678         if ( !result ) {
679                 common->Warning( "idMsgChannel::SendReliableMessage: overflowed" );
680                 return false;
681         }
682         return result;
683 }
684
685 /*
686 =================
687 idMsgChannel::GetReliableMessage
688 =================
689 */
690 bool idMsgChannel::GetReliableMessage( idBitMsg &msg ) {
691         int size;
692         bool result;
693
694         result = reliableReceive.Get( msg.GetData(), size );
695         msg.SetSize( size );
696         msg.BeginReading();
697         return result;
698 }
699
700 /*
701 ===============
702 idMsgChannel::ClearReliableMessages
703 ================
704 */
705 void idMsgChannel::ClearReliableMessages( void ) {
706         reliableSend.Init( 1 );
707         reliableReceive.Init( 0 );
708 }
709
710 /*
711 =================
712 idMsgChannel::UpdateOutgoingRate
713 =================
714 */
715 void idMsgChannel::UpdateOutgoingRate( const int time, const int size ) {
716         // update the outgoing rate control variables
717         int deltaTime = time - lastSendTime;
718         if ( deltaTime > 1000 ) {
719                 lastDataBytes = 0;
720         } else {
721                 lastDataBytes -= ( deltaTime * maxRate ) / 1000;
722                 if ( lastDataBytes < 0 ) {
723                         lastDataBytes = 0;
724                 }
725         }
726         lastDataBytes += size;
727         lastSendTime = time;
728
729         // update outgoing rate variables
730         if ( time - outgoingRateTime > 1000 ) {
731                 outgoingRateBytes -= outgoingRateBytes * ( time - outgoingRateTime - 1000 ) / 1000;
732                 if ( outgoingRateBytes < 0 ) {
733                         outgoingRateBytes = 0;
734                 }
735         }
736         outgoingRateTime = time - 1000;
737         outgoingRateBytes += size;
738 }
739
740 /*
741 =================
742 idMsgChannel::UpdateIncomingRate
743 =================
744 */
745 void idMsgChannel::UpdateIncomingRate( const int time, const int size ) {
746         // update incoming rate variables
747         if ( time - incomingRateTime > 1000 ) {
748                 incomingRateBytes -= incomingRateBytes * ( time - incomingRateTime - 1000 ) / 1000;
749                 if ( incomingRateBytes < 0 ) {
750                         incomingRateBytes = 0;
751                 }
752         }
753         incomingRateTime = time - 1000;
754         incomingRateBytes += size;
755 }
756
757 /*
758 =================
759 idMsgChannel::UpdatePacketLoss
760 =================
761 */
762 void idMsgChannel::UpdatePacketLoss( const int time, const int numReceived, const int numDropped ) {
763         // update incoming packet loss variables
764         if ( time - incomingPacketLossTime > 5000 ) {
765                 float scale = ( time - incomingPacketLossTime - 5000 ) * ( 1.0f / 5000.0f );
766                 incomingReceivedPackets -= incomingReceivedPackets * scale;
767                 if ( incomingReceivedPackets < 0.0f ) {
768                         incomingReceivedPackets = 0.0f;
769                 }
770                 incomingDroppedPackets -= incomingDroppedPackets * scale;
771                 if ( incomingDroppedPackets < 0.0f ) {
772                         incomingDroppedPackets = 0.0f;
773                 }
774         }
775         incomingPacketLossTime = time - 5000;
776         incomingReceivedPackets += numReceived;
777         incomingDroppedPackets += numDropped;
778 }
779
780 /*
781 =================
782 idMsgChannel::GetIncomingPacketLoss
783 =================
784 */
785 float idMsgChannel::GetIncomingPacketLoss( void ) const {
786         if ( incomingReceivedPackets == 0.0f && incomingDroppedPackets == 0.0f ) {
787                 return 0.0f;
788         }
789         return incomingDroppedPackets * 100.0f / ( incomingReceivedPackets + incomingDroppedPackets );
790 }