1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP 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 RTCW MP 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 RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP 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 RTCW MP 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
30 #include "server.h"
31
32 #ifdef USE_VOIP
33 cvar_t *sv_voip;
34 cvar_t *sv_voipProtocol;
35 #endif
36
37 serverStatic_t svs; // persistant server info
38 server_t sv; // local server
39 vm_t *gvm = NULL; // game virtual machine
40
41 #ifdef UPDATE_SERVER
42 versionMapping_t versionMap[MAX_UPDATE_VERSIONS];
43 int numVersions = 0;
44 #endif
45
46 cvar_t *sv_fps = NULL; // time rate for running non-clients
47 cvar_t *sv_timeout; // seconds without any message
48 cvar_t *sv_zombietime; // seconds to sink messages after disconnect
49 cvar_t *sv_rconPassword; // password for remote server commands
50 cvar_t *sv_privatePassword; // password for the privateClient slots
51 cvar_t *sv_allowDownload;
52 cvar_t *sv_maxclients;
53
54 cvar_t *sv_privateClients; // number of clients reserved for password
55 cvar_t *sv_hostname;
56 cvar_t *sv_master[MAX_MASTER_SERVERS]; // master server ip address
57 cvar_t *sv_reconnectlimit; // minimum seconds between connect messages
58 cvar_t *sv_showloss; // report when usercmds are lost
59 cvar_t *sv_padPackets; // add nop bytes to messages
60 cvar_t *sv_killserver; // menu system can set to 1 to shut server down
61 cvar_t *sv_mapname;
62 cvar_t *sv_mapChecksum;
63 cvar_t *sv_serverid;
64 cvar_t *sv_minRate;
65 cvar_t *sv_maxRate;
66 cvar_t *sv_dlRate;
67 cvar_t *sv_minPing;
68 cvar_t *sv_maxPing;
69 cvar_t *sv_gametype;
70 cvar_t *sv_pure;
71 cvar_t *sv_floodProtect;
72 cvar_t *sv_allowAnonymous;
73 cvar_t *sv_lanForceRate; // TTimo - dedicated 1 (LAN) server forces local client rates to 99999 (bug #491)
74 cvar_t *sv_onlyVisibleClients; // DHM - Nerve
75
76 cvar_t *sv_forceNameUniq;
77
78 cvar_t *sv_friendlyFire; // NERVE - SMF
79 cvar_t *sv_maxlives; // NERVE - SMF
80 cvar_t *sv_tourney; // NERVE - SMF
81
82 cvar_t *sv_dl_maxRate;
83
84 // Rafael gameskill
85 cvar_t *sv_gameskill;
86 // done
87
88 cvar_t *sv_showAverageBPS; // NERVE - SMF - net debugging
89
90 cvar_t *sv_banFile;
91
92 serverBan_t serverBans[SERVER_MAXBANS];
93 int serverBansCount = 0;
94
95 #if defined ANTIWALLHACK
96 cvar_t *awh_active;
97 cvar_t *awh_bbox_horz;
98 cvar_t *awh_bbox_vert;
99 #endif
100
101 /*
102 =============================================================================
103
104 EVENT MESSAGES
105
106 =============================================================================
107 */
108
109 /*
110 ===============
111 SV_ExpandNewlines
112
113 Converts newlines to "\n" so a line prints nicer
114 ===============
115 */
SV_ExpandNewlines(char * in)116 static char *SV_ExpandNewlines( char *in ) {
117 static char string[1024];
118 int l;
119
120 l = 0;
121 while ( *in && l < sizeof( string ) - 3 ) {
122 if ( *in == '\n' ) {
123 string[l++] = '\\';
124 string[l++] = 'n';
125 } else {
126 // NERVE - SMF - HACK - strip out localization tokens before string command is displayed in syscon window
127 if ( !Q_strncmp( in, "[lon]", 5 ) || !Q_strncmp( in, "[lof]", 5 ) ) {
128 in += 5;
129 continue;
130 }
131
132 string[l++] = *in;
133 }
134 in++;
135 }
136 string[l] = 0;
137
138 return string;
139 }
140
141 /*
142 ======================
143 SV_AddServerCommand
144
145 The given command will be transmitted to the client, and is guaranteed to
146 not have future snapshot_t executed before it is executed
147 ======================
148 */
SV_AddServerCommand(client_t * client,const char * cmd)149 void SV_AddServerCommand( client_t *client, const char *cmd ) {
150 int index, i;
151
152 // do not send commands until the gamestate has been sent
153 if( client->state < CS_PRIMED )
154 return;
155
156 client->reliableSequence++;
157 // if we would be losing an old command that hasn't been acknowledged,
158 // we must drop the connection
159 // we check == instead of >= so a broadcast print added by SV_DropClient()
160 // doesn't cause a recursive drop client
161 if ( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 ) {
162 Com_Printf( "===== pending server commands =====\n" );
163 for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) {
164 Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & ( MAX_RELIABLE_COMMANDS - 1 ) ] );
165 }
166 Com_Printf( "cmd %5d: %s\n", i, cmd );
167 SV_DropClient( client, "Server command overflow" );
168 return;
169 }
170 index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
171 Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
172 }
173
174
175 /*
176 =================
177 SV_SendServerCommand
178
179 Sends a reliable command string to be interpreted by
180 the client game module: "cp", "print", "chat", etc
181 A NULL client will broadcast to all clients
182 =================
183 */
SV_SendServerCommand(client_t * cl,const char * fmt,...)184 void QDECL SV_SendServerCommand( client_t *cl, const char *fmt, ... ) {
185 va_list argptr;
186 byte message[MAX_MSGLEN];
187 client_t *client;
188 int j;
189
190 va_start( argptr,fmt );
191 Q_vsnprintf( (char *)message, sizeof( message ), fmt, argptr );
192 va_end( argptr );
193
194 // do not forward server command messages that would be too big to clients
195 // ( q3infoboom / q3msgboom stuff )
196 if ( strlen( (char *)message ) > 1022 ) {
197 return;
198 }
199
200 if ( cl != NULL ) {
201 SV_AddServerCommand( cl, (char *)message );
202 return;
203 }
204
205 // hack to echo broadcast prints to console
206 if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5 ) ) {
207 Com_Printf( "broadcast: %s\n", SV_ExpandNewlines( (char *)message ) );
208 }
209
210 // send the data to all relevent clients
211 for ( j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++ ) {
212 // Ridah, don't need to send messages to AI
213 if ( client->gentity && client->gentity->r.svFlags & SVF_CASTAI ) {
214 continue;
215 }
216 // done.
217 SV_AddServerCommand( client, (char *)message );
218 }
219 }
220
221
222 /*
223 ==============================================================================
224
225 MASTER SERVER FUNCTIONS
226
227 ==============================================================================
228 */
229
230 /*
231 ================
232 SV_MasterHeartbeat
233
234 Send a message to the masters every few minutes to
235 let it know we are alive, and log information.
236 We will also have a heartbeat sent when a server
237 changes from empty to non-empty, and full to non-full,
238 but not on every player enter or exit.
239 ================
240 */
241 #define HEARTBEAT_MSEC 300 * 1000
242 #define MASTERDNS_MSEC 24 * 60 * 60 * 1000
SV_MasterHeartbeat(const char * message)243 void SV_MasterHeartbeat(const char *message)
244 {
245 static netadr_t adr[MAX_MASTER_SERVERS][2]; // [2] for v4 and v6 address for the same address string.
246 int i;
247 int res;
248 int netenabled;
249 static qboolean firstRes = qtrue;
250
251 // DHM - Nerve :: Update Server doesn't send heartbeat
252 #ifdef UPDATE_SERVER
253 return;
254 #endif
255
256 netenabled = Cvar_VariableIntegerValue("net_enabled");
257
258 // "dedicated 1" is for lan play, "dedicated 2" is for inet public play
259 if (!com_dedicated || com_dedicated->integer != 2 || !(netenabled & (NET_ENABLEV4 | NET_ENABLEV6)))
260 return; // only dedicated servers send heartbeats
261
262 // if not time yet, don't send anything
263 if ( svs.time < svs.nextHeartbeatTime )
264 return;
265
266 if ( !Q_stricmp( com_gamename->string, LEGACY_MASTER_GAMENAME ) )
267 message = LEGACY_HEARTBEAT_FOR_MASTER;
268
269 svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC;
270
271 // send to group masters
272 for (i = 0; i < MAX_MASTER_SERVERS; i++)
273 {
274 if(!sv_master[i]->string[0])
275 continue;
276
277 // if server resolves on first attempt, attempt resolution on subsequent heartbeats
278 // if server does not resolve on first attempt, do not attempt another dns lookup for 24 hours
279 if(sv_master[i]->modified || svs.time > svs.masterResolveTime[i])
280 {
281 sv_master[i]->modified = qfalse;
282
283 if(svs.time > svs.masterResolveTime[i])
284 {
285 svs.masterResolveTime[i] = svs.time + MASTERDNS_MSEC;
286 firstRes = qtrue;
287 }
288
289 if(netenabled & NET_ENABLEV4)
290 {
291 if(firstRes || adr[i][0].type != NA_BAD) {
292 Com_Printf("Resolving %s (IPv4)\n", sv_master[i]->string);
293 res = NET_StringToAdr(sv_master[i]->string, &adr[i][0], NA_IP);
294
295 if(res == 2)
296 {
297 // if no port was specified, use the default master port
298 adr[i][0].port = BigShort(PORT_MASTER);
299 }
300
301 if(res) {
302 Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i][0]));
303
304 if(adr[i][0].type != NA_BAD) {
305 Com_Printf ("Sending heartbeat to %s (IPv4)\n", sv_master[i]->string );
306 NET_OutOfBandPrint( NS_SERVER, adr[i][0], "heartbeat %s\n", message);
307 }
308 sv_master[i]->modified = qtrue;
309 } else {
310 Com_Printf( "%s has no IPv4 address.\n", sv_master[i]->string);
311 }
312 }
313 }
314
315 if(netenabled & NET_ENABLEV6)
316 {
317 if(firstRes || adr[i][1].type != NA_BAD) {
318 Com_Printf("Resolving %s (IPv6)\n", sv_master[i]->string);
319 res = NET_StringToAdr(sv_master[i]->string, &adr[i][1], NA_IP6);
320
321 if(res == 2)
322 {
323 // if no port was specified, use the default master port
324 adr[i][1].port = BigShort(PORT_MASTER);
325 }
326
327 if(res) {
328 Com_Printf( "%s resolved to %s\n", sv_master[i]->string, NET_AdrToStringwPort(adr[i][1]));
329 if(adr[i][1].type != NA_BAD) {
330 Com_Printf ("Sending heartbeat to %s (IPv6)\n", sv_master[i]->string );
331 NET_OutOfBandPrint( NS_SERVER, adr[i][1], "heartbeat %s\n", message);
332 }
333 sv_master[i]->modified = qtrue;
334 } else {
335 Com_Printf( "%s has no IPv6 address.\n", sv_master[i]->string);
336 }
337 }
338 }
339
340 if(adr[i][0].type == NA_BAD && adr[i][1].type == NA_BAD)
341 {
342 Com_Printf("Couldn't resolve address: %s\n", sv_master[i]->string);
343 continue;
344 }
345 }
346 }
347
348 firstRes = qfalse;
349 }
350
351 /*
352 =================
353 SV_MasterShutdown
354
355 Informs all masters that this server is going down
356 =================
357 */
SV_MasterShutdown(void)358 void SV_MasterShutdown( void ) {
359 // send a heartbeat right now
360 svs.nextHeartbeatTime = -9999;
361 SV_MasterHeartbeat(FLATLINE_FOR_MASTER);
362
363 // send it again to minimize chance of drops
364 // svs.nextHeartbeatTime = -9999;
365 // SV_MasterHeartbeat(FLATLINE_FOR_MASTER);
366
367 // when the master tries to poll the server, it won't respond, so
368 // it will be removed from the list
369 }
370
371
372 /*
373 ==============================================================================
374
375 CONNECTIONLESS COMMANDS
376
377 ==============================================================================
378 */
379
380 // This is deliberately quite large to make it more of an effort to DoS
381 #define MAX_BUCKETS 16384
382 #define MAX_HASHES 1024
383
384 static leakyBucket_t buckets[ MAX_BUCKETS ];
385 static leakyBucket_t *bucketHashes[ MAX_HASHES ];
386 leakyBucket_t outboundLeakyBucket;
387
388 /*
389 ================
390 SVC_HashForAddress
391 ================
392 */
SVC_HashForAddress(netadr_t address)393 static long SVC_HashForAddress( netadr_t address ) {
394 byte *ip = NULL;
395 size_t size = 0;
396 int i;
397 long hash = 0;
398
399 switch ( address.type ) {
400 case NA_IP: ip = address.ip; size = 4; break;
401 case NA_IP6: ip = address.ip6; size = 16; break;
402 default: break;
403 }
404
405 for ( i = 0; i < size; i++ ) {
406 hash += (long)( ip[ i ] ) * ( i + 119 );
407 }
408
409 hash = ( hash ^ ( hash >> 10 ) ^ ( hash >> 20 ) );
410 hash &= ( MAX_HASHES - 1 );
411
412 return hash;
413 }
414
415 /*
416 ================
417 SVC_BucketForAddress
418
419 Find or allocate a bucket for an address
420 ================
421 */
SVC_BucketForAddress(netadr_t address,int burst,int period)422 static leakyBucket_t *SVC_BucketForAddress( netadr_t address, int burst, int period ) {
423 leakyBucket_t *bucket = NULL;
424 int i;
425 long hash = SVC_HashForAddress( address );
426 int now = Sys_Milliseconds();
427
428 for ( bucket = bucketHashes[ hash ]; bucket; bucket = bucket->next ) {
429 switch ( bucket->type ) {
430 case NA_IP:
431 if ( memcmp( bucket->ipv._4, address.ip, 4 ) == 0 ) {
432 return bucket;
433 }
434 break;
435
436 case NA_IP6:
437 if ( memcmp( bucket->ipv._6, address.ip6, 16 ) == 0 ) {
438 return bucket;
439 }
440 break;
441
442 default:
443 break;
444 }
445 }
446
447 for ( i = 0; i < MAX_BUCKETS; i++ ) {
448 int interval;
449
450 bucket = &buckets[ i ];
451 interval = now - bucket->lastTime;
452
453 // Reclaim expired buckets
454 if ( bucket->lastTime > 0 && ( interval > ( burst * period ) ||
455 interval < 0 ) ) {
456 if ( bucket->prev != NULL ) {
457 bucket->prev->next = bucket->next;
458 } else {
459 bucketHashes[ bucket->hash ] = bucket->next;
460 }
461
462 if ( bucket->next != NULL ) {
463 bucket->next->prev = bucket->prev;
464 }
465
466 Com_Memset( bucket, 0, sizeof( leakyBucket_t ) );
467 }
468
469 if ( bucket->type == NA_BAD ) {
470 bucket->type = address.type;
471 switch ( address.type ) {
472 case NA_IP: Com_Memcpy( bucket->ipv._4, address.ip, 4 ); break;
473 case NA_IP6: Com_Memcpy( bucket->ipv._6, address.ip6, 16 ); break;
474 default: break;
475 }
476
477 bucket->lastTime = now;
478 bucket->burst = 0;
479 bucket->hash = hash;
480
481 // Add to the head of the relevant hash chain
482 bucket->next = bucketHashes[ hash ];
483 if ( bucketHashes[ hash ] != NULL ) {
484 bucketHashes[ hash ]->prev = bucket;
485 }
486
487 bucket->prev = NULL;
488 bucketHashes[ hash ] = bucket;
489
490 return bucket;
491 }
492 }
493
494 // Couldn't allocate a bucket for this address
495 return NULL;
496 }
497
498 /*
499 ================
500 SVC_RateLimit
501 ================
502 */
SVC_RateLimit(leakyBucket_t * bucket,int burst,int period)503 qboolean SVC_RateLimit( leakyBucket_t *bucket, int burst, int period ) {
504 if ( bucket != NULL ) {
505 int now = Sys_Milliseconds();
506 int interval = now - bucket->lastTime;
507 int expired = interval / period;
508 int expiredRemainder = interval % period;
509
510 if ( expired > bucket->burst || interval < 0 ) {
511 bucket->burst = 0;
512 bucket->lastTime = now;
513 } else {
514 bucket->burst -= expired;
515 bucket->lastTime = now - expiredRemainder;
516 }
517
518 if ( bucket->burst < burst ) {
519 bucket->burst++;
520
521 return qfalse;
522 }
523 }
524
525 return qtrue;
526 }
527
528 /*
529 ================
530 SVC_RateLimitAddress
531
532 Rate limit for a particular address
533 ================
534 */
SVC_RateLimitAddress(netadr_t from,int burst,int period)535 qboolean SVC_RateLimitAddress( netadr_t from, int burst, int period ) {
536 leakyBucket_t *bucket = SVC_BucketForAddress( from, burst, period );
537
538 return SVC_RateLimit( bucket, burst, period );
539 }
540
541 /*
542 ================
543 SVC_Status
544
545 Responds with all the info that qplug or qspy can see about the server
546 and all connected players. Used for getting detailed information after
547 the simple info query.
548 ================
549 */
SVC_Status(netadr_t from)550 static void SVC_Status( netadr_t from ) {
551 char player[1024];
552 char status[MAX_MSGLEN];
553 int i;
554 client_t *cl;
555 playerState_t *ps;
556 int statusLength;
557 int playerLength;
558 char infostring[MAX_INFO_STRING];
559
560 // ignore if we are in single player
561 if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
562 return;
563 }
564
565 // Prevent using getstatus as an amplifier
566 if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
567 Com_DPrintf( "SVC_Status: rate limit from %s exceeded, dropping request\n",
568 NET_AdrToString( from ) );
569 return;
570 }
571
572 // Allow getstatus to be DoSed relatively easily, but prevent
573 // excess outbound bandwidth usage when being flooded inbound
574 if ( SVC_RateLimit( &outboundLeakyBucket, 10, 100 ) ) {
575 Com_DPrintf( "SVC_Status: rate limit exceeded, dropping request\n" );
576 return;
577 }
578
579 // DHM - Nerve
580 #ifdef UPDATE_SERVER
581 return;
582 #endif
583
584 // A maximum challenge length of 128 should be more than plenty.
585 if(strlen(Cmd_Argv(1)) > 128)
586 return;
587
588 strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) );
589
590 // echo back the parameter to status. so master servers can use it as a challenge
591 // to prevent timed spoofed reply packets that add ghost servers
592 Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) );
593
594 status[0] = 0;
595 statusLength = 0;
596
597 for ( i = 0 ; i < sv_maxclients->integer ; i++ ) {
598 cl = &svs.clients[i];
599 if ( cl->state >= CS_CONNECTED ) {
600 ps = SV_GameClientNum( i );
601 Com_sprintf( player, sizeof( player ), "%i %i \"%s\"\n",
602 ps->persistant[PERS_SCORE], cl->ping, cl->name );
603 playerLength = strlen( player );
604 if ( statusLength + playerLength >= sizeof( status ) ) {
605 break; // can't hold any more
606 }
607 strcpy( status + statusLength, player );
608 statusLength += playerLength;
609 }
610 }
611
612 NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status );
613 }
614
615 /*
616 ================
617 SVC_Info
618
619 Responds with a short info message that should be enough to determine
620 if a user is interested in a server to do a full status
621 ================
622 */
SVC_Info(netadr_t from)623 void SVC_Info( netadr_t from ) {
624 int i, count, humans;
625 char *gamedir;
626 char infostring[MAX_INFO_STRING];
627 char *antilag;
628
629 // DHM - Nerve
630 #ifdef UPDATE_SERVER
631 return;
632 #endif
633
634 // ignore if we are in single player
635 if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
636 return;
637 }
638
639 // Prevent using getinfo as an amplifier
640 if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
641 Com_DPrintf( "SVC_Info: rate limit from %s exceeded, dropping request\n",
642 NET_AdrToString( from ) );
643 return;
644 }
645
646 // Allow getinfo to be DoSed relatively easily, but prevent
647 // excess outbound bandwidth usage when being flooded inbound
648 if ( SVC_RateLimit( &outboundLeakyBucket, 10, 100 ) ) {
649 Com_DPrintf( "SVC_Info: rate limit exceeded, dropping request\n" );
650 return;
651 }
652
653 /*
654 * Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led
655 * to the Infostring bug discovered by Luigi Auriemma. See http://aluigi.altervista.org/ for the advisory.
656 */
657
658 // A maximum challenge length of 128 should be more than plenty.
659 if(strlen(Cmd_Argv(1)) > 128)
660 return;
661
662 // don't count privateclients
663 count = humans = 0;
664 for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) {
665 if ( svs.clients[i].state >= CS_CONNECTED ) {
666 count++;
667 if (svs.clients[i].netchan.remoteAddress.type != NA_BOT) {
668 humans++;
669 }
670 }
671 }
672
673 infostring[0] = 0;
674
675 // echo back the parameter to status. so servers can use it as a challenge
676 // to prevent timed spoofed reply packets that add ghost servers
677 Info_SetValueForKey( infostring, "challenge", Cmd_Argv( 1 ) );
678
679 Info_SetValueForKey( infostring, "gamename", com_gamename->string );
680
681 #ifdef LEGACY_PROTOCOL
682 if(com_legacyprotocol->integer > 0)
683 Info_SetValueForKey(infostring, "protocol", va("%i", com_legacyprotocol->integer));
684 else
685 #endif
686 Info_SetValueForKey(infostring, "protocol", va("%i", com_protocol->integer));
687
688 Info_SetValueForKey( infostring, "hostname", sv_hostname->string );
689 Info_SetValueForKey( infostring, "mapname", sv_mapname->string );
690 Info_SetValueForKey( infostring, "clients", va( "%i", count ) );
691 Info_SetValueForKey( infostring, "g_humanplayers", va( "%i", humans ) );
692 Info_SetValueForKey( infostring, "sv_maxclients",
693 va( "%i", sv_maxclients->integer - sv_privateClients->integer ) );
694 Info_SetValueForKey( infostring, "gametype", va( "%i", sv_gametype->integer ) );
695 Info_SetValueForKey( infostring, "pure", va( "%i", sv_pure->integer ) );
696 Info_SetValueForKey( infostring, "g_needpass", va("%d", Cvar_VariableIntegerValue( "g_needpass" ) ) );
697
698 #ifdef USE_VOIP
699 if (sv_voipProtocol->string && *sv_voipProtocol->string) {
700 Info_SetValueForKey( infostring, "voip", sv_voipProtocol->string );
701 }
702 #endif
703
704 if ( sv_minPing->integer ) {
705 Info_SetValueForKey( infostring, "minPing", va( "%i", sv_minPing->integer ) );
706 }
707 if ( sv_maxPing->integer ) {
708 Info_SetValueForKey( infostring, "maxPing", va( "%i", sv_maxPing->integer ) );
709 }
710 gamedir = Cvar_VariableString( "fs_game" );
711 if ( *gamedir ) {
712 Info_SetValueForKey( infostring, "game", gamedir );
713 }
714 Info_SetValueForKey( infostring, "sv_allowAnonymous", va( "%i", sv_allowAnonymous->integer ) );
715
716 // Rafael gameskill
717 Info_SetValueForKey( infostring, "gameskill", va( "%i", sv_gameskill->integer ) );
718 // done
719
720 Info_SetValueForKey( infostring, "friendlyFire", va( "%i", sv_friendlyFire->integer ) ); // NERVE - SMF
721 Info_SetValueForKey( infostring, "maxlives", va( "%i", sv_maxlives->integer ? 1 : 0 ) ); // NERVE - SMF
722 Info_SetValueForKey( infostring, "tourney", va( "%i", sv_tourney->integer ) ); // NERVE - SMF
723
724 // TTimo
725 antilag = Cvar_VariableString( "g_antilag" );
726 if ( antilag ) {
727 Info_SetValueForKey( infostring, "g_antilag", antilag );
728 }
729
730 NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring );
731 }
732
733 // DHM - Nerve
734 #ifdef UPDATE_SERVER
735 /*
736 ================
737 SVC_GetUpdateInfo
738
739 Responds with a short info message that tells the client if they
740 have an update available for their version
741 ================
742 */
SVC_GetUpdateInfo(netadr_t from)743 void SVC_GetUpdateInfo( netadr_t from ) {
744 char *version;
745 char *platform;
746 int i;
747 qboolean found = qfalse;
748
749 version = Cmd_Argv( 1 );
750 platform = Cmd_Argv( 2 );
751
752 Com_DPrintf( "SVC_GetUpdateInfo: version == %s / %s,\n", version, platform );
753
754 for ( i = 0; i < numVersions; i++ ) {
755 if ( !strcmp( versionMap[i].version, version ) &&
756 !strcmp( versionMap[i].platform, platform ) ) {
757
758 // If the installer is set to "current", we will skip over it
759 if ( strcmp( versionMap[i].installer, "current" ) ) {
760 found = qtrue;
761 }
762
763 break;
764 }
765 }
766
767 if ( found ) {
768 NET_OutOfBandPrint( NS_SERVER, from, "updateResponse 1 %s", versionMap[i].installer );
769 Com_DPrintf( " SENT: updateResponse 1 %s\n", versionMap[i].installer );
770 } else {
771 NET_OutOfBandPrint( NS_SERVER, from, "updateResponse 0" );
772 Com_DPrintf( " SENT: updateResponse 0\n" );
773 }
774 }
775 #endif
776 // DHM - Nerve
777
778 /*
779 ==============
780 SV_FlushRedirect
781
782 ==============
783 */
SV_FlushRedirect(char * outputbuf)784 static void SV_FlushRedirect( char *outputbuf ) {
785 NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf );
786 }
787
788 /*
789 ===============
790 SVC_RemoteCommand
791
792 An rcon packet arrived from the network.
793 Shift down the remaining args
794 Redirect all printfs
795 ===============
796 */
SVC_RemoteCommand(netadr_t from,msg_t * msg)797 static void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
798 qboolean valid;
799 char remaining[1024];
800 // show_bug.cgi?id=376
801 // if we send an OOB print message this size, 1.31 clients die in a Com_Printf buffer overflow
802 // the buffer overflow will be fixed in > 1.31 clients
803 // but we want a server side fix
804 // we must NEVER send an OOB message that will be > 1.31 MAXPRINTMSG (4096)
805 #define SV_OUTPUTBUF_LENGTH ( 256 - 16 )
806 char sv_outputbuf[SV_OUTPUTBUF_LENGTH];
807 char *cmd_aux;
808
809 // Prevent using rcon as an amplifier and make dictionary attacks impractical
810 if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
811 Com_DPrintf( "SVC_RemoteCommand: rate limit from %s exceeded, dropping request\n",
812 NET_AdrToString( from ) );
813 return;
814 }
815
816 if ( !strlen( sv_rconPassword->string ) ||
817 strcmp( Cmd_Argv( 1 ), sv_rconPassword->string ) ) {
818 static leakyBucket_t bucket;
819
820 // Make DoS via rcon impractical
821 if ( SVC_RateLimit( &bucket, 10, 1000 ) ) {
822 Com_DPrintf( "SVC_RemoteCommand: rate limit exceeded, dropping request\n" );
823 return;
824 }
825
826 valid = qfalse;
827 Com_Printf ("Bad rcon from %s: %s\n", NET_AdrToString (from), Cmd_ArgsFrom(2) );
828 } else {
829 valid = qtrue;
830 Com_Printf ("Rcon from %s: %s\n", NET_AdrToString (from), Cmd_ArgsFrom(2) );
831 }
832
833 // start redirecting all print outputs to the packet
834 svs.redirectAddress = from;
835 // FIXME TTimo our rcon redirection could be improved
836 // big rcon commands such as status lead to sending
837 // out of band packets on every single call to Com_Printf
838 // which leads to client overflows
839 // see show_bug.cgi?id=51
840 // (also a Q3 issue)
841 Com_BeginRedirect( sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect );
842
843 if ( !strlen( sv_rconPassword->string ) ) {
844 Com_Printf( "No rconpassword set on the server.\n" );
845 } else if ( !valid ) {
846 Com_Printf( "Bad rconpassword.\n" );
847 } else {
848 remaining[0] = 0;
849
850 // ATVI Wolfenstein Misc #284
851 // get the command directly, "rcon <pass> <command>" to avoid quoting issues
852 // extract the command by walking
853 // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing
854 cmd_aux = Cmd_Cmd();
855 cmd_aux += 4;
856 while ( cmd_aux[0] == ' ' )
857 cmd_aux++;
858 while ( cmd_aux[0] && cmd_aux[0] != ' ' ) // password
859 cmd_aux++;
860 while ( cmd_aux[0] == ' ' )
861 cmd_aux++;
862
863 Q_strcat( remaining, sizeof( remaining ), cmd_aux );
864
865 Cmd_ExecuteString( remaining );
866
867 }
868
869 Com_EndRedirect();
870 }
871
872 /*
873 =================
874 SV_ConnectionlessPacket
875
876 A connectionless packet has four leading 0xff
877 characters to distinguish it from a game channel.
878 Clients that are in the game can still send
879 connectionless packets.
880 =================
881 */
SV_ConnectionlessPacket(netadr_t from,msg_t * msg)882 static void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
883 char *s;
884 char *c;
885
886 MSG_BeginReadingOOB( msg );
887 MSG_ReadLong( msg ); // skip the -1 marker
888
889 if (!Q_strncmp("connect", (char *) &msg->data[4], 7)) {
890 Huff_Decompress( msg, 12 );
891 }
892
893 s = MSG_ReadStringLine( msg );
894
895 Cmd_TokenizeString( s );
896
897 c = Cmd_Argv( 0 );
898 Com_DPrintf( "SV packet %s : %s\n", NET_AdrToString( from ), c );
899
900 if ( !Q_stricmp( c,"getstatus" ) ) {
901 SVC_Status( from );
902 } else if ( !Q_stricmp( c,"getinfo" ) ) {
903 SVC_Info( from );
904 } else if ( !Q_stricmp( c,"getchallenge" ) ) {
905 SV_GetChallenge( from );
906 } else if ( !Q_stricmp( c,"connect" ) ) {
907 SV_DirectConnect( from );
908 #ifndef STANDALONE
909 #ifdef USE_AUTHORIZE_SERVER
910 } else if ( !Q_stricmp( c,"ipAuthorize" ) ) {
911 SV_AuthorizeIpPacket( from );
912 #endif
913 #endif
914 } else if ( !Q_stricmp( c, "rcon" ) ) {
915 SVC_RemoteCommand( from, msg );
916 // DHM - Nerve
917 #ifdef UPDATE_SERVER
918 } else if ( !Q_stricmp( c, "getUpdateInfo" ) ) {
919 SVC_GetUpdateInfo( from );
920 #endif
921 // DHM - Nerve
922 } else if ( !Q_stricmp( c,"disconnect" ) ) {
923 // if a client starts up a local server, we may see some spurious
924 // server disconnect messages when their new server sees our final
925 // sequenced messages to the old client
926 } else {
927 Com_DPrintf( "bad connectionless packet from %s:\n%s\n",
928 NET_AdrToString( from ), s );
929 }
930 }
931
932 //============================================================================
933
934 /*
935 =================
936 SV_PacketEvent
937 =================
938 */
SV_PacketEvent(netadr_t from,msg_t * msg)939 void SV_PacketEvent( netadr_t from, msg_t *msg ) {
940 int i;
941 client_t *cl;
942 int qport;
943
944 // check for connectionless packet (0xffffffff) first
945 if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) {
946 SV_ConnectionlessPacket( from, msg );
947 return;
948 }
949
950 // read the qport out of the message so we can fix up
951 // stupid address translating routers
952 MSG_BeginReadingOOB( msg );
953 MSG_ReadLong( msg ); // sequence number
954 qport = MSG_ReadShort( msg ) & 0xffff;
955
956 // find which client the message is from
957 for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) {
958 if ( cl->state == CS_FREE ) {
959 continue;
960 }
961 if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) {
962 continue;
963 }
964 // it is possible to have multiple clients from a single IP
965 // address, so they are differentiated by the qport variable
966 if ( cl->netchan.qport != qport ) {
967 continue;
968 }
969
970 // the IP port can't be used to differentiate them, because
971 // some address translating routers periodically change UDP
972 // port assignments
973 if ( cl->netchan.remoteAddress.port != from.port ) {
974 Com_Printf( "SV_PacketEvent: fixing up a translated port\n" );
975 cl->netchan.remoteAddress.port = from.port;
976 }
977
978 // make sure it is a valid, in sequence packet
979 if ( SV_Netchan_Process( cl, msg ) ) {
980 // zombie clients still need to do the Netchan_Process
981 // to make sure they don't need to retransmit the final
982 // reliable message, but they don't do any other processing
983 if ( cl->state != CS_ZOMBIE ) {
984 cl->lastPacketTime = svs.time; // don't timeout
985 SV_ExecuteClientMessage( cl, msg );
986 }
987 }
988 return;
989 }
990
991 }
992
993
994 /*
995 ===================
996 SV_CalcPings
997
998 Updates the cl->ping variables
999 ===================
1000 */
SV_CalcPings(void)1001 static void SV_CalcPings( void ) {
1002 int i, j;
1003 client_t *cl;
1004 int total, count;
1005 int delta;
1006 playerState_t *ps;
1007
1008 for ( i = 0 ; i < sv_maxclients->integer ; i++ ) {
1009 cl = &svs.clients[i];
1010
1011 // DHM - Nerve
1012 #ifdef UPDATE_SERVER
1013 if ( !cl ) {
1014 continue;
1015 }
1016 #endif
1017
1018 if ( cl->state != CS_ACTIVE ) {
1019 cl->ping = 999;
1020 continue;
1021 }
1022 if ( !cl->gentity ) {
1023 cl->ping = 999;
1024 continue;
1025 }
1026 if ( cl->gentity->r.svFlags & SVF_BOT ) {
1027 cl->ping = 0;
1028 continue;
1029 }
1030
1031 total = 0;
1032 count = 0;
1033 for ( j = 0 ; j < PACKET_BACKUP ; j++ ) {
1034 if ( cl->frames[j].messageAcked <= 0 ) {
1035 continue;
1036 }
1037 delta = cl->frames[j].messageAcked - cl->frames[j].messageSent;
1038 count++;
1039 total += delta;
1040 }
1041 if ( !count ) {
1042 cl->ping = 999;
1043 } else {
1044 cl->ping = total / count;
1045 if ( cl->ping > 999 ) {
1046 cl->ping = 999;
1047 }
1048 }
1049
1050 // let the game dll know about the ping
1051 ps = SV_GameClientNum( i );
1052 ps->ping = cl->ping;
1053 }
1054 }
1055
1056 /*
1057 ==================
1058 SV_CheckTimeouts
1059
1060 If a packet has not been received from a client for timeout->integer
1061 seconds, drop the conneciton. Server time is used instead of
1062 realtime to avoid dropping the local client while debugging.
1063
1064 When a client is normally dropped, the client_t goes into a zombie state
1065 for a few seconds to make sure any final reliable message gets resent
1066 if necessary
1067 ==================
1068 */
SV_CheckTimeouts(void)1069 static void SV_CheckTimeouts( void ) {
1070 int i;
1071 client_t *cl;
1072 int droppoint;
1073 int zombiepoint;
1074
1075 droppoint = svs.time - 1000 * sv_timeout->integer;
1076 zombiepoint = svs.time - 1000 * sv_zombietime->integer;
1077
1078 for ( i = 0,cl = svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) {
1079 // message times may be wrong across a changelevel
1080 if ( cl->lastPacketTime > svs.time ) {
1081 cl->lastPacketTime = svs.time;
1082 }
1083
1084 if ( cl->state == CS_ZOMBIE
1085 && cl->lastPacketTime < zombiepoint ) {
1086 // using the client id cause the cl->name is empty at this point
1087 Com_DPrintf( "Going from CS_ZOMBIE to CS_FREE for client %d\n", i );
1088 cl->state = CS_FREE; // can now be reused
1089 continue;
1090 }
1091 if ( cl->state >= CS_CONNECTED && cl->lastPacketTime < droppoint ) {
1092 // wait several frames so a debugger session doesn't
1093 // cause a timeout
1094 if ( ++cl->timeoutCount > 5 ) {
1095 SV_DropClient( cl, "timed out" );
1096 cl->state = CS_FREE; // don't bother with zombie state
1097 }
1098 } else {
1099 cl->timeoutCount = 0;
1100 }
1101 }
1102 }
1103
1104
1105 /*
1106 ==================
1107 SV_CheckPaused
1108 ==================
1109 */
SV_CheckPaused(void)1110 static qboolean SV_CheckPaused( void ) {
1111 int count;
1112 client_t *cl;
1113 int i;
1114
1115 if ( !cl_paused->integer ) {
1116 return qfalse;
1117 }
1118
1119 // only pause if there is just a single client connected
1120 count = 0;
1121 for ( i = 0,cl = svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) {
1122 if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) {
1123 count++;
1124 }
1125 }
1126
1127 if ( count > 1 ) {
1128 // don't pause
1129 if ( sv_paused->integer ) {
1130 Cvar_Set( "sv_paused", "0" );
1131 }
1132 return qfalse;
1133 }
1134
1135 if ( !sv_paused->integer ) {
1136 Cvar_Set( "sv_paused", "1" );
1137 }
1138 return qtrue;
1139 }
1140
1141 /*
1142 ==================
1143 SV_FrameMsec
1144 Return time in millseconds until processing of the next server frame.
1145 ==================
1146 */
SV_FrameMsec()1147 int SV_FrameMsec()
1148 {
1149 if(sv_fps)
1150 {
1151 int frameMsec;
1152
1153 frameMsec = 1000.0f / sv_fps->value;
1154
1155 if(frameMsec < sv.timeResidual)
1156 return 0;
1157 else
1158 return frameMsec - sv.timeResidual;
1159 }
1160 else
1161 return 1;
1162 }
1163
1164 /*
1165 ==================
1166 SV_Frame
1167
1168 Player movement occurs as a result of packet events, which
1169 happen before SV_Frame is called
1170 ==================
1171 */
SV_Frame(int msec)1172 void SV_Frame( int msec ) {
1173 int frameMsec;
1174 int startTime;
1175 char mapname[MAX_QPATH];
1176
1177 // the menu kills the server with this cvar
1178 if ( sv_killserver->integer ) {
1179 SV_Shutdown( "Server was killed." );
1180 Cvar_Set( "sv_killserver", "0" );
1181 return;
1182 }
1183
1184 if (!com_sv_running->integer)
1185 {
1186 // Running as a server, but no map loaded
1187 #ifdef DEDICATED
1188 // Block until something interesting happens
1189 Sys_Sleep(-1);
1190 #endif
1191
1192 return;
1193 }
1194
1195 // allow pause if only the local client is connected
1196 if ( SV_CheckPaused() ) {
1197 return;
1198 }
1199
1200 // if it isn't time for the next frame, do nothing
1201 if ( sv_fps->integer < 1 ) {
1202 Cvar_Set( "sv_fps", "10" );
1203 }
1204
1205 frameMsec = 1000 / sv_fps->integer * com_timescale->value;
1206 // don't let it scale below 1ms
1207 if(frameMsec < 1)
1208 {
1209 Cvar_Set("timescale", va("%f", sv_fps->integer / 1000.0f));
1210 frameMsec = 1;
1211 }
1212
1213 sv.timeResidual += msec;
1214
1215 if (!com_dedicated->integer) SV_BotFrame (sv.time + sv.timeResidual);
1216
1217 // if time is about to hit the 32nd bit, kick all clients
1218 // and clear sv.time, rather
1219 // than checking for negative time wraparound everywhere.
1220 // 2giga-milliseconds = 23 days, so it won't be too often
1221 if ( svs.time > 0x70000000 ) {
1222 Q_strncpyz( mapname, sv_mapname->string, MAX_QPATH );
1223 SV_Shutdown( "Restarting server due to time wrapping" );
1224 Cbuf_AddText( va( "map %s\n", Cvar_VariableString( "mapname" ) ) );
1225 return;
1226 }
1227 // this can happen considerably earlier when lots of clients play and the map doesn't change
1228 if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) {
1229 Q_strncpyz( mapname, sv_mapname->string, MAX_QPATH );
1230 SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" );
1231 Cbuf_AddText( va( "map %s\n", Cvar_VariableString( "mapname" ) ) );
1232 return;
1233 }
1234
1235 if( sv.restartTime && sv.time >= sv.restartTime ) {
1236 sv.restartTime = 0;
1237 Cbuf_AddText( "map_restart 0\n" );
1238 return;
1239 }
1240
1241 // update infostrings if anything has been changed
1242 if ( cvar_modifiedFlags & CVAR_SERVERINFO ) {
1243 SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
1244 cvar_modifiedFlags &= ~CVAR_SERVERINFO;
1245 }
1246 if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) {
1247 SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) );
1248 cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
1249 }
1250 // NERVE - SMF
1251 if ( cvar_modifiedFlags & CVAR_WOLFINFO ) {
1252 SV_SetConfigstring( CS_WOLFINFO, Cvar_InfoString( CVAR_WOLFINFO ) );
1253 cvar_modifiedFlags &= ~CVAR_WOLFINFO;
1254 }
1255
1256 if ( com_speeds->integer ) {
1257 startTime = Sys_Milliseconds();
1258 } else {
1259 startTime = 0; // quite a compiler warning
1260 }
1261
1262 // update ping based on the all received frames
1263 SV_CalcPings();
1264
1265 if (com_dedicated->integer) SV_BotFrame (sv.time);
1266
1267 // run the game simulation in chunks
1268 while ( sv.timeResidual >= frameMsec ) {
1269 sv.timeResidual -= frameMsec;
1270 svs.time += frameMsec;
1271 sv.time += frameMsec;
1272
1273 // let everything in the world think and move
1274 #ifndef UPDATE_SERVER
1275 VM_Call (gvm, GAME_RUN_FRAME, sv.time);
1276 #endif
1277 }
1278
1279 if ( com_speeds->integer ) {
1280 time_game = Sys_Milliseconds() - startTime;
1281 }
1282
1283 // check timeouts
1284 SV_CheckTimeouts();
1285
1286 // send messages back to the clients
1287 SV_SendClientMessages();
1288
1289 // send a heartbeat to the master if needed
1290 SV_MasterHeartbeat(HEARTBEAT_FOR_MASTER);
1291 }
1292
1293 /*
1294 ====================
1295 SV_RateMsec
1296
1297 Return the number of msec until another message can be sent to
1298 a client based on its rate settings
1299 ====================
1300 */
1301
1302 #define UDPIP_HEADER_SIZE 28
1303 #define UDPIP6_HEADER_SIZE 48
1304
SV_RateMsec(client_t * client)1305 int SV_RateMsec(client_t *client)
1306 {
1307 int rate, rateMsec;
1308 int messageSize;
1309
1310 messageSize = client->netchan.lastSentSize;
1311 rate = client->rate;
1312
1313 if(sv_maxRate->integer)
1314 {
1315 if(sv_maxRate->integer < 1000)
1316 Cvar_Set( "sv_MaxRate", "1000" );
1317 if(sv_maxRate->integer < rate)
1318 rate = sv_maxRate->integer;
1319 }
1320
1321 if(sv_minRate->integer)
1322 {
1323 if(sv_minRate->integer < 1000)
1324 Cvar_Set("sv_minRate", "1000");
1325 if(sv_minRate->integer > rate)
1326 rate = sv_minRate->integer;
1327 }
1328
1329 if(client->netchan.remoteAddress.type == NA_IP6)
1330 messageSize += UDPIP6_HEADER_SIZE;
1331 else
1332 messageSize += UDPIP_HEADER_SIZE;
1333
1334 rateMsec = messageSize * 1000 / ((int) (rate * com_timescale->value));
1335 rate = Sys_Milliseconds() - client->netchan.lastSentTime;
1336
1337 if(rate > rateMsec)
1338 return 0;
1339 else
1340 return rateMsec - rate;
1341 }
1342
1343 /*
1344 ====================
1345 SV_SendQueuedPackets
1346
1347 Send download messages and queued packets in the time that we're idle, i.e.
1348 not computing a server frame or sending client snapshots.
1349 Return the time in msec until we expect to be called next
1350 ====================
1351 */
1352
SV_SendQueuedPackets()1353 int SV_SendQueuedPackets()
1354 {
1355 int numBlocks;
1356 int dlStart, deltaT, delayT;
1357 static int dlNextRound = 0;
1358 int timeVal = INT_MAX;
1359
1360 // Send out fragmented packets now that we're idle
1361 delayT = SV_SendQueuedMessages();
1362 if(delayT >= 0)
1363 timeVal = delayT;
1364
1365 if(sv_dlRate->integer)
1366 {
1367 // Rate limiting. This is very imprecise for high
1368 // download rates due to millisecond timedelta resolution
1369 dlStart = Sys_Milliseconds();
1370 deltaT = dlNextRound - dlStart;
1371
1372 if(deltaT > 0)
1373 {
1374 if(deltaT < timeVal)
1375 timeVal = deltaT + 1;
1376 }
1377 else
1378 {
1379 numBlocks = SV_SendDownloadMessages();
1380
1381 if(numBlocks)
1382 {
1383 // There are active downloads
1384 deltaT = Sys_Milliseconds() - dlStart;
1385
1386 delayT = 1000 * numBlocks * MAX_DOWNLOAD_BLKSIZE;
1387 delayT /= sv_dlRate->integer * 1024;
1388
1389 if(delayT <= deltaT + 1)
1390 {
1391 // Sending the last round of download messages
1392 // took too long for given rate, don't wait for
1393 // next round, but always enforce a 1ms delay
1394 // between DL message rounds so we don't hog
1395 // all of the bandwidth. This will result in an
1396 // effective maximum rate of 1MB/s per user, but the
1397 // low download window size limits this anyways.
1398 if(timeVal > 2)
1399 timeVal = 2;
1400
1401 dlNextRound = dlStart + deltaT + 1;
1402 }
1403 else
1404 {
1405 dlNextRound = dlStart + delayT;
1406 delayT -= deltaT;
1407
1408 if(delayT < timeVal)
1409 timeVal = delayT;
1410 }
1411 }
1412 }
1413 }
1414 else
1415 {
1416 if(SV_SendDownloadMessages())
1417 timeVal = 0;
1418 }
1419
1420 return timeVal;
1421 }
1422