|
|
1.1.1.4 ! root 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: */ 1.1 root 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; 1.1.1.2 root 38: cvar_t *allow_download_players; 39: cvar_t *allow_download_models; 40: cvar_t *allow_download_sounds; 41: cvar_t *allow_download_maps; 1.1 root 42: 1.1.1.3 root 43: cvar_t *sv_airaccelerate; 44: 1.1 root 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]; 1.1.1.3 root 112: static char status[MAX_MSGLEN - 16]; 1.1 root 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: { 1.1.1.3 root 149: Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString()); 150: #if 0 1.1 root 151: Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); 152: Com_Printf (SV_StatusString()); 153: Com_EndRedirect (); 1.1.1.3 root 154: #endif 1.1 root 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 1.1.1.2 root 250: svs.challenges[oldest].challenge = rand() & 0x7fff; 1.1 root 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); 1.1.1.2 root 297: userinfo[sizeof(userinfo) - 1] = 0; 1.1 root 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: { 1.1.1.2 root 345: if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000)) 1.1 root 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; 1.1.1.2 root 382: newcl->challenge = challenge; // save challenge for checksumming 1.1 root 383: 384: // get the game a chance to reject this connection or modify the userinfo 385: if (!(ge->ClientConnect (ent, userinfo))) 386: { 1.1.1.3 root 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" ); 1.1 root 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); 1.1.1.2 root 442: else 1.1 root 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: 1.1.1.2 root 533: #if 0 1.1 root 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; 1.1.1.2 root 538: #endif 1.1 root 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 1.1.1.2 root 553: #if 0 1.1 root 554: cl->ping = total*100/count - 100; 1.1.1.2 root 555: #else 556: cl->ping = total / count; 557: #endif 1.1 root 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: } 1.1.1.2 root 789: NET_Sleep(sv.time - svs.realtime); 1.1 root 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: 1.1.1.4 ! root 832: // pgm post3.19 change, cvar pointer not validated before dereferencing ! 833: if (!dedicated || !dedicated->value) 1.1 root 834: return; // only dedicated servers send heartbeats 835: 1.1.1.4 ! root 836: // pgm post3.19 change, cvar pointer not validated before dereferencing ! 837: if (!public_server || !public_server->value) 1.1 root 838: return; // a private dedicated game 839: 840: // check for time wraparound 841: if (svs.last_heartbeat > svs.realtime) 842: svs.last_heartbeat = svs.realtime; 843: 844: if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000) 845: return; // not time to send yet 846: 847: svs.last_heartbeat = svs.realtime; 848: 849: // send the same string that we would give for a status OOB command 850: string = SV_StatusString(); 851: 852: // send to group master 853: for (i=0 ; i<MAX_MASTERS ; i++) 854: if (master_adr[i].port) 855: { 856: Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); 857: Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "heartbeat\n%s", string); 858: } 859: } 860: 861: /* 862: ================= 863: Master_Shutdown 864: 865: Informs all masters that this server is going down 866: ================= 867: */ 868: void Master_Shutdown (void) 869: { 870: int i; 871: 1.1.1.4 ! root 872: // pgm post3.19 change, cvar pointer not validated before dereferencing ! 873: if (!dedicated || !dedicated->value) 1.1 root 874: return; // only dedicated servers send heartbeats 875: 1.1.1.4 ! root 876: // pgm post3.19 change, cvar pointer not validated before dereferencing ! 877: if (!public_server || !public_server->value) 1.1 root 878: return; // a private dedicated game 879: 880: // send to group master 881: for (i=0 ; i<MAX_MASTERS ; i++) 882: if (master_adr[i].port) 883: { 884: if (i > 0) 885: Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); 886: Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "shutdown"); 887: } 888: } 889: 890: //============================================================================ 891: 892: 893: /* 894: ================= 895: SV_UserinfoChanged 896: 897: Pull specific info from a newly changed userinfo string 898: into a more C freindly form. 899: ================= 900: */ 901: void SV_UserinfoChanged (client_t *cl) 902: { 903: char *val; 904: int i; 905: 906: // call prog code to allow overrides 907: ge->ClientUserinfoChanged (cl->edict, cl->userinfo); 908: 909: // name for C code 910: strncpy (cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name)-1); 911: // mask off high bit 912: for (i=0 ; i<sizeof(cl->name) ; i++) 913: cl->name[i] &= 127; 914: 915: // rate command 916: val = Info_ValueForKey (cl->userinfo, "rate"); 917: if (strlen(val)) 918: { 919: i = atoi(val); 920: cl->rate = i; 921: if (cl->rate < 100) 922: cl->rate = 100; 923: if (cl->rate > 15000) 924: cl->rate = 15000; 925: } 926: else 927: cl->rate = 5000; 928: 929: // msg command 930: val = Info_ValueForKey (cl->userinfo, "msg"); 931: if (strlen(val)) 932: { 933: cl->messagelevel = atoi(val); 934: } 935: 936: } 937: 938: 939: //============================================================================ 940: 941: /* 942: =============== 943: SV_Init 944: 945: Only called at quake2.exe startup, not for each game 946: =============== 947: */ 948: void SV_Init (void) 949: { 950: SV_InitOperatorCommands (); 951: 952: rcon_password = Cvar_Get ("rcon_password", "", 0); 953: Cvar_Get ("skill", "1", 0); 954: Cvar_Get ("deathmatch", "0", CVAR_LATCH); 955: Cvar_Get ("coop", "0", CVAR_LATCH); 956: Cvar_Get ("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO); 957: Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO); 958: Cvar_Get ("timelimit", "0", CVAR_SERVERINFO); 959: Cvar_Get ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH); 960: Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_NOSET);; 961: maxclients = Cvar_Get ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH); 962: hostname = Cvar_Get ("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE); 963: timeout = Cvar_Get ("timeout", "125", 0); 964: zombietime = Cvar_Get ("zombietime", "2", 0); 965: sv_showclamp = Cvar_Get ("showclamp", "0", 0); 966: sv_paused = Cvar_Get ("paused", "0", 0); 967: sv_timedemo = Cvar_Get ("timedemo", "0", 0); 968: sv_enforcetime = Cvar_Get ("sv_enforcetime", "0", 0); 1.1.1.4 ! root 969: allow_download = Cvar_Get ("allow_download", "1", CVAR_ARCHIVE); 1.1.1.2 root 970: allow_download_players = Cvar_Get ("allow_download_players", "0", CVAR_ARCHIVE); 971: allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE); 972: allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE); 973: allow_download_maps = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE); 974: 1.1 root 975: sv_noreload = Cvar_Get ("sv_noreload", "0", 0); 976: 1.1.1.3 root 977: sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH); 978: 1.1 root 979: public_server = Cvar_Get ("public", "0", 0); 980: 981: sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE); 982: 983: SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer)); 984: } 985: 986: /* 987: ================== 988: SV_FinalMessage 989: 990: Used by SV_Shutdown to send a final message to all 991: connected clients before the server goes down. The messages are sent immediately, 992: not just stuck on the outgoing message list, because the server is going 993: to totally exit after returning from this function. 994: ================== 995: */ 996: void SV_FinalMessage (char *message, qboolean reconnect) 997: { 998: int i; 999: client_t *cl; 1000: 1001: SZ_Clear (&net_message); 1002: MSG_WriteByte (&net_message, svc_print); 1003: MSG_WriteByte (&net_message, PRINT_HIGH); 1004: MSG_WriteString (&net_message, message); 1005: 1006: if (reconnect) 1007: MSG_WriteByte (&net_message, svc_reconnect); 1008: else 1009: MSG_WriteByte (&net_message, svc_disconnect); 1010: 1011: // send it twice 1012: // stagger the packets to crutch operating system limited buffers 1013: 1014: for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++) 1015: if (cl->state >= cs_connected) 1016: Netchan_Transmit (&cl->netchan, net_message.cursize 1017: , net_message.data); 1018: 1019: for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++) 1020: if (cl->state >= cs_connected) 1021: Netchan_Transmit (&cl->netchan, net_message.cursize 1022: , net_message.data); 1023: } 1024: 1025: 1026: 1027: /* 1028: ================ 1029: SV_Shutdown 1030: 1031: Called when each game quits, 1032: before Sys_Quit or Sys_Error 1033: ================ 1034: */ 1035: void SV_Shutdown (char *finalmsg, qboolean reconnect) 1036: { 1037: if (svs.clients) 1038: SV_FinalMessage (finalmsg, reconnect); 1039: 1040: Master_Shutdown (); 1041: SV_ShutdownGameProgs (); 1042: 1043: // free current level 1044: if (sv.demofile) 1045: fclose (sv.demofile); 1046: memset (&sv, 0, sizeof(sv)); 1047: Com_SetServerState (sv.state); 1048: 1049: // free server static data 1050: if (svs.clients) 1051: Z_Free (svs.clients); 1052: if (svs.client_entities) 1053: Z_Free (svs.client_entities); 1054: if (svs.demofile) 1055: fclose (svs.demofile); 1056: memset (&svs, 0, sizeof(svs)); 1057: } 1058:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.