sv_main.c (24080B)
1 /* 2 Copyright (C) 1997-2001 Id Software, Inc. 3 4 This program is free software; you can redistribute it and/or 5 modify it under the terms of the GNU General Public License 6 as published by the Free Software Foundation; either version 2 7 of the License, or (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13 See the GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 */ 20 21 #include "server.h" 22 23 netadr_t master_adr[MAX_MASTERS]; // address of group servers 24 25 client_t *sv_client; // current client 26 27 cvar_t *sv_paused; 28 cvar_t *sv_timedemo; 29 30 cvar_t *sv_enforcetime; 31 32 cvar_t *timeout; // seconds without any message 33 cvar_t *zombietime; // seconds to sink messages after disconnect 34 35 cvar_t *rcon_password; // password for remote server commands 36 37 cvar_t *allow_download; 38 cvar_t *allow_download_players; 39 cvar_t *allow_download_models; 40 cvar_t *allow_download_sounds; 41 cvar_t *allow_download_maps; 42 43 cvar_t *sv_airaccelerate; 44 45 cvar_t *sv_noreload; // don't reload level state when reentering 46 47 cvar_t *maxclients; // FIXME: rename sv_maxclients 48 cvar_t *sv_showclamp; 49 50 cvar_t *hostname; 51 cvar_t *public_server; // should heartbeats be sent 52 53 cvar_t *sv_reconnect_limit; // minimum seconds between connect messages 54 55 void Master_Shutdown (void); 56 57 58 //============================================================================ 59 60 61 /* 62 ===================== 63 SV_DropClient 64 65 Called when the player is totally leaving the server, either willingly 66 or unwillingly. This is NOT called if the entire server is quiting 67 or crashing. 68 ===================== 69 */ 70 void SV_DropClient (client_t *drop) 71 { 72 // add the disconnect 73 MSG_WriteByte (&drop->netchan.message, svc_disconnect); 74 75 if (drop->state == cs_spawned) 76 { 77 // call the prog function for removing a client 78 // this will remove the body, among other things 79 ge->ClientDisconnect (drop->edict); 80 } 81 82 if (drop->download) 83 { 84 FS_FreeFile (drop->download); 85 drop->download = NULL; 86 } 87 88 drop->state = cs_zombie; // become free in a few seconds 89 drop->name[0] = 0; 90 } 91 92 93 94 /* 95 ============================================================================== 96 97 CONNECTIONLESS COMMANDS 98 99 ============================================================================== 100 */ 101 102 /* 103 =============== 104 SV_StatusString 105 106 Builds the string that is sent as heartbeats and status replies 107 =============== 108 */ 109 char *SV_StatusString (void) 110 { 111 char player[1024]; 112 static char status[MAX_MSGLEN - 16]; 113 int i; 114 client_t *cl; 115 int statusLength; 116 int playerLength; 117 118 strcpy (status, Cvar_Serverinfo()); 119 strcat (status, "\n"); 120 statusLength = strlen(status); 121 122 for (i=0 ; i<maxclients->value ; i++) 123 { 124 cl = &svs.clients[i]; 125 if (cl->state == cs_connected || cl->state == cs_spawned ) 126 { 127 Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", 128 cl->edict->client->ps.stats[STAT_FRAGS], cl->ping, cl->name); 129 playerLength = strlen(player); 130 if (statusLength + playerLength >= sizeof(status) ) 131 break; // can't hold any more 132 strcpy (status + statusLength, player); 133 statusLength += playerLength; 134 } 135 } 136 137 return status; 138 } 139 140 /* 141 ================ 142 SVC_Status 143 144 Responds with all the info that qplug or qspy can see 145 ================ 146 */ 147 void SVC_Status (void) 148 { 149 Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString()); 150 #if 0 151 Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); 152 Com_Printf (SV_StatusString()); 153 Com_EndRedirect (); 154 #endif 155 } 156 157 /* 158 ================ 159 SVC_Ack 160 161 ================ 162 */ 163 void SVC_Ack (void) 164 { 165 Com_Printf ("Ping acknowledge from %s\n", NET_AdrToString(net_from)); 166 } 167 168 /* 169 ================ 170 SVC_Info 171 172 Responds with short info for broadcast scans 173 The second parameter should be the current protocol version number. 174 ================ 175 */ 176 void SVC_Info (void) 177 { 178 char string[64]; 179 int i, count; 180 int version; 181 182 if (maxclients->value == 1) 183 return; // ignore in single player 184 185 version = atoi (Cmd_Argv(1)); 186 187 if (version != PROTOCOL_VERSION) 188 Com_sprintf (string, sizeof(string), "%s: wrong version\n", hostname->string, sizeof(string)); 189 else 190 { 191 count = 0; 192 for (i=0 ; i<maxclients->value ; i++) 193 if (svs.clients[i].state >= cs_connected) 194 count++; 195 196 Com_sprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int)maxclients->value); 197 } 198 199 Netchan_OutOfBandPrint (NS_SERVER, net_from, "info\n%s", string); 200 } 201 202 /* 203 ================ 204 SVC_Ping 205 206 Just responds with an acknowledgement 207 ================ 208 */ 209 void SVC_Ping (void) 210 { 211 Netchan_OutOfBandPrint (NS_SERVER, net_from, "ack"); 212 } 213 214 215 /* 216 ================= 217 SVC_GetChallenge 218 219 Returns a challenge number that can be used 220 in a subsequent client_connect command. 221 We do this to prevent denial of service attacks that 222 flood the server with invalid connection IPs. With a 223 challenge, they must give a valid IP address. 224 ================= 225 */ 226 void SVC_GetChallenge (void) 227 { 228 int i; 229 int oldest; 230 int oldestTime; 231 232 oldest = 0; 233 oldestTime = 0x7fffffff; 234 235 // see if we already have a challenge for this ip 236 for (i = 0 ; i < MAX_CHALLENGES ; i++) 237 { 238 if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) 239 break; 240 if (svs.challenges[i].time < oldestTime) 241 { 242 oldestTime = svs.challenges[i].time; 243 oldest = i; 244 } 245 } 246 247 if (i == MAX_CHALLENGES) 248 { 249 // overwrite the oldest 250 svs.challenges[oldest].challenge = rand() & 0x7fff; 251 svs.challenges[oldest].adr = net_from; 252 svs.challenges[oldest].time = curtime; 253 i = oldest; 254 } 255 256 // send it back 257 Netchan_OutOfBandPrint (NS_SERVER, net_from, "challenge %i", svs.challenges[i].challenge); 258 } 259 260 /* 261 ================== 262 SVC_DirectConnect 263 264 A connection request that did not come from the master 265 ================== 266 */ 267 void SVC_DirectConnect (void) 268 { 269 char userinfo[MAX_INFO_STRING]; 270 netadr_t adr; 271 int i; 272 client_t *cl, *newcl; 273 client_t temp; 274 edict_t *ent; 275 int edictnum; 276 int version; 277 int qport; 278 int challenge; 279 280 adr = net_from; 281 282 Com_DPrintf ("SVC_DirectConnect ()\n"); 283 284 version = atoi(Cmd_Argv(1)); 285 if (version != PROTOCOL_VERSION) 286 { 287 Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION); 288 Com_DPrintf (" rejected connect from version %i\n", version); 289 return; 290 } 291 292 qport = atoi(Cmd_Argv(2)); 293 294 challenge = atoi(Cmd_Argv(3)); 295 296 strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1); 297 userinfo[sizeof(userinfo) - 1] = 0; 298 299 // force the IP key/value pair so the game can filter based on ip 300 Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from)); 301 302 // attractloop servers are ONLY for local clients 303 if (sv.attractloop) 304 { 305 if (!NET_IsLocalAddress (adr)) 306 { 307 Com_Printf ("Remote connect in attract loop. Ignored.\n"); 308 Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n"); 309 return; 310 } 311 } 312 313 // see if the challenge is valid 314 if (!NET_IsLocalAddress (adr)) 315 { 316 for (i=0 ; i<MAX_CHALLENGES ; i++) 317 { 318 if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) 319 { 320 if (challenge == svs.challenges[i].challenge) 321 break; // good 322 Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad challenge.\n"); 323 return; 324 } 325 } 326 if (i == MAX_CHALLENGES) 327 { 328 Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nNo challenge for address.\n"); 329 return; 330 } 331 } 332 333 newcl = &temp; 334 memset (newcl, 0, sizeof(client_t)); 335 336 // if there is already a slot for this ip, reuse it 337 for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++) 338 { 339 if (cl->state == cs_free) 340 continue; 341 if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) 342 && ( cl->netchan.qport == qport 343 || adr.port == cl->netchan.remote_address.port ) ) 344 { 345 if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000)) 346 { 347 Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr)); 348 return; 349 } 350 Com_Printf ("%s:reconnect\n", NET_AdrToString (adr)); 351 newcl = cl; 352 goto gotnewcl; 353 } 354 } 355 356 // find a client slot 357 newcl = NULL; 358 for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++) 359 { 360 if (cl->state == cs_free) 361 { 362 newcl = cl; 363 break; 364 } 365 } 366 if (!newcl) 367 { 368 Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is full.\n"); 369 Com_DPrintf ("Rejected a connection.\n"); 370 return; 371 } 372 373 gotnewcl: 374 // build a new connection 375 // accept the new client 376 // this is the only place a client_t is ever initialized 377 *newcl = temp; 378 sv_client = newcl; 379 edictnum = (newcl-svs.clients)+1; 380 ent = EDICT_NUM(edictnum); 381 newcl->edict = ent; 382 newcl->challenge = challenge; // save challenge for checksumming 383 384 // get the game a chance to reject this connection or modify the userinfo 385 if (!(ge->ClientConnect (ent, userinfo))) 386 { 387 if (*Info_ValueForKey (userinfo, "rejmsg")) 388 Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n", 389 Info_ValueForKey (userinfo, "rejmsg")); 390 else 391 Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" ); 392 Com_DPrintf ("Game rejected a connection.\n"); 393 return; 394 } 395 396 // parse some info from the info strings 397 strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1); 398 SV_UserinfoChanged (newcl); 399 400 // send the connect packet to the client 401 Netchan_OutOfBandPrint (NS_SERVER, adr, "client_connect"); 402 403 Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport); 404 405 newcl->state = cs_connected; 406 407 SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) ); 408 newcl->datagram.allowoverflow = true; 409 newcl->lastmessage = svs.realtime; // don't timeout 410 newcl->lastconnect = svs.realtime; 411 } 412 413 int Rcon_Validate (void) 414 { 415 if (!strlen (rcon_password->string)) 416 return 0; 417 418 if (strcmp (Cmd_Argv(1), rcon_password->string) ) 419 return 0; 420 421 return 1; 422 } 423 424 /* 425 =============== 426 SVC_RemoteCommand 427 428 A client issued an rcon command. 429 Shift down the remaining args 430 Redirect all printfs 431 =============== 432 */ 433 void SVC_RemoteCommand (void) 434 { 435 int i; 436 char remaining[1024]; 437 438 i = Rcon_Validate (); 439 440 if (i == 0) 441 Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4); 442 else 443 Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4); 444 445 Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); 446 447 if (!Rcon_Validate ()) 448 { 449 Com_Printf ("Bad rcon_password.\n"); 450 } 451 else 452 { 453 remaining[0] = 0; 454 455 for (i=2 ; i<Cmd_Argc() ; i++) 456 { 457 strcat (remaining, Cmd_Argv(i) ); 458 strcat (remaining, " "); 459 } 460 461 Cmd_ExecuteString (remaining); 462 } 463 464 Com_EndRedirect (); 465 } 466 467 /* 468 ================= 469 SV_ConnectionlessPacket 470 471 A connectionless packet has four leading 0xff 472 characters to distinguish it from a game channel. 473 Clients that are in the game can still send 474 connectionless packets. 475 ================= 476 */ 477 void SV_ConnectionlessPacket (void) 478 { 479 char *s; 480 char *c; 481 482 MSG_BeginReading (&net_message); 483 MSG_ReadLong (&net_message); // skip the -1 marker 484 485 s = MSG_ReadStringLine (&net_message); 486 487 Cmd_TokenizeString (s, false); 488 489 c = Cmd_Argv(0); 490 Com_DPrintf ("Packet %s : %s\n", NET_AdrToString(net_from), c); 491 492 if (!strcmp(c, "ping")) 493 SVC_Ping (); 494 else if (!strcmp(c, "ack")) 495 SVC_Ack (); 496 else if (!strcmp(c,"status")) 497 SVC_Status (); 498 else if (!strcmp(c,"info")) 499 SVC_Info (); 500 else if (!strcmp(c,"getchallenge")) 501 SVC_GetChallenge (); 502 else if (!strcmp(c,"connect")) 503 SVC_DirectConnect (); 504 else if (!strcmp(c, "rcon")) 505 SVC_RemoteCommand (); 506 else 507 Com_Printf ("bad connectionless packet from %s:\n%s\n" 508 , NET_AdrToString (net_from), s); 509 } 510 511 512 //============================================================================ 513 514 /* 515 =================== 516 SV_CalcPings 517 518 Updates the cl->ping variables 519 =================== 520 */ 521 void SV_CalcPings (void) 522 { 523 int i, j; 524 client_t *cl; 525 int total, count; 526 527 for (i=0 ; i<maxclients->value ; i++) 528 { 529 cl = &svs.clients[i]; 530 if (cl->state != cs_spawned ) 531 continue; 532 533 #if 0 534 if (cl->lastframe > 0) 535 cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = sv.framenum - cl->lastframe + 1; 536 else 537 cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = 0; 538 #endif 539 540 total = 0; 541 count = 0; 542 for (j=0 ; j<LATENCY_COUNTS ; j++) 543 { 544 if (cl->frame_latency[j] > 0) 545 { 546 count++; 547 total += cl->frame_latency[j]; 548 } 549 } 550 if (!count) 551 cl->ping = 0; 552 else 553 #if 0 554 cl->ping = total*100/count - 100; 555 #else 556 cl->ping = total / count; 557 #endif 558 559 // let the game dll know about the ping 560 cl->edict->client->ping = cl->ping; 561 } 562 } 563 564 565 /* 566 =================== 567 SV_GiveMsec 568 569 Every few frames, gives all clients an allotment of milliseconds 570 for their command moves. If they exceed it, assume cheating. 571 =================== 572 */ 573 void SV_GiveMsec (void) 574 { 575 int i; 576 client_t *cl; 577 578 if (sv.framenum & 15) 579 return; 580 581 for (i=0 ; i<maxclients->value ; i++) 582 { 583 cl = &svs.clients[i]; 584 if (cl->state == cs_free ) 585 continue; 586 587 cl->commandMsec = 1800; // 1600 + some slop 588 } 589 } 590 591 592 /* 593 ================= 594 SV_ReadPackets 595 ================= 596 */ 597 void SV_ReadPackets (void) 598 { 599 int i; 600 client_t *cl; 601 int qport; 602 603 while (NET_GetPacket (NS_SERVER, &net_from, &net_message)) 604 { 605 // check for connectionless packet (0xffffffff) first 606 if (*(int *)net_message.data == -1) 607 { 608 SV_ConnectionlessPacket (); 609 continue; 610 } 611 612 // read the qport out of the message so we can fix up 613 // stupid address translating routers 614 MSG_BeginReading (&net_message); 615 MSG_ReadLong (&net_message); // sequence number 616 MSG_ReadLong (&net_message); // sequence number 617 qport = MSG_ReadShort (&net_message) & 0xffff; 618 619 // check for packets from connected clients 620 for (i=0, cl=svs.clients ; i<maxclients->value ; i++,cl++) 621 { 622 if (cl->state == cs_free) 623 continue; 624 if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address)) 625 continue; 626 if (cl->netchan.qport != qport) 627 continue; 628 if (cl->netchan.remote_address.port != net_from.port) 629 { 630 Com_Printf ("SV_ReadPackets: fixing up a translated port\n"); 631 cl->netchan.remote_address.port = net_from.port; 632 } 633 634 if (Netchan_Process(&cl->netchan, &net_message)) 635 { // this is a valid, sequenced packet, so process it 636 if (cl->state != cs_zombie) 637 { 638 cl->lastmessage = svs.realtime; // don't timeout 639 SV_ExecuteClientMessage (cl); 640 } 641 } 642 break; 643 } 644 645 if (i != maxclients->value) 646 continue; 647 } 648 } 649 650 /* 651 ================== 652 SV_CheckTimeouts 653 654 If a packet has not been received from a client for timeout->value 655 seconds, drop the conneciton. Server frames are used instead of 656 realtime to avoid dropping the local client while debugging. 657 658 When a client is normally dropped, the client_t goes into a zombie state 659 for a few seconds to make sure any final reliable message gets resent 660 if necessary 661 ================== 662 */ 663 void SV_CheckTimeouts (void) 664 { 665 int i; 666 client_t *cl; 667 int droppoint; 668 int zombiepoint; 669 670 droppoint = svs.realtime - 1000*timeout->value; 671 zombiepoint = svs.realtime - 1000*zombietime->value; 672 673 for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++) 674 { 675 // message times may be wrong across a changelevel 676 if (cl->lastmessage > svs.realtime) 677 cl->lastmessage = svs.realtime; 678 679 if (cl->state == cs_zombie 680 && cl->lastmessage < zombiepoint) 681 { 682 cl->state = cs_free; // can now be reused 683 continue; 684 } 685 if ( (cl->state == cs_connected || cl->state == cs_spawned) 686 && cl->lastmessage < droppoint) 687 { 688 SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); 689 SV_DropClient (cl); 690 cl->state = cs_free; // don't bother with zombie state 691 } 692 } 693 } 694 695 /* 696 ================ 697 SV_PrepWorldFrame 698 699 This has to be done before the world logic, because 700 player processing happens outside RunWorldFrame 701 ================ 702 */ 703 void SV_PrepWorldFrame (void) 704 { 705 edict_t *ent; 706 int i; 707 708 for (i=0 ; i<ge->num_edicts ; i++, ent++) 709 { 710 ent = EDICT_NUM(i); 711 // events only last for a single message 712 ent->s.event = 0; 713 } 714 715 } 716 717 718 /* 719 ================= 720 SV_RunGameFrame 721 ================= 722 */ 723 void SV_RunGameFrame (void) 724 { 725 if (host_speeds->value) 726 time_before_game = Sys_Milliseconds (); 727 728 // we always need to bump framenum, even if we 729 // don't run the world, otherwise the delta 730 // compression can get confused when a client 731 // has the "current" frame 732 sv.framenum++; 733 sv.time = sv.framenum*100; 734 735 // don't run if paused 736 if (!sv_paused->value || maxclients->value > 1) 737 { 738 ge->RunFrame (); 739 740 // never get more than one tic behind 741 if (sv.time < svs.realtime) 742 { 743 if (sv_showclamp->value) 744 Com_Printf ("sv highclamp\n"); 745 svs.realtime = sv.time; 746 } 747 } 748 749 if (host_speeds->value) 750 time_after_game = Sys_Milliseconds (); 751 752 } 753 754 /* 755 ================== 756 SV_Frame 757 758 ================== 759 */ 760 void SV_Frame (int msec) 761 { 762 time_before_game = time_after_game = 0; 763 764 // if server is not active, do nothing 765 if (!svs.initialized) 766 return; 767 768 svs.realtime += msec; 769 770 // keep the random time dependent 771 rand (); 772 773 // check timeouts 774 SV_CheckTimeouts (); 775 776 // get packets from clients 777 SV_ReadPackets (); 778 779 // move autonomous things around if enough time has passed 780 if (!sv_timedemo->value && svs.realtime < sv.time) 781 { 782 // never let the time get too far off 783 if (sv.time - svs.realtime > 100) 784 { 785 if (sv_showclamp->value) 786 Com_Printf ("sv lowclamp\n"); 787 svs.realtime = sv.time - 100; 788 } 789 NET_Sleep(sv.time - svs.realtime); 790 return; 791 } 792 793 // update ping based on the last known frame from all clients 794 SV_CalcPings (); 795 796 // give the clients some timeslices 797 SV_GiveMsec (); 798 799 // let everything in the world think and move 800 SV_RunGameFrame (); 801 802 // send messages back to the clients that had packets read this frame 803 SV_SendClientMessages (); 804 805 // save the entire world state if recording a serverdemo 806 SV_RecordDemoMessage (); 807 808 // send a heartbeat to the master if needed 809 Master_Heartbeat (); 810 811 // clear teleport flags, etc for next frame 812 SV_PrepWorldFrame (); 813 814 } 815 816 //============================================================================ 817 818 /* 819 ================ 820 Master_Heartbeat 821 822 Send a message to the master every few minutes to 823 let it know we are alive, and log information 824 ================ 825 */ 826 #define HEARTBEAT_SECONDS 300 827 void Master_Heartbeat (void) 828 { 829 char *string; 830 int i; 831 832 833 if (!dedicated->value) 834 return; // only dedicated servers send heartbeats 835 836 if (!public_server->value) 837 return; // a private dedicated game 838 839 // check for time wraparound 840 if (svs.last_heartbeat > svs.realtime) 841 svs.last_heartbeat = svs.realtime; 842 843 if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000) 844 return; // not time to send yet 845 846 svs.last_heartbeat = svs.realtime; 847 848 // send the same string that we would give for a status OOB command 849 string = SV_StatusString(); 850 851 // send to group master 852 for (i=0 ; i<MAX_MASTERS ; i++) 853 if (master_adr[i].port) 854 { 855 Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); 856 Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "heartbeat\n%s", string); 857 } 858 } 859 860 /* 861 ================= 862 Master_Shutdown 863 864 Informs all masters that this server is going down 865 ================= 866 */ 867 void Master_Shutdown (void) 868 { 869 int i; 870 871 if (!dedicated->value) 872 return; // only dedicated servers send heartbeats 873 874 if (!public_server->value) 875 return; // a private dedicated game 876 877 // send to group master 878 for (i=0 ; i<MAX_MASTERS ; i++) 879 if (master_adr[i].port) 880 { 881 if (i > 0) 882 Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); 883 Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "shutdown"); 884 } 885 } 886 887 //============================================================================ 888 889 890 /* 891 ================= 892 SV_UserinfoChanged 893 894 Pull specific info from a newly changed userinfo string 895 into a more C freindly form. 896 ================= 897 */ 898 void SV_UserinfoChanged (client_t *cl) 899 { 900 char *val; 901 int i; 902 903 // call prog code to allow overrides 904 ge->ClientUserinfoChanged (cl->edict, cl->userinfo); 905 906 // name for C code 907 strncpy (cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name)-1); 908 // mask off high bit 909 for (i=0 ; i<sizeof(cl->name) ; i++) 910 cl->name[i] &= 127; 911 912 // rate command 913 val = Info_ValueForKey (cl->userinfo, "rate"); 914 if (strlen(val)) 915 { 916 i = atoi(val); 917 cl->rate = i; 918 if (cl->rate < 100) 919 cl->rate = 100; 920 if (cl->rate > 15000) 921 cl->rate = 15000; 922 } 923 else 924 cl->rate = 5000; 925 926 // msg command 927 val = Info_ValueForKey (cl->userinfo, "msg"); 928 if (strlen(val)) 929 { 930 cl->messagelevel = atoi(val); 931 } 932 933 } 934 935 936 //============================================================================ 937 938 /* 939 =============== 940 SV_Init 941 942 Only called at quake2.exe startup, not for each game 943 =============== 944 */ 945 void SV_Init (void) 946 { 947 SV_InitOperatorCommands (); 948 949 rcon_password = Cvar_Get ("rcon_password", "", 0); 950 Cvar_Get ("skill", "1", 0); 951 Cvar_Get ("deathmatch", "0", CVAR_LATCH); 952 Cvar_Get ("coop", "0", CVAR_LATCH); 953 Cvar_Get ("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO); 954 Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO); 955 Cvar_Get ("timelimit", "0", CVAR_SERVERINFO); 956 Cvar_Get ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH); 957 Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_NOSET);; 958 maxclients = Cvar_Get ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH); 959 hostname = Cvar_Get ("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE); 960 timeout = Cvar_Get ("timeout", "125", 0); 961 zombietime = Cvar_Get ("zombietime", "2", 0); 962 sv_showclamp = Cvar_Get ("showclamp", "0", 0); 963 sv_paused = Cvar_Get ("paused", "0", 0); 964 sv_timedemo = Cvar_Get ("timedemo", "0", 0); 965 sv_enforcetime = Cvar_Get ("sv_enforcetime", "0", 0); 966 allow_download = Cvar_Get ("allow_download", "0", CVAR_ARCHIVE); 967 allow_download_players = Cvar_Get ("allow_download_players", "0", CVAR_ARCHIVE); 968 allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE); 969 allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE); 970 allow_download_maps = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE); 971 972 sv_noreload = Cvar_Get ("sv_noreload", "0", 0); 973 974 sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH); 975 976 public_server = Cvar_Get ("public", "0", 0); 977 978 sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE); 979 980 SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer)); 981 } 982 983 /* 984 ================== 985 SV_FinalMessage 986 987 Used by SV_Shutdown to send a final message to all 988 connected clients before the server goes down. The messages are sent immediately, 989 not just stuck on the outgoing message list, because the server is going 990 to totally exit after returning from this function. 991 ================== 992 */ 993 void SV_FinalMessage (char *message, qboolean reconnect) 994 { 995 int i; 996 client_t *cl; 997 998 SZ_Clear (&net_message); 999 MSG_WriteByte (&net_message, svc_print); 1000 MSG_WriteByte (&net_message, PRINT_HIGH); 1001 MSG_WriteString (&net_message, message); 1002 1003 if (reconnect) 1004 MSG_WriteByte (&net_message, svc_reconnect); 1005 else 1006 MSG_WriteByte (&net_message, svc_disconnect); 1007 1008 // send it twice 1009 // stagger the packets to crutch operating system limited buffers 1010 1011 for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++) 1012 if (cl->state >= cs_connected) 1013 Netchan_Transmit (&cl->netchan, net_message.cursize 1014 , net_message.data); 1015 1016 for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++) 1017 if (cl->state >= cs_connected) 1018 Netchan_Transmit (&cl->netchan, net_message.cursize 1019 , net_message.data); 1020 } 1021 1022 1023 1024 /* 1025 ================ 1026 SV_Shutdown 1027 1028 Called when each game quits, 1029 before Sys_Quit or Sys_Error 1030 ================ 1031 */ 1032 void SV_Shutdown (char *finalmsg, qboolean reconnect) 1033 { 1034 if (svs.clients) 1035 SV_FinalMessage (finalmsg, reconnect); 1036 1037 Master_Shutdown (); 1038 SV_ShutdownGameProgs (); 1039 1040 // free current level 1041 if (sv.demofile) 1042 fclose (sv.demofile); 1043 memset (&sv, 0, sizeof(sv)); 1044 Com_SetServerState (sv.state); 1045 1046 // free server static data 1047 if (svs.clients) 1048 Z_Free (svs.clients); 1049 if (svs.client_entities) 1050 Z_Free (svs.client_entities); 1051 if (svs.demofile) 1052 fclose (svs.demofile); 1053 memset (&svs, 0, sizeof(svs)); 1054 } 1055