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