1 /* 2 Copyright (C) 1996-1997 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 "qwsvdef.h" 22 23 quakeparms_t host_parms; 24 25 qboolean host_initialized; // true if into command execution (compatability) 26 27 double host_frametime; 28 double realtime; // without any filtering or bounding 29 30 int host_hunklevel; 31 32 netadr_t master_adr[MAX_MASTERS]; // address of group servers 33 34 client_t *host_client; // current client 35 36 cvar_t sv_mintic = {"sv_mintic","0.03"}; // bound the size of the 37 cvar_t sv_maxtic = {"sv_maxtic","0.1"}; // physics time tic 38 39 cvar_t developer = {"developer","0"}; // show extra messages 40 41 cvar_t timeout = {"timeout","65"}; // seconds without any message 42 cvar_t zombietime = {"zombietime", "2"}; // seconds to sink messages 43 // after disconnect 44 45 cvar_t rcon_password = {"rcon_password", ""}; // password for remote server commands 46 cvar_t password = {"password", ""}; // password for entering the game 47 cvar_t spectator_password = {"spectator_password", ""}; // password for entering as a sepctator 48 49 cvar_t allow_download = {"allow_download", "1"}; 50 cvar_t allow_download_skins = {"allow_download_skins", "1"}; 51 cvar_t allow_download_models = {"allow_download_models", "1"}; 52 cvar_t allow_download_sounds = {"allow_download_sounds", "1"}; 53 cvar_t allow_download_maps = {"allow_download_maps", "1"}; 54 55 cvar_t sv_highchars = {"sv_highchars", "1"}; 56 57 cvar_t sv_phs = {"sv_phs", "1"}; 58 59 cvar_t pausable = {"pausable", "1"}; 60 61 62 // 63 // game rules mirrored in svs.info 64 // 65 cvar_t fraglimit = {"fraglimit","0",false,true}; 66 cvar_t timelimit = {"timelimit","0",false,true}; 67 cvar_t teamplay = {"teamplay","0",false,true}; 68 cvar_t samelevel = {"samelevel","0", false, true}; 69 cvar_t maxclients = {"maxclients","8", false, true}; 70 cvar_t maxspectators = {"maxspectators","8", false, true}; 71 cvar_t deathmatch = {"deathmatch","1", false, true}; // 0, 1, or 2 72 cvar_t spawn = {"spawn","0", false, true}; 73 cvar_t watervis = {"watervis", "0", false, true}; 74 75 cvar_t hostname = {"hostname","unnamed", false, true}; 76 77 FILE *sv_logfile; 78 FILE *sv_fraglogfile; 79 80 void SV_AcceptClient (netadr_t adr, int userid, char *userinfo); 81 void Master_Shutdown (void); 82 83 //============================================================================ 84 85 qboolean ServerPaused(void) 86 { 87 return sv.paused; 88 } 89 90 /* 91 ================ 92 SV_Shutdown 93 94 Quake calls this before calling Sys_Quit or Sys_Error 95 ================ 96 */ 97 void SV_Shutdown (void) 98 { 99 Master_Shutdown (); 100 if (sv_logfile) 101 { 102 fclose (sv_logfile); 103 sv_logfile = NULL; 104 } 105 if (sv_fraglogfile) 106 { 107 fclose (sv_fraglogfile); 108 sv_logfile = NULL; 109 } 110 NET_Shutdown (); 111 } 112 113 /* 114 ================ 115 SV_Error 116 117 Sends a datagram to all the clients informing them of the server crash, 118 then exits 119 ================ 120 */ 121 void SV_Error (char *error, ...) 122 { 123 va_list argptr; 124 static char string[1024]; 125 static qboolean inerror = false; 126 127 if (inerror) 128 Sys_Error ("SV_Error: recursively entered (%s)", string); 129 130 inerror = true; 131 132 va_start (argptr,error); 133 vsprintf (string,error,argptr); 134 va_end (argptr); 135 136 Con_Printf ("SV_Error: %s\n",string); 137 138 SV_FinalMessage (va("server crashed: %s\n", string)); 139 140 SV_Shutdown (); 141 142 Sys_Error ("SV_Error: %s\n",string); 143 } 144 145 /* 146 ================== 147 SV_FinalMessage 148 149 Used by SV_Error and SV_Quit_f to send a final message to all connected 150 clients before the server goes down. The messages are sent immediately, 151 not just stuck on the outgoing message list, because the server is going 152 to totally exit after returning from this function. 153 ================== 154 */ 155 void SV_FinalMessage (char *message) 156 { 157 int i; 158 client_t *cl; 159 160 SZ_Clear (&net_message); 161 MSG_WriteByte (&net_message, svc_print); 162 MSG_WriteByte (&net_message, PRINT_HIGH); 163 MSG_WriteString (&net_message, message); 164 MSG_WriteByte (&net_message, svc_disconnect); 165 166 for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++) 167 if (cl->state >= cs_spawned) 168 Netchan_Transmit (&cl->netchan, net_message.cursize 169 , net_message.data); 170 } 171 172 173 174 /* 175 ===================== 176 SV_DropClient 177 178 Called when the player is totally leaving the server, either willingly 179 or unwillingly. This is NOT called if the entire server is quiting 180 or crashing. 181 ===================== 182 */ 183 void SV_DropClient (client_t *drop) 184 { 185 // add the disconnect 186 MSG_WriteByte (&drop->netchan.message, svc_disconnect); 187 188 if (drop->state == cs_spawned) 189 if (!drop->spectator) 190 { 191 // call the prog function for removing a client 192 // this will set the body to a dead frame, among other things 193 pr_global_struct->self = EDICT_TO_PROG(drop->edict); 194 PR_ExecuteProgram (pr_global_struct->ClientDisconnect); 195 } 196 else if (SpectatorDisconnect) 197 { 198 // call the prog function for removing a client 199 // this will set the body to a dead frame, among other things 200 pr_global_struct->self = EDICT_TO_PROG(drop->edict); 201 PR_ExecuteProgram (SpectatorDisconnect); 202 } 203 204 if (drop->spectator) 205 Con_Printf ("Spectator %s removed\n",drop->name); 206 else 207 Con_Printf ("Client %s removed\n",drop->name); 208 209 if (drop->download) 210 { 211 fclose (drop->download); 212 drop->download = NULL; 213 } 214 if (drop->upload) 215 { 216 fclose (drop->upload); 217 drop->upload = NULL; 218 } 219 *drop->uploadfn = 0; 220 221 drop->state = cs_zombie; // become free in a few seconds 222 drop->connection_started = realtime; // for zombie timeout 223 224 drop->old_frags = 0; 225 drop->edict->v.frags = 0; 226 drop->name[0] = 0; 227 memset (drop->userinfo, 0, sizeof(drop->userinfo)); 228 229 // send notification to all remaining clients 230 SV_FullClientUpdate (drop, &sv.reliable_datagram); 231 } 232 233 234 //==================================================================== 235 236 /* 237 =================== 238 SV_CalcPing 239 240 =================== 241 */ 242 int SV_CalcPing (client_t *cl) 243 { 244 float ping; 245 int i; 246 int count; 247 register client_frame_t *frame; 248 249 ping = 0; 250 count = 0; 251 for (frame = cl->frames, i=0 ; i<UPDATE_BACKUP ; i++, frame++) 252 { 253 if (frame->ping_time > 0) 254 { 255 ping += frame->ping_time; 256 count++; 257 } 258 } 259 if (!count) 260 return 9999; 261 ping /= count; 262 263 return ping*1000; 264 } 265 266 /* 267 =================== 268 SV_FullClientUpdate 269 270 Writes all update values to a sizebuf 271 =================== 272 */ 273 void SV_FullClientUpdate (client_t *client, sizebuf_t *buf) 274 { 275 int i; 276 char info[MAX_INFO_STRING]; 277 278 i = client - svs.clients; 279 280 //Sys_Printf("SV_FullClientUpdate: Updated frags for client %d\n", i); 281 282 MSG_WriteByte (buf, svc_updatefrags); 283 MSG_WriteByte (buf, i); 284 MSG_WriteShort (buf, client->old_frags); 285 286 MSG_WriteByte (buf, svc_updateping); 287 MSG_WriteByte (buf, i); 288 MSG_WriteShort (buf, SV_CalcPing (client)); 289 290 MSG_WriteByte (buf, svc_updatepl); 291 MSG_WriteByte (buf, i); 292 MSG_WriteByte (buf, client->lossage); 293 294 MSG_WriteByte (buf, svc_updateentertime); 295 MSG_WriteByte (buf, i); 296 MSG_WriteFloat (buf, realtime - client->connection_started); 297 298 strcpy (info, client->userinfo); 299 Info_RemovePrefixedKeys (info, '_'); // server passwords, etc 300 301 MSG_WriteByte (buf, svc_updateuserinfo); 302 MSG_WriteByte (buf, i); 303 MSG_WriteLong (buf, client->userid); 304 MSG_WriteString (buf, info); 305 } 306 307 /* 308 =================== 309 SV_FullClientUpdateToClient 310 311 Writes all update values to a client's reliable stream 312 =================== 313 */ 314 void SV_FullClientUpdateToClient (client_t *client, client_t *cl) 315 { 316 ClientReliableCheckBlock(cl, 24 + strlen(client->userinfo)); 317 if (cl->num_backbuf) { 318 SV_FullClientUpdate (client, &cl->backbuf); 319 ClientReliable_FinishWrite(cl); 320 } else 321 SV_FullClientUpdate (client, &cl->netchan.message); 322 } 323 324 325 /* 326 ============================================================================== 327 328 CONNECTIONLESS COMMANDS 329 330 ============================================================================== 331 */ 332 333 /* 334 ================ 335 SVC_Status 336 337 Responds with all the info that qplug or qspy can see 338 This message can be up to around 5k with worst case string lengths. 339 ================ 340 */ 341 void SVC_Status (void) 342 { 343 int i; 344 client_t *cl; 345 int ping; 346 int top, bottom; 347 348 Cmd_TokenizeString ("status"); 349 SV_BeginRedirect (RD_PACKET); 350 Con_Printf ("%s\n", svs.info); 351 for (i=0 ; i<MAX_CLIENTS ; i++) 352 { 353 cl = &svs.clients[i]; 354 if ((cl->state == cs_connected || cl->state == cs_spawned ) && !cl->spectator) 355 { 356 top = atoi(Info_ValueForKey (cl->userinfo, "topcolor")); 357 bottom = atoi(Info_ValueForKey (cl->userinfo, "bottomcolor")); 358 top = (top < 0) ? 0 : ((top > 13) ? 13 : top); 359 bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom); 360 ping = SV_CalcPing (cl); 361 Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid, 362 cl->old_frags, (int)(realtime - cl->connection_started)/60, 363 ping, cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, bottom); 364 } 365 } 366 SV_EndRedirect (); 367 } 368 369 /* 370 =================== 371 SV_CheckLog 372 373 =================== 374 */ 375 #define LOG_HIGHWATER 4096 376 #define LOG_FLUSH 10*60 377 void SV_CheckLog (void) 378 { 379 sizebuf_t *sz; 380 381 sz = &svs.log[svs.logsequence&1]; 382 383 // bump sequence if allmost full, or ten minutes have passed and 384 // there is something still sitting there 385 if (sz->cursize > LOG_HIGHWATER 386 || (realtime - svs.logtime > LOG_FLUSH && sz->cursize) ) 387 { 388 // swap buffers and bump sequence 389 svs.logtime = realtime; 390 svs.logsequence++; 391 sz = &svs.log[svs.logsequence&1]; 392 sz->cursize = 0; 393 Con_Printf ("beginning fraglog sequence %i\n", svs.logsequence); 394 } 395 396 } 397 398 /* 399 ================ 400 SVC_Log 401 402 Responds with all the logged frags for ranking programs. 403 If a sequence number is passed as a parameter and it is 404 the same as the current sequence, an A2A_NACK will be returned 405 instead of the data. 406 ================ 407 */ 408 void SVC_Log (void) 409 { 410 int seq; 411 char data[MAX_DATAGRAM+64]; 412 413 if (Cmd_Argc() == 2) 414 seq = atoi(Cmd_Argv(1)); 415 else 416 seq = -1; 417 418 if (seq == svs.logsequence-1 || !sv_fraglogfile) 419 { // they allready have this data, or we aren't logging frags 420 data[0] = A2A_NACK; 421 NET_SendPacket (1, data, net_from); 422 return; 423 } 424 425 Con_DPrintf ("sending log %i to %s\n", svs.logsequence-1, NET_AdrToString(net_from)); 426 427 sprintf (data, "stdlog %i\n", svs.logsequence-1); 428 strcat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)]); 429 430 NET_SendPacket (strlen(data)+1, data, net_from); 431 } 432 433 /* 434 ================ 435 SVC_Ping 436 437 Just responds with an acknowledgement 438 ================ 439 */ 440 void SVC_Ping (void) 441 { 442 char data; 443 444 data = A2A_ACK; 445 446 NET_SendPacket (1, &data, net_from); 447 } 448 449 /* 450 ================= 451 SVC_GetChallenge 452 453 Returns a challenge number that can be used 454 in a subsequent client_connect command. 455 We do this to prevent denial of service attacks that 456 flood the server with invalid connection IPs. With a 457 challenge, they must give a valid IP address. 458 ================= 459 */ 460 void SVC_GetChallenge (void) 461 { 462 int i; 463 int oldest; 464 int oldestTime; 465 466 oldest = 0; 467 oldestTime = 0x7fffffff; 468 469 // see if we already have a challenge for this ip 470 for (i = 0 ; i < MAX_CHALLENGES ; i++) 471 { 472 if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) 473 break; 474 if (svs.challenges[i].time < oldestTime) 475 { 476 oldestTime = svs.challenges[i].time; 477 oldest = i; 478 } 479 } 480 481 if (i == MAX_CHALLENGES) 482 { 483 // overwrite the oldest 484 svs.challenges[oldest].challenge = (rand() << 16) ^ rand(); 485 svs.challenges[oldest].adr = net_from; 486 svs.challenges[oldest].time = realtime; 487 i = oldest; 488 } 489 490 // send it back 491 Netchan_OutOfBandPrint (net_from, "%c%i", S2C_CHALLENGE, 492 svs.challenges[i].challenge); 493 } 494 495 /* 496 ================== 497 SVC_DirectConnect 498 499 A connection request that did not come from the master 500 ================== 501 */ 502 void SVC_DirectConnect (void) 503 { 504 char userinfo[1024]; 505 static int userid; 506 netadr_t adr; 507 int i; 508 client_t *cl, *newcl; 509 client_t temp; 510 edict_t *ent; 511 int edictnum; 512 char *s; 513 int clients, spectators; 514 qboolean spectator; 515 int qport; 516 int version; 517 int challenge; 518 519 version = atoi(Cmd_Argv(1)); 520 if (version != PROTOCOL_VERSION) 521 { 522 Netchan_OutOfBandPrint (net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, VERSION); 523 Con_Printf ("* rejected connect from version %i\n", version); 524 return; 525 } 526 527 qport = atoi(Cmd_Argv(2)); 528 529 challenge = atoi(Cmd_Argv(3)); 530 531 // note an extra byte is needed to replace spectator key 532 strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-2); 533 userinfo[sizeof(userinfo) - 2] = 0; 534 535 // see if the challenge is valid 536 for (i=0 ; i<MAX_CHALLENGES ; i++) 537 { 538 if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) 539 { 540 if (challenge == svs.challenges[i].challenge) 541 break; // good 542 Netchan_OutOfBandPrint (net_from, "%c\nBad challenge.\n", A2C_PRINT); 543 return; 544 } 545 } 546 if (i == MAX_CHALLENGES) 547 { 548 Netchan_OutOfBandPrint (net_from, "%c\nNo challenge for address.\n", A2C_PRINT); 549 return; 550 } 551 552 // check for password or spectator_password 553 s = Info_ValueForKey (userinfo, "spectator"); 554 if (s[0] && strcmp(s, "0")) 555 { 556 if (spectator_password.string[0] && 557 stricmp(spectator_password.string, "none") && 558 strcmp(spectator_password.string, s) ) 559 { // failed 560 Con_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from)); 561 Netchan_OutOfBandPrint (net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT); 562 return; 563 } 564 Info_RemoveKey (userinfo, "spectator"); // remove passwd 565 Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING); 566 spectator = true; 567 } 568 else 569 { 570 s = Info_ValueForKey (userinfo, "password"); 571 if (password.string[0] && 572 stricmp(password.string, "none") && 573 strcmp(password.string, s) ) 574 { 575 Con_Printf ("%s:password failed\n", NET_AdrToString (net_from)); 576 Netchan_OutOfBandPrint (net_from, "%c\nserver requires a password\n\n", A2C_PRINT); 577 return; 578 } 579 spectator = false; 580 Info_RemoveKey (userinfo, "password"); // remove passwd 581 } 582 583 adr = net_from; 584 userid++; // so every client gets a unique id 585 586 newcl = &temp; 587 memset (newcl, 0, sizeof(client_t)); 588 589 newcl->userid = userid; 590 591 // works properly 592 if (!sv_highchars.value) { 593 byte *p, *q; 594 595 for (p = (byte *)newcl->userinfo, q = (byte *)userinfo; 596 *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++) 597 if (*q > 31 && *q <= 127) 598 *p++ = *q; 599 } else 600 strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1); 601 602 // if there is allready a slot for this ip, drop it 603 for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) 604 { 605 if (cl->state == cs_free) 606 continue; 607 if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) 608 && ( cl->netchan.qport == qport 609 || adr.port == cl->netchan.remote_address.port )) 610 { 611 if (cl->state == cs_connected) { 612 Con_Printf("%s:dup connect\n", NET_AdrToString (adr)); 613 userid--; 614 return; 615 } 616 617 Con_Printf ("%s:reconnect\n", NET_AdrToString (adr)); 618 SV_DropClient (cl); 619 break; 620 } 621 } 622 623 // count up the clients and spectators 624 clients = 0; 625 spectators = 0; 626 for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) 627 { 628 if (cl->state == cs_free) 629 continue; 630 if (cl->spectator) 631 spectators++; 632 else 633 clients++; 634 } 635 636 // if at server limits, refuse connection 637 if ( maxclients.value > MAX_CLIENTS ) 638 Cvar_SetValue ("maxclients", MAX_CLIENTS); 639 if (maxspectators.value > MAX_CLIENTS) 640 Cvar_SetValue ("maxspectators", MAX_CLIENTS); 641 if (maxspectators.value + maxclients.value > MAX_CLIENTS) 642 Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.value + maxclients.value); 643 if ( (spectator && spectators >= (int)maxspectators.value) 644 || (!spectator && clients >= (int)maxclients.value) ) 645 { 646 Con_Printf ("%s:full connect\n", NET_AdrToString (adr)); 647 Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT); 648 return; 649 } 650 651 // find a client slot 652 newcl = NULL; 653 for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) 654 { 655 if (cl->state == cs_free) 656 { 657 newcl = cl; 658 break; 659 } 660 } 661 if (!newcl) 662 { 663 Con_Printf ("WARNING: miscounted available clients\n"); 664 return; 665 } 666 667 668 // build a new connection 669 // accept the new client 670 // this is the only place a client_t is ever initialized 671 *newcl = temp; 672 673 Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION ); 674 675 edictnum = (newcl-svs.clients)+1; 676 677 Netchan_Setup (&newcl->netchan , adr, qport); 678 679 newcl->state = cs_connected; 680 681 newcl->datagram.allowoverflow = true; 682 newcl->datagram.data = newcl->datagram_buf; 683 newcl->datagram.maxsize = sizeof(newcl->datagram_buf); 684 685 // spectator mode can ONLY be set at join time 686 newcl->spectator = spectator; 687 688 ent = EDICT_NUM(edictnum); 689 newcl->edict = ent; 690 691 // parse some info from the info strings 692 SV_ExtractFromUserinfo (newcl); 693 694 // JACK: Init the floodprot stuff. 695 for (i=0; i<10; i++) 696 newcl->whensaid[i] = 0.0; 697 newcl->whensaidhead = 0; 698 newcl->lockedtill = 0; 699 700 // call the progs to get default spawn parms for the new client 701 PR_ExecuteProgram (pr_global_struct->SetNewParms); 702 for (i=0 ; i<NUM_SPAWN_PARMS ; i++) 703 newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i]; 704 705 if (newcl->spectator) 706 Con_Printf ("Spectator %s connected\n", newcl->name); 707 else 708 Con_DPrintf ("Client %s connected\n", newcl->name); 709 newcl->sendinfo = true; 710 } 711 712 int Rcon_Validate (void) 713 { 714 if (!strlen (rcon_password.string)) 715 return 0; 716 717 if (strcmp (Cmd_Argv(1), rcon_password.string) ) 718 return 0; 719 720 return 1; 721 } 722 723 /* 724 =============== 725 SVC_RemoteCommand 726 727 A client issued an rcon command. 728 Shift down the remaining args 729 Redirect all printfs 730 =============== 731 */ 732 void SVC_RemoteCommand (void) 733 { 734 int i; 735 char remaining[1024]; 736 737 738 if (!Rcon_Validate ()) { 739 Con_Printf ("Bad rcon from %s:\n%s\n" 740 , NET_AdrToString (net_from), net_message.data+4); 741 742 SV_BeginRedirect (RD_PACKET); 743 744 Con_Printf ("Bad rcon_password.\n"); 745 746 } else { 747 748 Con_Printf ("Rcon from %s:\n%s\n" 749 , NET_AdrToString (net_from), net_message.data+4); 750 751 SV_BeginRedirect (RD_PACKET); 752 753 remaining[0] = 0; 754 755 for (i=2 ; i<Cmd_Argc() ; i++) 756 { 757 strcat (remaining, Cmd_Argv(i) ); 758 strcat (remaining, " "); 759 } 760 761 Cmd_ExecuteString (remaining); 762 763 } 764 765 SV_EndRedirect (); 766 } 767 768 769 /* 770 ================= 771 SV_ConnectionlessPacket 772 773 A connectionless packet has four leading 0xff 774 characters to distinguish it from a game channel. 775 Clients that are in the game can still send 776 connectionless packets. 777 ================= 778 */ 779 void SV_ConnectionlessPacket (void) 780 { 781 char *s; 782 char *c; 783 784 MSG_BeginReading (); 785 MSG_ReadLong (); // skip the -1 marker 786 787 s = MSG_ReadStringLine (); 788 789 Cmd_TokenizeString (s); 790 791 c = Cmd_Argv(0); 792 793 if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) ) 794 { 795 SVC_Ping (); 796 return; 797 } 798 if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n') ) 799 { 800 Con_Printf ("A2A_ACK from %s\n", NET_AdrToString (net_from)); 801 return; 802 } 803 else if (!strcmp(c,"status")) 804 { 805 SVC_Status (); 806 return; 807 } 808 else if (!strcmp(c,"log")) 809 { 810 SVC_Log (); 811 return; 812 } 813 else if (!strcmp(c,"connect")) 814 { 815 SVC_DirectConnect (); 816 return; 817 } 818 else if (!strcmp(c,"getchallenge")) 819 { 820 SVC_GetChallenge (); 821 return; 822 } 823 else if (!strcmp(c, "rcon")) 824 SVC_RemoteCommand (); 825 else 826 Con_Printf ("bad connectionless packet from %s:\n%s\n" 827 , NET_AdrToString (net_from), s); 828 } 829 830 /* 831 ============================================================================== 832 833 PACKET FILTERING 834 835 836 You can add or remove addresses from the filter list with: 837 838 addip <ip> 839 removeip <ip> 840 841 The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40". 842 843 Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host. 844 845 listip 846 Prints the current list of filters. 847 848 writeip 849 Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion. 850 851 filterban <0 or 1> 852 853 If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting. 854 855 If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network. 856 857 858 ============================================================================== 859 */ 860 861 862 typedef struct 863 { 864 unsigned mask; 865 unsigned compare; 866 } ipfilter_t; 867 868 #define MAX_IPFILTERS 1024 869 870 ipfilter_t ipfilters[MAX_IPFILTERS]; 871 int numipfilters; 872 873 cvar_t filterban = {"filterban", "1"}; 874 875 /* 876 ================= 877 StringToFilter 878 ================= 879 */ 880 qboolean StringToFilter (char *s, ipfilter_t *f) 881 { 882 char num[128]; 883 int i, j; 884 byte b[4]; 885 byte m[4]; 886 887 for (i=0 ; i<4 ; i++) 888 { 889 b[i] = 0; 890 m[i] = 0; 891 } 892 893 for (i=0 ; i<4 ; i++) 894 { 895 if (*s < '0' || *s > '9') 896 { 897 Con_Printf ("Bad filter address: %s\n", s); 898 return false; 899 } 900 901 j = 0; 902 while (*s >= '0' && *s <= '9') 903 { 904 num[j++] = *s++; 905 } 906 num[j] = 0; 907 b[i] = atoi(num); 908 if (b[i] != 0) 909 m[i] = 255; 910 911 if (!*s) 912 break; 913 s++; 914 } 915 916 f->mask = *(unsigned *)m; 917 f->compare = *(unsigned *)b; 918 919 return true; 920 } 921 922 /* 923 ================= 924 SV_AddIP_f 925 ================= 926 */ 927 void SV_AddIP_f (void) 928 { 929 int i; 930 931 for (i=0 ; i<numipfilters ; i++) 932 if (ipfilters[i].compare == 0xffffffff) 933 break; // free spot 934 if (i == numipfilters) 935 { 936 if (numipfilters == MAX_IPFILTERS) 937 { 938 Con_Printf ("IP filter list is full\n"); 939 return; 940 } 941 numipfilters++; 942 } 943 944 if (!StringToFilter (Cmd_Argv(1), &ipfilters[i])) 945 ipfilters[i].compare = 0xffffffff; 946 } 947 948 /* 949 ================= 950 SV_RemoveIP_f 951 ================= 952 */ 953 void SV_RemoveIP_f (void) 954 { 955 ipfilter_t f; 956 int i, j; 957 958 if (!StringToFilter (Cmd_Argv(1), &f)) 959 return; 960 for (i=0 ; i<numipfilters ; i++) 961 if (ipfilters[i].mask == f.mask 962 && ipfilters[i].compare == f.compare) 963 { 964 for (j=i+1 ; j<numipfilters ; j++) 965 ipfilters[j-1] = ipfilters[j]; 966 numipfilters--; 967 Con_Printf ("Removed.\n"); 968 return; 969 } 970 Con_Printf ("Didn't find %s.\n", Cmd_Argv(1)); 971 } 972 973 /* 974 ================= 975 SV_ListIP_f 976 ================= 977 */ 978 void SV_ListIP_f (void) 979 { 980 int i; 981 byte b[4]; 982 983 Con_Printf ("Filter list:\n"); 984 for (i=0 ; i<numipfilters ; i++) 985 { 986 *(unsigned *)b = ipfilters[i].compare; 987 Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]); 988 } 989 } 990 991 /* 992 ================= 993 SV_WriteIP_f 994 ================= 995 */ 996 void SV_WriteIP_f (void) 997 { 998 FILE *f; 999 char name[MAX_OSPATH]; 1000 byte b[4]; 1001 int i; 1002 1003 sprintf (name, "%s/listip.cfg", com_gamedir); 1004 1005 Con_Printf ("Writing %s.\n", name); 1006 1007 f = fopen (name, "wb"); 1008 if (!f) 1009 { 1010 Con_Printf ("Couldn't open %s\n", name); 1011 return; 1012 } 1013 1014 for (i=0 ; i<numipfilters ; i++) 1015 { 1016 *(unsigned *)b = ipfilters[i].compare; 1017 fprintf (f, "addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]); 1018 } 1019 1020 fclose (f); 1021 } 1022 1023 /* 1024 ================= 1025 SV_SendBan 1026 ================= 1027 */ 1028 void SV_SendBan (void) 1029 { 1030 char data[128]; 1031 1032 data[0] = data[1] = data[2] = data[3] = 0xff; 1033 data[4] = A2C_PRINT; 1034 data[5] = 0; 1035 strcat (data, "\nbanned.\n"); 1036 1037 NET_SendPacket (strlen(data), data, net_from); 1038 } 1039 1040 /* 1041 ================= 1042 SV_FilterPacket 1043 ================= 1044 */ 1045 qboolean SV_FilterPacket (void) 1046 { 1047 int i; 1048 unsigned in; 1049 1050 in = *(unsigned *)net_from.ip; 1051 1052 for (i=0 ; i<numipfilters ; i++) 1053 if ( (in & ipfilters[i].mask) == ipfilters[i].compare) 1054 return filterban.value; 1055 1056 return !filterban.value; 1057 } 1058 1059 //============================================================================ 1060 1061 /* 1062 ================= 1063 SV_ReadPackets 1064 ================= 1065 */ 1066 void SV_ReadPackets (void) 1067 { 1068 int i; 1069 client_t *cl; 1070 qboolean good; 1071 int qport; 1072 1073 good = false; 1074 while (NET_GetPacket ()) 1075 { 1076 if (SV_FilterPacket ()) 1077 { 1078 SV_SendBan (); // tell them we aren't listening... 1079 continue; 1080 } 1081 1082 // check for connectionless packet (0xffffffff) first 1083 if (*(int *)net_message.data == -1) 1084 { 1085 SV_ConnectionlessPacket (); 1086 continue; 1087 } 1088 1089 // read the qport out of the message so we can fix up 1090 // stupid address translating routers 1091 MSG_BeginReading (); 1092 MSG_ReadLong (); // sequence number 1093 MSG_ReadLong (); // sequence number 1094 qport = MSG_ReadShort () & 0xffff; 1095 1096 // check for packets from connected clients 1097 for (i=0, cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) 1098 { 1099 if (cl->state == cs_free) 1100 continue; 1101 if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address)) 1102 continue; 1103 if (cl->netchan.qport != qport) 1104 continue; 1105 if (cl->netchan.remote_address.port != net_from.port) 1106 { 1107 Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n"); 1108 cl->netchan.remote_address.port = net_from.port; 1109 } 1110 if (Netchan_Process(&cl->netchan)) 1111 { // this is a valid, sequenced packet, so process it 1112 svs.stats.packets++; 1113 good = true; 1114 cl->send_message = true; // reply at end of frame 1115 if (cl->state != cs_zombie) 1116 SV_ExecuteClientMessage (cl); 1117 } 1118 break; 1119 } 1120 1121 if (i != MAX_CLIENTS) 1122 continue; 1123 1124 // packet is not from a known client 1125 // Con_Printf ("%s:sequenced packet without connection\n" 1126 // ,NET_AdrToString(net_from)); 1127 } 1128 } 1129 1130 /* 1131 ================== 1132 SV_CheckTimeouts 1133 1134 If a packet has not been received from a client in timeout.value 1135 seconds, drop the conneciton. 1136 1137 When a client is normally dropped, the client_t goes into a zombie state 1138 for a few seconds to make sure any final reliable message gets resent 1139 if necessary 1140 ================== 1141 */ 1142 void SV_CheckTimeouts (void) 1143 { 1144 int i; 1145 client_t *cl; 1146 float droptime; 1147 int nclients; 1148 1149 droptime = realtime - timeout.value; 1150 nclients = 0; 1151 1152 for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++) 1153 { 1154 if (cl->state == cs_connected || cl->state == cs_spawned) { 1155 if (!cl->spectator) 1156 nclients++; 1157 if (cl->netchan.last_received < droptime) { 1158 SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); 1159 SV_DropClient (cl); 1160 cl->state = cs_free; // don't bother with zombie state 1161 } 1162 } 1163 if (cl->state == cs_zombie && 1164 realtime - cl->connection_started > zombietime.value) 1165 { 1166 cl->state = cs_free; // can now be reused 1167 } 1168 } 1169 if (sv.paused && !nclients) { 1170 // nobody left, unpause the server 1171 SV_TogglePause("Pause released since no players are left.\n"); 1172 } 1173 } 1174 1175 /* 1176 =================== 1177 SV_GetConsoleCommands 1178 1179 Add them exactly as if they had been typed at the console 1180 =================== 1181 */ 1182 void SV_GetConsoleCommands (void) 1183 { 1184 char *cmd; 1185 1186 while (1) 1187 { 1188 cmd = Sys_ConsoleInput (); 1189 if (!cmd) 1190 break; 1191 Cbuf_AddText (cmd); 1192 } 1193 } 1194 1195 /* 1196 =================== 1197 SV_CheckVars 1198 1199 =================== 1200 */ 1201 void SV_CheckVars (void) 1202 { 1203 static char *pw, *spw; 1204 int v; 1205 1206 if (password.string == pw && spectator_password.string == spw) 1207 return; 1208 pw = password.string; 1209 spw = spectator_password.string; 1210 1211 v = 0; 1212 if (pw && pw[0] && strcmp(pw, "none")) 1213 v |= 1; 1214 if (spw && spw[0] && strcmp(spw, "none")) 1215 v |= 2; 1216 1217 Con_Printf ("Updated needpass.\n"); 1218 if (!v) 1219 Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING); 1220 else 1221 Info_SetValueForKey (svs.info, "needpass", va("%i",v), MAX_SERVERINFO_STRING); 1222 } 1223 1224 /* 1225 ================== 1226 SV_Frame 1227 1228 ================== 1229 */ 1230 void SV_Frame (float time) 1231 { 1232 static double start, end; 1233 1234 start = Sys_DoubleTime (); 1235 svs.stats.idle += start - end; 1236 1237 // keep the random time dependent 1238 rand (); 1239 1240 // decide the simulation time 1241 if (!sv.paused) { 1242 realtime += time; 1243 sv.time += time; 1244 } 1245 1246 // check timeouts 1247 SV_CheckTimeouts (); 1248 1249 // toggle the log buffer if full 1250 SV_CheckLog (); 1251 1252 // move autonomous things around if enough time has passed 1253 if (!sv.paused) 1254 SV_Physics (); 1255 1256 // get packets 1257 SV_ReadPackets (); 1258 1259 // check for commands typed to the host 1260 SV_GetConsoleCommands (); 1261 1262 // process console commands 1263 Cbuf_Execute (); 1264 1265 SV_CheckVars (); 1266 1267 // send messages back to the clients that had packets read this frame 1268 SV_SendClientMessages (); 1269 1270 // send a heartbeat to the master if needed 1271 Master_Heartbeat (); 1272 1273 // collect timing statistics 1274 end = Sys_DoubleTime (); 1275 svs.stats.active += end-start; 1276 if (++svs.stats.count == STATFRAMES) 1277 { 1278 svs.stats.latched_active = svs.stats.active; 1279 svs.stats.latched_idle = svs.stats.idle; 1280 svs.stats.latched_packets = svs.stats.packets; 1281 svs.stats.active = 0; 1282 svs.stats.idle = 0; 1283 svs.stats.packets = 0; 1284 svs.stats.count = 0; 1285 } 1286 } 1287 1288 /* 1289 =============== 1290 SV_InitLocal 1291 =============== 1292 */ 1293 void SV_InitLocal (void) 1294 { 1295 int i; 1296 extern cvar_t sv_maxvelocity; 1297 extern cvar_t sv_gravity; 1298 extern cvar_t sv_aim; 1299 extern cvar_t sv_stopspeed; 1300 extern cvar_t sv_spectatormaxspeed; 1301 extern cvar_t sv_accelerate; 1302 extern cvar_t sv_airaccelerate; 1303 extern cvar_t sv_wateraccelerate; 1304 extern cvar_t sv_friction; 1305 extern cvar_t sv_waterfriction; 1306 1307 SV_InitOperatorCommands (); 1308 SV_UserInit (); 1309 1310 Cvar_RegisterVariable (&rcon_password); 1311 Cvar_RegisterVariable (&password); 1312 Cvar_RegisterVariable (&spectator_password); 1313 1314 Cvar_RegisterVariable (&sv_mintic); 1315 Cvar_RegisterVariable (&sv_maxtic); 1316 1317 Cvar_RegisterVariable (&fraglimit); 1318 Cvar_RegisterVariable (&timelimit); 1319 Cvar_RegisterVariable (&teamplay); 1320 Cvar_RegisterVariable (&samelevel); 1321 Cvar_RegisterVariable (&maxclients); 1322 Cvar_RegisterVariable (&maxspectators); 1323 Cvar_RegisterVariable (&hostname); 1324 Cvar_RegisterVariable (&deathmatch); 1325 Cvar_RegisterVariable (&spawn); 1326 Cvar_RegisterVariable (&watervis); 1327 1328 Cvar_RegisterVariable (&developer); 1329 1330 Cvar_RegisterVariable (&timeout); 1331 Cvar_RegisterVariable (&zombietime); 1332 1333 Cvar_RegisterVariable (&sv_maxvelocity); 1334 Cvar_RegisterVariable (&sv_gravity); 1335 Cvar_RegisterVariable (&sv_stopspeed); 1336 Cvar_RegisterVariable (&sv_maxspeed); 1337 Cvar_RegisterVariable (&sv_spectatormaxspeed); 1338 Cvar_RegisterVariable (&sv_accelerate); 1339 Cvar_RegisterVariable (&sv_airaccelerate); 1340 Cvar_RegisterVariable (&sv_wateraccelerate); 1341 Cvar_RegisterVariable (&sv_friction); 1342 Cvar_RegisterVariable (&sv_waterfriction); 1343 1344 Cvar_RegisterVariable (&sv_aim); 1345 1346 Cvar_RegisterVariable (&filterban); 1347 1348 Cvar_RegisterVariable (&allow_download); 1349 Cvar_RegisterVariable (&allow_download_skins); 1350 Cvar_RegisterVariable (&allow_download_models); 1351 Cvar_RegisterVariable (&allow_download_sounds); 1352 Cvar_RegisterVariable (&allow_download_maps); 1353 1354 Cvar_RegisterVariable (&sv_highchars); 1355 1356 Cvar_RegisterVariable (&sv_phs); 1357 1358 Cvar_RegisterVariable (&pausable); 1359 1360 Cmd_AddCommand ("addip", SV_AddIP_f); 1361 Cmd_AddCommand ("removeip", SV_RemoveIP_f); 1362 Cmd_AddCommand ("listip", SV_ListIP_f); 1363 Cmd_AddCommand ("writeip", SV_WriteIP_f); 1364 1365 for (i=0 ; i<MAX_MODELS ; i++) 1366 sprintf (localmodels[i], "*%i", i); 1367 1368 Info_SetValueForStarKey (svs.info, "*version", va("%4.2f", VERSION), MAX_SERVERINFO_STRING); 1369 1370 // init fraglog stuff 1371 svs.logsequence = 1; 1372 svs.logtime = realtime; 1373 svs.log[0].data = svs.log_buf[0]; 1374 svs.log[0].maxsize = sizeof(svs.log_buf[0]); 1375 svs.log[0].cursize = 0; 1376 svs.log[0].allowoverflow = true; 1377 svs.log[1].data = svs.log_buf[1]; 1378 svs.log[1].maxsize = sizeof(svs.log_buf[1]); 1379 svs.log[1].cursize = 0; 1380 svs.log[1].allowoverflow = true; 1381 } 1382 1383 1384 //============================================================================ 1385 1386 /* 1387 ================ 1388 Master_Heartbeat 1389 1390 Send a message to the master every few minutes to 1391 let it know we are alive, and log information 1392 ================ 1393 */ 1394 #define HEARTBEAT_SECONDS 300 1395 void Master_Heartbeat (void) 1396 { 1397 char string[2048]; 1398 int active; 1399 int i; 1400 1401 if (realtime - svs.last_heartbeat < HEARTBEAT_SECONDS) 1402 return; // not time to send yet 1403 1404 svs.last_heartbeat = realtime; 1405 1406 // 1407 // count active users 1408 // 1409 active = 0; 1410 for (i=0 ; i<MAX_CLIENTS ; i++) 1411 if (svs.clients[i].state == cs_connected || 1412 svs.clients[i].state == cs_spawned ) 1413 active++; 1414 1415 svs.heartbeat_sequence++; 1416 sprintf (string, "%c\n%i\n%i\n", S2M_HEARTBEAT, 1417 svs.heartbeat_sequence, active); 1418 1419 1420 // send to group master 1421 for (i=0 ; i<MAX_MASTERS ; i++) 1422 if (master_adr[i].port) 1423 { 1424 Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); 1425 NET_SendPacket (strlen(string), string, master_adr[i]); 1426 } 1427 } 1428 1429 /* 1430 ================= 1431 Master_Shutdown 1432 1433 Informs all masters that this server is going down 1434 ================= 1435 */ 1436 void Master_Shutdown (void) 1437 { 1438 char string[2048]; 1439 int i; 1440 1441 sprintf (string, "%c\n", S2M_SHUTDOWN); 1442 1443 // send to group master 1444 for (i=0 ; i<MAX_MASTERS ; i++) 1445 if (master_adr[i].port) 1446 { 1447 Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); 1448 NET_SendPacket (strlen(string), string, master_adr[i]); 1449 } 1450 } 1451 1452 /* 1453 ================= 1454 SV_ExtractFromUserinfo 1455 1456 Pull specific info from a newly changed userinfo string 1457 into a more C freindly form. 1458 ================= 1459 */ 1460 void SV_ExtractFromUserinfo (client_t *cl) 1461 { 1462 char *val, *p, *q; 1463 int i; 1464 client_t *client; 1465 int dupc = 1; 1466 char newname[80]; 1467 1468 1469 // name for C code 1470 val = Info_ValueForKey (cl->userinfo, "name"); 1471 1472 // trim user name 1473 strncpy(newname, val, sizeof(newname) - 1); 1474 newname[sizeof(newname) - 1] = 0; 1475 1476 for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++) 1477 ; 1478 1479 if (p != newname && !*p) { 1480 //white space only 1481 strcpy(newname, "unnamed"); 1482 p = newname; 1483 } 1484 1485 if (p != newname && *p) { 1486 for (q = newname; *p; *q++ = *p++) 1487 ; 1488 *q = 0; 1489 } 1490 for (p = newname + strlen(newname) - 1; p != newname && (*p == ' ' || *p == '\r' || *p == '\n') ; p--) 1491 ; 1492 p[1] = 0; 1493 1494 if (strcmp(val, newname)) { 1495 Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); 1496 val = Info_ValueForKey (cl->userinfo, "name"); 1497 } 1498 1499 if (!val[0] || !stricmp(val, "console")) { 1500 Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING); 1501 val = Info_ValueForKey (cl->userinfo, "name"); 1502 } 1503 1504 // check to see if another user by the same name exists 1505 while (1) { 1506 for (i=0, client = svs.clients ; i<MAX_CLIENTS ; i++, client++) { 1507 if (client->state != cs_spawned || client == cl) 1508 continue; 1509 if (!stricmp(client->name, val)) 1510 break; 1511 } 1512 if (i != MAX_CLIENTS) { // dup name 1513 if (strlen(val) > sizeof(cl->name) - 1) 1514 val[sizeof(cl->name) - 4] = 0; 1515 p = val; 1516 1517 if (val[0] == '(') 1518 if (val[2] == ')') 1519 p = val + 3; 1520 else if (val[3] == ')') 1521 p = val + 4; 1522 1523 sprintf(newname, "(%d)%-.40s", dupc++, p); 1524 Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); 1525 val = Info_ValueForKey (cl->userinfo, "name"); 1526 } else 1527 break; 1528 } 1529 1530 if (strncmp(val, cl->name, strlen(cl->name))) { 1531 if (!sv.paused) { 1532 if (!cl->lastnametime || realtime - cl->lastnametime > 5) { 1533 cl->lastnamecount = 0; 1534 cl->lastnametime = realtime; 1535 } else if (cl->lastnamecount++ > 4) { 1536 SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name spam\n", cl->name); 1537 SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game for name spamming\n"); 1538 SV_DropClient (cl); 1539 return; 1540 } 1541 } 1542 1543 if (cl->state >= cs_spawned && !cl->spectator) 1544 SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n", cl->name, val); 1545 } 1546 1547 1548 strncpy (cl->name, val, sizeof(cl->name)-1); 1549 1550 // rate command 1551 val = Info_ValueForKey (cl->userinfo, "rate"); 1552 if (strlen(val)) 1553 { 1554 i = atoi(val); 1555 if (i < 500) 1556 i = 500; 1557 if (i > 10000) 1558 i = 10000; 1559 cl->netchan.rate = 1.0/i; 1560 } 1561 1562 // msg command 1563 val = Info_ValueForKey (cl->userinfo, "msg"); 1564 if (strlen(val)) 1565 { 1566 cl->messagelevel = atoi(val); 1567 } 1568 1569 } 1570 1571 1572 //============================================================================ 1573 1574 /* 1575 ==================== 1576 SV_InitNet 1577 ==================== 1578 */ 1579 void SV_InitNet (void) 1580 { 1581 int port; 1582 int p; 1583 1584 port = PORT_SERVER; 1585 p = COM_CheckParm ("-port"); 1586 if (p && p < com_argc) 1587 { 1588 port = atoi(com_argv[p+1]); 1589 Con_Printf ("Port: %i\n", port); 1590 } 1591 NET_Init (port); 1592 1593 Netchan_Init (); 1594 1595 // heartbeats will allways be sent to the id master 1596 svs.last_heartbeat = -99999; // send immediately 1597 // NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr); 1598 } 1599 1600 1601 /* 1602 ==================== 1603 SV_Init 1604 ==================== 1605 */ 1606 void SV_Init (quakeparms_t *parms) 1607 { 1608 COM_InitArgv (parms->argc, parms->argv); 1609 COM_AddParm ("-game"); 1610 COM_AddParm ("qw"); 1611 1612 if (COM_CheckParm ("-minmemory")) 1613 parms->memsize = MINIMUM_MEMORY; 1614 1615 host_parms = *parms; 1616 1617 if (parms->memsize < MINIMUM_MEMORY) 1618 SV_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000); 1619 1620 Memory_Init (parms->membase, parms->memsize); 1621 Cbuf_Init (); 1622 Cmd_Init (); 1623 1624 COM_Init (); 1625 1626 PR_Init (); 1627 Mod_Init (); 1628 1629 SV_InitNet (); 1630 1631 SV_InitLocal (); 1632 Sys_Init (); 1633 Pmove_Init (); 1634 1635 Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); 1636 host_hunklevel = Hunk_LowMark (); 1637 1638 Cbuf_InsertText ("exec server.cfg\n"); 1639 1640 host_initialized = true; 1641 1642 Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); 1643 Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0)); 1644 1645 Con_Printf ("\nServer Version %4.2f (Build %04d)\n\n", VERSION, build_number()); 1646 1647 Con_Printf ("======== QuakeWorld Initialized ========\n"); 1648 1649 // process command line arguments 1650 Cmd_StuffCmds_f (); 1651 Cbuf_Execute (); 1652 1653 // if a map wasn't specified on the command line, spawn start.map 1654 if (sv.state == ss_dead) 1655 Cmd_ExecuteString ("map start"); 1656 if (sv.state == ss_dead) 1657 SV_Error ("Couldn't spawn a server"); 1658 } 1659