sv_main.c (23741B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 23 #include "server.h" 24 25 serverStatic_t svs; // persistant server info 26 server_t sv; // local server 27 vm_t *gvm = NULL; // game virtual machine // bk001212 init 28 29 cvar_t *sv_fps; // time rate for running non-clients 30 cvar_t *sv_timeout; // seconds without any message 31 cvar_t *sv_zombietime; // seconds to sink messages after disconnect 32 cvar_t *sv_rconPassword; // password for remote server commands 33 cvar_t *sv_privatePassword; // password for the privateClient slots 34 cvar_t *sv_allowDownload; 35 cvar_t *sv_maxclients; 36 37 cvar_t *sv_privateClients; // number of clients reserved for password 38 cvar_t *sv_hostname; 39 cvar_t *sv_master[MAX_MASTER_SERVERS]; // master server ip address 40 cvar_t *sv_reconnectlimit; // minimum seconds between connect messages 41 cvar_t *sv_showloss; // report when usercmds are lost 42 cvar_t *sv_padPackets; // add nop bytes to messages 43 cvar_t *sv_killserver; // menu system can set to 1 to shut server down 44 cvar_t *sv_mapname; 45 cvar_t *sv_mapChecksum; 46 cvar_t *sv_serverid; 47 cvar_t *sv_maxRate; 48 cvar_t *sv_minPing; 49 cvar_t *sv_maxPing; 50 cvar_t *sv_gametype; 51 cvar_t *sv_pure; 52 cvar_t *sv_floodProtect; 53 cvar_t *sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491) 54 cvar_t *sv_strictAuth; 55 56 /* 57 ============================================================================= 58 59 EVENT MESSAGES 60 61 ============================================================================= 62 */ 63 64 /* 65 =============== 66 SV_ExpandNewlines 67 68 Converts newlines to "\n" so a line prints nicer 69 =============== 70 */ 71 char *SV_ExpandNewlines( char *in ) { 72 static char string[1024]; 73 int l; 74 75 l = 0; 76 while ( *in && l < sizeof(string) - 3 ) { 77 if ( *in == '\n' ) { 78 string[l++] = '\\'; 79 string[l++] = 'n'; 80 } else { 81 string[l++] = *in; 82 } 83 in++; 84 } 85 string[l] = 0; 86 87 return string; 88 } 89 90 /* 91 ====================== 92 SV_ReplacePendingServerCommands 93 94 This is ugly 95 ====================== 96 */ 97 int SV_ReplacePendingServerCommands( client_t *client, const char *cmd ) { 98 int i, index, csnum1, csnum2; 99 100 for ( i = client->reliableSent+1; i <= client->reliableSequence; i++ ) { 101 index = i & ( MAX_RELIABLE_COMMANDS - 1 ); 102 // 103 if ( !Q_strncmp(cmd, client->reliableCommands[ index ], strlen("cs")) ) { 104 sscanf(cmd, "cs %i", &csnum1); 105 sscanf(client->reliableCommands[ index ], "cs %i", &csnum2); 106 if ( csnum1 == csnum2 ) { 107 Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) ); 108 /* 109 if ( client->netchan.remoteAddress.type != NA_BOT ) { 110 Com_Printf( "WARNING: client %i removed double pending config string %i: %s\n", client-svs.clients, csnum1, cmd ); 111 } 112 */ 113 return qtrue; 114 } 115 } 116 } 117 return qfalse; 118 } 119 120 /* 121 ====================== 122 SV_AddServerCommand 123 124 The given command will be transmitted to the client, and is guaranteed to 125 not have future snapshot_t executed before it is executed 126 ====================== 127 */ 128 void SV_AddServerCommand( client_t *client, const char *cmd ) { 129 int index, i; 130 131 // this is very ugly but it's also a waste to for instance send multiple config string updates 132 // for the same config string index in one snapshot 133 // if ( SV_ReplacePendingServerCommands( client, cmd ) ) { 134 // return; 135 // } 136 137 client->reliableSequence++; 138 // if we would be losing an old command that hasn't been acknowledged, 139 // we must drop the connection 140 // we check == instead of >= so a broadcast print added by SV_DropClient() 141 // doesn't cause a recursive drop client 142 if ( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 ) { 143 Com_Printf( "===== pending server commands =====\n" ); 144 for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) { 145 Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); 146 } 147 Com_Printf( "cmd %5d: %s\n", i, cmd ); 148 SV_DropClient( client, "Server command overflow" ); 149 return; 150 } 151 index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); 152 Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) ); 153 } 154 155 156 /* 157 ================= 158 SV_SendServerCommand 159 160 Sends a reliable command string to be interpreted by 161 the client game module: "cp", "print", "chat", etc 162 A NULL client will broadcast to all clients 163 ================= 164 */ 165 void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) { 166 va_list argptr; 167 byte message[MAX_MSGLEN]; 168 client_t *client; 169 int j; 170 171 va_start (argptr,fmt); 172 Q_vsnprintf ((char *)message, sizeof(message), fmt,argptr); 173 va_end (argptr); 174 175 if ( cl != NULL ) { 176 SV_AddServerCommand( cl, (char *)message ); 177 return; 178 } 179 180 // hack to echo broadcast prints to console 181 if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5) ) { 182 Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) ); 183 } 184 185 // send the data to all relevent clients 186 for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) { 187 if ( client->state < CS_PRIMED ) { 188 continue; 189 } 190 SV_AddServerCommand( client, (char *)message ); 191 } 192 } 193 194 195 /* 196 ============================================================================== 197 198 MASTER SERVER FUNCTIONS 199 200 ============================================================================== 201 */ 202 203 /* 204 ================ 205 SV_MasterHeartbeat 206 207 Send a message to the masters every few minutes to 208 let it know we are alive, and log information. 209 We will also have a heartbeat sent when a server 210 changes from empty to non-empty, and full to non-full, 211 but not on every player enter or exit. 212 ================ 213 */ 214 #define HEARTBEAT_MSEC 300*1000 215 #define HEARTBEAT_GAME "QuakeArena-1" 216 void SV_MasterHeartbeat( void ) { 217 static netadr_t adr[MAX_MASTER_SERVERS]; 218 int i; 219 220 // "dedicated 1" is for lan play, "dedicated 2" is for inet public play 221 if ( !com_dedicated || com_dedicated->integer != 2 ) { 222 return; // only dedicated servers send heartbeats 223 } 224 225 // if not time yet, don't send anything 226 if ( svs.time < svs.nextHeartbeatTime ) { 227 return; 228 } 229 svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC; 230 231 232 // send to group masters 233 for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) { 234 if ( !sv_master[i]->string[0] ) { 235 continue; 236 } 237 238 // see if we haven't already resolved the name 239 // resolving usually causes hitches on win95, so only 240 // do it when needed 241 if ( sv_master[i]->modified ) { 242 sv_master[i]->modified = qfalse; 243 244 Com_Printf( "Resolving %s\n", sv_master[i]->string ); 245 if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) { 246 // if the address failed to resolve, clear it 247 // so we don't take repeated dns hits 248 Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string ); 249 Cvar_Set( sv_master[i]->name, "" ); 250 sv_master[i]->modified = qfalse; 251 continue; 252 } 253 if ( !strstr( ":", sv_master[i]->string ) ) { 254 adr[i].port = BigShort( PORT_MASTER ); 255 } 256 Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string, 257 adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3], 258 BigShort( adr[i].port ) ); 259 } 260 261 262 Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string ); 263 // this command should be changed if the server info / status format 264 // ever incompatably changes 265 NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME ); 266 } 267 } 268 269 /* 270 ================= 271 SV_MasterShutdown 272 273 Informs all masters that this server is going down 274 ================= 275 */ 276 void SV_MasterShutdown( void ) { 277 // send a hearbeat right now 278 svs.nextHeartbeatTime = -9999; 279 SV_MasterHeartbeat(); 280 281 // send it again to minimize chance of drops 282 svs.nextHeartbeatTime = -9999; 283 SV_MasterHeartbeat(); 284 285 // when the master tries to poll the server, it won't respond, so 286 // it will be removed from the list 287 } 288 289 290 /* 291 ============================================================================== 292 293 CONNECTIONLESS COMMANDS 294 295 ============================================================================== 296 */ 297 298 /* 299 ================ 300 SVC_Status 301 302 Responds with all the info that qplug or qspy can see about the server 303 and all connected players. Used for getting detailed information after 304 the simple info query. 305 ================ 306 */ 307 void SVC_Status( netadr_t from ) { 308 char player[1024]; 309 char status[MAX_MSGLEN]; 310 int i; 311 client_t *cl; 312 playerState_t *ps; 313 int statusLength; 314 int playerLength; 315 char infostring[MAX_INFO_STRING]; 316 317 // ignore if we are in single player 318 if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) { 319 return; 320 } 321 322 strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) ); 323 324 // echo back the parameter to status. so master servers can use it as a challenge 325 // to prevent timed spoofed reply packets that add ghost servers 326 Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); 327 328 // add "demo" to the sv_keywords if restricted 329 if ( Cvar_VariableValue( "fs_restrict" ) ) { 330 char keywords[MAX_INFO_STRING]; 331 332 Com_sprintf( keywords, sizeof( keywords ), "demo %s", 333 Info_ValueForKey( infostring, "sv_keywords" ) ); 334 Info_SetValueForKey( infostring, "sv_keywords", keywords ); 335 } 336 337 status[0] = 0; 338 statusLength = 0; 339 340 for (i=0 ; i < sv_maxclients->integer ; i++) { 341 cl = &svs.clients[i]; 342 if ( cl->state >= CS_CONNECTED ) { 343 ps = SV_GameClientNum( i ); 344 Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", 345 ps->persistant[PERS_SCORE], cl->ping, cl->name); 346 playerLength = strlen(player); 347 if (statusLength + playerLength >= sizeof(status) ) { 348 break; // can't hold any more 349 } 350 strcpy (status + statusLength, player); 351 statusLength += playerLength; 352 } 353 } 354 355 NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status ); 356 } 357 358 /* 359 ================ 360 SVC_Info 361 362 Responds with a short info message that should be enough to determine 363 if a user is interested in a server to do a full status 364 ================ 365 */ 366 void SVC_Info( netadr_t from ) { 367 int i, count; 368 char *gamedir; 369 char infostring[MAX_INFO_STRING]; 370 371 // ignore if we are in single player 372 if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { 373 return; 374 } 375 376 // don't count privateclients 377 count = 0; 378 for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { 379 if ( svs.clients[i].state >= CS_CONNECTED ) { 380 count++; 381 } 382 } 383 384 infostring[0] = 0; 385 386 // echo back the parameter to status. so servers can use it as a challenge 387 // to prevent timed spoofed reply packets that add ghost servers 388 Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); 389 390 Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) ); 391 Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); 392 Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); 393 Info_SetValueForKey( infostring, "clients", va("%i", count) ); 394 Info_SetValueForKey( infostring, "sv_maxclients", 395 va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); 396 Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); 397 Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); 398 399 if( sv_minPing->integer ) { 400 Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) ); 401 } 402 if( sv_maxPing->integer ) { 403 Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) ); 404 } 405 gamedir = Cvar_VariableString( "fs_game" ); 406 if( *gamedir ) { 407 Info_SetValueForKey( infostring, "game", gamedir ); 408 } 409 410 NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); 411 } 412 413 /* 414 ================ 415 SVC_FlushRedirect 416 417 ================ 418 */ 419 void SV_FlushRedirect( char *outputbuf ) { 420 NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf ); 421 } 422 423 /* 424 =============== 425 SVC_RemoteCommand 426 427 An rcon packet arrived from the network. 428 Shift down the remaining args 429 Redirect all printfs 430 =============== 431 */ 432 void SVC_RemoteCommand( netadr_t from, msg_t *msg ) { 433 qboolean valid; 434 unsigned int time; 435 char remaining[1024]; 436 // TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc. 437 // (OOB messages are the bottleneck here) 438 #define SV_OUTPUTBUF_LENGTH (1024 - 16) 439 char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; 440 static unsigned int lasttime = 0; 441 char *cmd_aux; 442 443 // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534 444 time = Com_Milliseconds(); 445 if (time<(lasttime+500)) { 446 return; 447 } 448 lasttime = time; 449 450 if ( !strlen( sv_rconPassword->string ) || 451 strcmp (Cmd_Argv(1), sv_rconPassword->string) ) { 452 valid = qfalse; 453 Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); 454 } else { 455 valid = qtrue; 456 Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) ); 457 } 458 459 // start redirecting all print outputs to the packet 460 svs.redirectAddress = from; 461 Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); 462 463 if ( !strlen( sv_rconPassword->string ) ) { 464 Com_Printf ("No rconpassword set on the server.\n"); 465 } else if ( !valid ) { 466 Com_Printf ("Bad rconpassword.\n"); 467 } else { 468 remaining[0] = 0; 469 470 // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 471 // get the command directly, "rcon <pass> <command>" to avoid quoting issues 472 // extract the command by walking 473 // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing 474 cmd_aux = Cmd_Cmd(); 475 cmd_aux+=4; 476 while(cmd_aux[0]==' ') 477 cmd_aux++; 478 while(cmd_aux[0] && cmd_aux[0]!=' ') // password 479 cmd_aux++; 480 while(cmd_aux[0]==' ') 481 cmd_aux++; 482 483 Q_strcat( remaining, sizeof(remaining), cmd_aux); 484 485 Cmd_ExecuteString (remaining); 486 487 } 488 489 Com_EndRedirect (); 490 } 491 492 /* 493 ================= 494 SV_ConnectionlessPacket 495 496 A connectionless packet has four leading 0xff 497 characters to distinguish it from a game channel. 498 Clients that are in the game can still send 499 connectionless packets. 500 ================= 501 */ 502 void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) { 503 char *s; 504 char *c; 505 506 MSG_BeginReadingOOB( msg ); 507 MSG_ReadLong( msg ); // skip the -1 marker 508 509 if (!Q_strncmp("connect", &msg->data[4], 7)) { 510 Huff_Decompress(msg, 12); 511 } 512 513 s = MSG_ReadStringLine( msg ); 514 Cmd_TokenizeString( s ); 515 516 c = Cmd_Argv(0); 517 Com_DPrintf ("SV packet %s : %s\n", NET_AdrToString(from), c); 518 519 if (!Q_stricmp(c, "getstatus")) { 520 SVC_Status( from ); 521 } else if (!Q_stricmp(c, "getinfo")) { 522 SVC_Info( from ); 523 } else if (!Q_stricmp(c, "getchallenge")) { 524 SV_GetChallenge( from ); 525 } else if (!Q_stricmp(c, "connect")) { 526 SV_DirectConnect( from ); 527 } else if (!Q_stricmp(c, "ipAuthorize")) { 528 SV_AuthorizeIpPacket( from ); 529 } else if (!Q_stricmp(c, "rcon")) { 530 SVC_RemoteCommand( from, msg ); 531 } else if (!Q_stricmp(c, "disconnect")) { 532 // if a client starts up a local server, we may see some spurious 533 // server disconnect messages when their new server sees our final 534 // sequenced messages to the old client 535 } else { 536 Com_DPrintf ("bad connectionless packet from %s:\n%s\n" 537 , NET_AdrToString (from), s); 538 } 539 } 540 541 //============================================================================ 542 543 /* 544 ================= 545 SV_ReadPackets 546 ================= 547 */ 548 void SV_PacketEvent( netadr_t from, msg_t *msg ) { 549 int i; 550 client_t *cl; 551 int qport; 552 553 // check for connectionless packet (0xffffffff) first 554 if ( msg->cursize >= 4 && *(int *)msg->data == -1) { 555 SV_ConnectionlessPacket( from, msg ); 556 return; 557 } 558 559 // read the qport out of the message so we can fix up 560 // stupid address translating routers 561 MSG_BeginReadingOOB( msg ); 562 MSG_ReadLong( msg ); // sequence number 563 qport = MSG_ReadShort( msg ) & 0xffff; 564 565 // find which client the message is from 566 for (i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { 567 if (cl->state == CS_FREE) { 568 continue; 569 } 570 if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) { 571 continue; 572 } 573 // it is possible to have multiple clients from a single IP 574 // address, so they are differentiated by the qport variable 575 if (cl->netchan.qport != qport) { 576 continue; 577 } 578 579 // the IP port can't be used to differentiate them, because 580 // some address translating routers periodically change UDP 581 // port assignments 582 if (cl->netchan.remoteAddress.port != from.port) { 583 Com_Printf( "SV_PacketEvent: fixing up a translated port\n" ); 584 cl->netchan.remoteAddress.port = from.port; 585 } 586 587 // make sure it is a valid, in sequence packet 588 if (SV_Netchan_Process(cl, msg)) { 589 // zombie clients still need to do the Netchan_Process 590 // to make sure they don't need to retransmit the final 591 // reliable message, but they don't do any other processing 592 if (cl->state != CS_ZOMBIE) { 593 cl->lastPacketTime = svs.time; // don't timeout 594 SV_ExecuteClientMessage( cl, msg ); 595 } 596 } 597 return; 598 } 599 600 // if we received a sequenced packet from an address we don't recognize, 601 // send an out of band disconnect packet to it 602 NET_OutOfBandPrint( NS_SERVER, from, "disconnect" ); 603 } 604 605 606 /* 607 =================== 608 SV_CalcPings 609 610 Updates the cl->ping variables 611 =================== 612 */ 613 void SV_CalcPings( void ) { 614 int i, j; 615 client_t *cl; 616 int total, count; 617 int delta; 618 playerState_t *ps; 619 620 for (i=0 ; i < sv_maxclients->integer ; i++) { 621 cl = &svs.clients[i]; 622 if ( cl->state != CS_ACTIVE ) { 623 cl->ping = 999; 624 continue; 625 } 626 if ( !cl->gentity ) { 627 cl->ping = 999; 628 continue; 629 } 630 if ( cl->gentity->r.svFlags & SVF_BOT ) { 631 cl->ping = 0; 632 continue; 633 } 634 635 total = 0; 636 count = 0; 637 for ( j = 0 ; j < PACKET_BACKUP ; j++ ) { 638 if ( cl->frames[j].messageAcked <= 0 ) { 639 continue; 640 } 641 delta = cl->frames[j].messageAcked - cl->frames[j].messageSent; 642 count++; 643 total += delta; 644 } 645 if (!count) { 646 cl->ping = 999; 647 } else { 648 cl->ping = total/count; 649 if ( cl->ping > 999 ) { 650 cl->ping = 999; 651 } 652 } 653 654 // let the game dll know about the ping 655 ps = SV_GameClientNum( i ); 656 ps->ping = cl->ping; 657 } 658 } 659 660 /* 661 ================== 662 SV_CheckTimeouts 663 664 If a packet has not been received from a client for timeout->integer 665 seconds, drop the conneciton. Server time is used instead of 666 realtime to avoid dropping the local client while debugging. 667 668 When a client is normally dropped, the client_t goes into a zombie state 669 for a few seconds to make sure any final reliable message gets resent 670 if necessary 671 ================== 672 */ 673 void SV_CheckTimeouts( void ) { 674 int i; 675 client_t *cl; 676 int droppoint; 677 int zombiepoint; 678 679 droppoint = svs.time - 1000 * sv_timeout->integer; 680 zombiepoint = svs.time - 1000 * sv_zombietime->integer; 681 682 for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { 683 // message times may be wrong across a changelevel 684 if (cl->lastPacketTime > svs.time) { 685 cl->lastPacketTime = svs.time; 686 } 687 688 if (cl->state == CS_ZOMBIE 689 && cl->lastPacketTime < zombiepoint) { 690 // using the client id cause the cl->name is empty at this point 691 Com_DPrintf( "Going from CS_ZOMBIE to CS_FREE for client %d\n", i ); 692 cl->state = CS_FREE; // can now be reused 693 continue; 694 } 695 if ( cl->state >= CS_CONNECTED && cl->lastPacketTime < droppoint) { 696 // wait several frames so a debugger session doesn't 697 // cause a timeout 698 if ( ++cl->timeoutCount > 5 ) { 699 SV_DropClient (cl, "timed out"); 700 cl->state = CS_FREE; // don't bother with zombie state 701 } 702 } else { 703 cl->timeoutCount = 0; 704 } 705 } 706 } 707 708 709 /* 710 ================== 711 SV_CheckPaused 712 ================== 713 */ 714 qboolean SV_CheckPaused( void ) { 715 int count; 716 client_t *cl; 717 int i; 718 719 if ( !cl_paused->integer ) { 720 return qfalse; 721 } 722 723 // only pause if there is just a single client connected 724 count = 0; 725 for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { 726 if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) { 727 count++; 728 } 729 } 730 731 if ( count > 1 ) { 732 // don't pause 733 if (sv_paused->integer) 734 Cvar_Set("sv_paused", "0"); 735 return qfalse; 736 } 737 738 if (!sv_paused->integer) 739 Cvar_Set("sv_paused", "1"); 740 return qtrue; 741 } 742 743 /* 744 ================== 745 SV_Frame 746 747 Player movement occurs as a result of packet events, which 748 happen before SV_Frame is called 749 ================== 750 */ 751 void SV_Frame( int msec ) { 752 int frameMsec; 753 int startTime; 754 755 // the menu kills the server with this cvar 756 if ( sv_killserver->integer ) { 757 SV_Shutdown ("Server was killed.\n"); 758 Cvar_Set( "sv_killserver", "0" ); 759 return; 760 } 761 762 if ( !com_sv_running->integer ) { 763 return; 764 } 765 766 // allow pause if only the local client is connected 767 if ( SV_CheckPaused() ) { 768 return; 769 } 770 771 // if it isn't time for the next frame, do nothing 772 if ( sv_fps->integer < 1 ) { 773 Cvar_Set( "sv_fps", "10" ); 774 } 775 frameMsec = 1000 / sv_fps->integer ; 776 777 sv.timeResidual += msec; 778 779 if (!com_dedicated->integer) SV_BotFrame( svs.time + sv.timeResidual ); 780 781 if ( com_dedicated->integer && sv.timeResidual < frameMsec ) { 782 // NET_Sleep will give the OS time slices until either get a packet 783 // or time enough for a server frame has gone by 784 NET_Sleep(frameMsec - sv.timeResidual); 785 return; 786 } 787 788 // if time is about to hit the 32nd bit, kick all clients 789 // and clear sv.time, rather 790 // than checking for negative time wraparound everywhere. 791 // 2giga-milliseconds = 23 days, so it won't be too often 792 if ( svs.time > 0x70000000 ) { 793 SV_Shutdown( "Restarting server due to time wrapping" ); 794 Cbuf_AddText( "vstr nextmap\n" ); 795 return; 796 } 797 // this can happen considerably earlier when lots of clients play and the map doesn't change 798 if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) { 799 SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" ); 800 Cbuf_AddText( "vstr nextmap\n" ); 801 return; 802 } 803 804 if( sv.restartTime && svs.time >= sv.restartTime ) { 805 sv.restartTime = 0; 806 Cbuf_AddText( "map_restart 0\n" ); 807 return; 808 } 809 810 // update infostrings if anything has been changed 811 if ( cvar_modifiedFlags & CVAR_SERVERINFO ) { 812 SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 813 cvar_modifiedFlags &= ~CVAR_SERVERINFO; 814 } 815 if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) { 816 SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) ); 817 cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; 818 } 819 820 if ( com_speeds->integer ) { 821 startTime = Sys_Milliseconds (); 822 } else { 823 startTime = 0; // quite a compiler warning 824 } 825 826 // update ping based on the all received frames 827 SV_CalcPings(); 828 829 if (com_dedicated->integer) SV_BotFrame( svs.time ); 830 831 // run the game simulation in chunks 832 while ( sv.timeResidual >= frameMsec ) { 833 sv.timeResidual -= frameMsec; 834 svs.time += frameMsec; 835 836 // let everything in the world think and move 837 VM_Call( gvm, GAME_RUN_FRAME, svs.time ); 838 } 839 840 if ( com_speeds->integer ) { 841 time_game = Sys_Milliseconds () - startTime; 842 } 843 844 // check timeouts 845 SV_CheckTimeouts(); 846 847 // send messages back to the clients 848 SV_SendClientMessages(); 849 850 // send a heartbeat to the master if needed 851 SV_MasterHeartbeat(); 852 } 853 854 //============================================================================ 855