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 // host.c -- coordinates spawning and killing of local servers 21 22 #include "quakedef.h" 23 #include "r_local.h" 24 25 /* 26 27 A server can allways be started, even if the system started out as a client 28 to a remote system. 29 30 A client can NOT be started if the system started as a dedicated server. 31 32 Memory is cleared / released when a server or client begins, not when they end. 33 34 */ 35 36 quakeparms_t host_parms; 37 38 qboolean host_initialized; // true if into command execution 39 40 double host_frametime; 41 double host_time; 42 double realtime; // without any filtering or bounding 43 double oldrealtime; // last frame run 44 int host_framecount; 45 46 int host_hunklevel; 47 48 int minimum_memory; 49 50 client_t *host_client; // current client 51 52 jmp_buf host_abortserver; 53 54 byte *host_basepal; 55 byte *host_colormap; 56 57 cvar_t host_framerate = CVAR2("host_framerate","0"); // set for slow motion 58 cvar_t host_speeds = CVAR2("host_speeds","0"); // set for running times 59 60 cvar_t sys_ticrate = CVAR2("sys_ticrate","0.05"); 61 cvar_t serverprofile = CVAR2("serverprofile","0"); 62 63 cvar_t fraglimit = CVAR4("fraglimit","0",false,true); 64 cvar_t timelimit = CVAR4("timelimit","0",false,true); 65 cvar_t teamplay = CVAR4("teamplay","0",false,true); 66 67 cvar_t samelevel = CVAR2("samelevel","0"); 68 cvar_t noexit = CVAR4("noexit","0",false,true); 69 70 #ifdef QUAKE2 71 cvar_t developer = CVAR2("developer","1"); // should be 0 for release! 72 #else 73 cvar_t developer = CVAR2("developer","0"); 74 #endif 75 76 cvar_t skill = CVAR2("skill","1"); // 0 - 3 77 cvar_t deathmatch = CVAR2("deathmatch","0"); // 0, 1, or 2 78 cvar_t coop = CVAR2("coop","0"); // 0 or 1 79 80 cvar_t pausable = CVAR2("pausable","1"); 81 82 cvar_t temp1 = CVAR2("temp1","0"); 83 84 85 /* 86 ================ 87 Host_EndGame 88 ================ 89 */ 90 void Host_EndGame (const char *message, ...) 91 { 92 va_list argptr; 93 char string[1024]; 94 95 va_start (argptr,message); 96 vsprintf (string,message,argptr); 97 va_end (argptr); 98 Con_DPrintf ("Host_EndGame: %s\n",string); 99 100 if (sv.active) 101 Host_ShutdownServer (false); 102 103 if (cls.state == ca_dedicated) 104 Sys_Error ("Host_EndGame: %s\n",string); // dedicated servers exit 105 106 if (cls.demonum != -1) 107 CL_NextDemo (); 108 else 109 CL_Disconnect (); 110 111 longjmp (host_abortserver, 1); 112 } 113 114 /* 115 ================ 116 Host_Error 117 118 This shuts down both the client and server 119 ================ 120 */ 121 void Host_Error (const char *error, ...) 122 { 123 va_list argptr; 124 char string[1024]; 125 static qboolean inerror = false; 126 127 if (inerror) 128 Sys_Error ("Host_Error: recursively entered"); 129 inerror = true; 130 131 SCR_EndLoadingPlaque (); // reenable screen updates 132 133 va_start (argptr,error); 134 vsprintf (string,error,argptr); 135 va_end (argptr); 136 Con_Printf ("Host_Error: %s\n",string); 137 138 if (sv.active) 139 Host_ShutdownServer (false); 140 141 if (cls.state == ca_dedicated) 142 Sys_Error ("Host_Error: %s\n",string); // dedicated servers exit 143 144 CL_Disconnect (); 145 cls.demonum = -1; 146 147 inerror = false; 148 149 longjmp (host_abortserver, 1); 150 } 151 152 /* 153 ================ 154 Host_FindMaxClients 155 ================ 156 */ 157 void Host_FindMaxClients (void) 158 { 159 int i; 160 161 svs.maxclients = 1; 162 163 i = COM_CheckParm ("-dedicated"); 164 if (i) 165 { 166 cls.state = ca_dedicated; 167 if (i != (com_argc - 1)) 168 { 169 svs.maxclients = Q_atoi (com_argv[i+1]); 170 } 171 else 172 svs.maxclients = 8; 173 } 174 else 175 cls.state = ca_disconnected; 176 177 i = COM_CheckParm ("-listen"); 178 if (i) 179 { 180 if (cls.state == ca_dedicated) 181 Sys_Error ("Only one of -dedicated or -listen can be specified"); 182 if (i != (com_argc - 1)) 183 svs.maxclients = Q_atoi (com_argv[i+1]); 184 else 185 svs.maxclients = 8; 186 } 187 if (svs.maxclients < 1) 188 svs.maxclients = 8; 189 else if (svs.maxclients > MAX_SCOREBOARD) 190 svs.maxclients = MAX_SCOREBOARD; 191 192 svs.maxclientslimit = svs.maxclients; 193 if (svs.maxclientslimit < 4) 194 svs.maxclientslimit = 4; 195 svs.clients = (client_s*) Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients"); 196 197 if (svs.maxclients > 1) 198 Cvar_SetValue ("deathmatch", 1.0); 199 else 200 Cvar_SetValue ("deathmatch", 0.0); 201 } 202 203 204 /* 205 ======================= 206 Host_InitLocal 207 ====================== 208 */ 209 void Host_InitLocal (void) 210 { 211 Host_InitCommands (); 212 213 Cvar_RegisterVariable (&host_framerate); 214 Cvar_RegisterVariable (&host_speeds); 215 216 Cvar_RegisterVariable (&sys_ticrate); 217 Cvar_RegisterVariable (&serverprofile); 218 219 Cvar_RegisterVariable (&fraglimit); 220 Cvar_RegisterVariable (&timelimit); 221 Cvar_RegisterVariable (&teamplay); 222 Cvar_RegisterVariable (&samelevel); 223 Cvar_RegisterVariable (&noexit); 224 Cvar_RegisterVariable (&skill); 225 Cvar_RegisterVariable (&developer); 226 Cvar_RegisterVariable (&deathmatch); 227 Cvar_RegisterVariable (&coop); 228 229 Cvar_RegisterVariable (&pausable); 230 231 Cvar_RegisterVariable (&temp1); 232 233 Host_FindMaxClients (); 234 235 host_time = 1.0; // so a think at time 0 won't get called 236 } 237 238 239 /* 240 =============== 241 Host_WriteConfiguration 242 243 Writes key bindings and archived cvars to config.cfg 244 =============== 245 */ 246 void Host_WriteConfiguration (void) 247 { 248 FILE *f; 249 250 // dedicated servers initialize the host but don't parse and set the 251 // config.cfg cvars 252 if (host_initialized & !isDedicated) 253 { 254 f = fopen (va("%s/config.cfg",com_gamedir), "w"); 255 if (!f) 256 { 257 Con_Printf ("Couldn't write config.cfg.\n"); 258 return; 259 } 260 261 Key_WriteBindings (f); 262 Cvar_WriteVariables (f); 263 264 fclose (f); 265 } 266 } 267 268 269 /* 270 ================= 271 SV_ClientPrintf 272 273 Sends text across to be displayed 274 FIXME: make this just a stuffed echo? 275 ================= 276 */ 277 void SV_ClientPrintf (const char *fmt, ...) 278 { 279 va_list argptr; 280 char string[1024]; 281 282 va_start (argptr,fmt); 283 vsprintf (string, fmt,argptr); 284 va_end (argptr); 285 286 MSG_WriteByte (&host_client->message, svc_print); 287 MSG_WriteString (&host_client->message, string); 288 } 289 290 /* 291 ================= 292 SV_BroadcastPrintf 293 294 Sends text to all active clients 295 ================= 296 */ 297 void SV_BroadcastPrintf (const char *fmt, ...) 298 { 299 va_list argptr; 300 char string[1024]; 301 int i; 302 303 va_start (argptr,fmt); 304 vsprintf (string, fmt,argptr); 305 va_end (argptr); 306 307 for (i=0 ; i<svs.maxclients ; i++) 308 if (svs.clients[i].active && svs.clients[i].spawned) 309 { 310 MSG_WriteByte (&svs.clients[i].message, svc_print); 311 MSG_WriteString (&svs.clients[i].message, string); 312 } 313 } 314 315 /* 316 ================= 317 Host_ClientCommands 318 319 Send text over to the client to be executed 320 ================= 321 */ 322 void Host_ClientCommands (const char *fmt, ...) 323 { 324 va_list argptr; 325 char string[1024]; 326 327 va_start (argptr,fmt); 328 vsprintf (string, fmt,argptr); 329 va_end (argptr); 330 331 MSG_WriteByte (&host_client->message, svc_stufftext); 332 MSG_WriteString (&host_client->message, string); 333 } 334 335 /* 336 ===================== 337 SV_DropClient 338 339 Called when the player is getting totally kicked off the host 340 if (crash = true), don't bother sending signofs 341 ===================== 342 */ 343 void SV_DropClient (qboolean crash) 344 { 345 int saveSelf; 346 int i; 347 client_t *client; 348 349 if (!crash) 350 { 351 // send any final messages (don't check for errors) 352 if (NET_CanSendMessage (host_client->netconnection)) 353 { 354 MSG_WriteByte (&host_client->message, svc_disconnect); 355 NET_SendMessage (host_client->netconnection, &host_client->message); 356 } 357 358 if (host_client->edict && host_client->spawned) 359 { 360 // call the prog function for removing a client 361 // this will set the body to a dead frame, among other things 362 saveSelf = pr_global_struct->self; 363 pr_global_struct->self = EDICT_TO_PROG(host_client->edict); 364 PR_ExecuteProgram (pr_global_struct->ClientDisconnect); 365 pr_global_struct->self = saveSelf; 366 } 367 368 Sys_Printf ("Client %s removed\n",host_client->name); 369 } 370 371 // break the net connection 372 NET_Close (host_client->netconnection); 373 host_client->netconnection = NULL; 374 375 // free the client (the body stays around) 376 host_client->active = false; 377 host_client->name[0] = 0; 378 host_client->old_frags = -999999; 379 net_activeconnections--; 380 381 // send notification to all clients 382 for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++) 383 { 384 if (!client->active) 385 continue; 386 MSG_WriteByte (&client->message, svc_updatename); 387 MSG_WriteByte (&client->message, host_client - svs.clients); 388 MSG_WriteString (&client->message, ""); 389 MSG_WriteByte (&client->message, svc_updatefrags); 390 MSG_WriteByte (&client->message, host_client - svs.clients); 391 MSG_WriteShort (&client->message, 0); 392 MSG_WriteByte (&client->message, svc_updatecolors); 393 MSG_WriteByte (&client->message, host_client - svs.clients); 394 MSG_WriteByte (&client->message, 0); 395 } 396 } 397 398 /* 399 ================== 400 Host_ShutdownServer 401 402 This only happens at the end of a game, not between levels 403 ================== 404 */ 405 void Host_ShutdownServer(qboolean crash) 406 { 407 int i; 408 int count; 409 sizebuf_t buf; 410 char message[4]; 411 double start; 412 413 if (!sv.active) 414 return; 415 416 sv.active = false; 417 418 // stop all client sounds immediately 419 if (cls.state == ca_connected) 420 CL_Disconnect (); 421 422 // flush any pending messages - like the score!!! 423 start = Sys_FloatTime(); 424 do 425 { 426 count = 0; 427 for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) 428 { 429 if (host_client->active && host_client->message.cursize) 430 { 431 if (NET_CanSendMessage (host_client->netconnection)) 432 { 433 NET_SendMessage(host_client->netconnection, &host_client->message); 434 SZ_Clear (&host_client->message); 435 } 436 else 437 { 438 NET_GetMessage(host_client->netconnection); 439 count++; 440 } 441 } 442 } 443 if ((Sys_FloatTime() - start) > 3.0) 444 break; 445 } 446 while (count); 447 448 // make sure all the clients know we're disconnecting 449 buf.data = (byte*) message; 450 buf.maxsize = 4; 451 buf.cursize = 0; 452 MSG_WriteByte(&buf, svc_disconnect); 453 count = NET_SendToAll(&buf, 5); 454 if (count) 455 Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count); 456 457 for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) 458 if (host_client->active) 459 SV_DropClient(crash); 460 461 // 462 // clear structures 463 // 464 memset (&sv, 0, sizeof(sv)); 465 memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t)); 466 } 467 468 469 /* 470 ================ 471 Host_ClearMemory 472 473 This clears all the memory used by both the client and server, but does 474 not reinitialize anything. 475 ================ 476 */ 477 void Host_ClearMemory (void) 478 { 479 Con_DPrintf ("Clearing memory\n"); 480 D_FlushCaches (); 481 Mod_ClearAll (); 482 if (host_hunklevel) 483 Hunk_FreeToLowMark (host_hunklevel); 484 485 cls.signon = 0; 486 memset (&sv, 0, sizeof(sv)); 487 memset (&cl, 0, sizeof(cl)); 488 } 489 490 491 //============================================================================ 492 493 494 /* 495 =================== 496 Host_FilterTime 497 498 Returns false if the time is too short to run a frame 499 =================== 500 */ 501 qboolean Host_FilterTime (float time) 502 { 503 realtime += time; 504 505 if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0) 506 return false; // framerate is too high 507 508 host_frametime = realtime - oldrealtime; 509 oldrealtime = realtime; 510 511 if (host_framerate.value > 0) 512 host_frametime = host_framerate.value; 513 else 514 { // don't allow really long or short frames 515 if (host_frametime > 0.1) 516 host_frametime = 0.1; 517 if (host_frametime < 0.001) 518 host_frametime = 0.001; 519 } 520 521 return true; 522 } 523 524 525 /* 526 =================== 527 Host_GetConsoleCommands 528 529 Add them exactly as if they had been typed at the console 530 =================== 531 */ 532 void Host_GetConsoleCommands (void) 533 { 534 char *cmd; 535 536 while (1) 537 { 538 cmd = Sys_ConsoleInput (); 539 if (!cmd) 540 break; 541 Cbuf_AddText (cmd); 542 } 543 } 544 545 546 /* 547 ================== 548 Host_ServerFrame 549 550 ================== 551 */ 552 #ifdef FPS_20 553 554 void _Host_ServerFrame (void) 555 { 556 // run the world state 557 pr_global_struct->frametime = host_frametime; 558 559 // read client messages 560 SV_RunClients (); 561 562 // move things around and think 563 // always pause in single player if in console or menus 564 if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) ) 565 SV_Physics (); 566 } 567 568 void Host_ServerFrame (void) 569 { 570 float save_host_frametime; 571 float temp_host_frametime; 572 573 // run the world state 574 pr_global_struct->frametime = host_frametime; 575 576 // set the time and clear the general datagram 577 SV_ClearDatagram (); 578 579 // check for new clients 580 SV_CheckForNewClients (); 581 582 temp_host_frametime = save_host_frametime = host_frametime; 583 while(temp_host_frametime > (1.0/72.0)) 584 { 585 if (temp_host_frametime > 0.05) 586 host_frametime = 0.05; 587 else 588 host_frametime = temp_host_frametime; 589 temp_host_frametime -= host_frametime; 590 _Host_ServerFrame (); 591 } 592 host_frametime = save_host_frametime; 593 594 // send all messages to the clients 595 SV_SendClientMessages (); 596 } 597 598 #else 599 600 void Host_ServerFrame (void) 601 { 602 // run the world state 603 pr_global_struct->frametime = host_frametime; 604 605 // set the time and clear the general datagram 606 SV_ClearDatagram (); 607 608 // check for new clients 609 SV_CheckForNewClients (); 610 611 // read client messages 612 SV_RunClients (); 613 614 // move things around and think 615 // always pause in single player if in console or menus 616 if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) ) 617 SV_Physics (); 618 619 // send all messages to the clients 620 SV_SendClientMessages (); 621 } 622 623 #endif 624 625 626 /* 627 ================== 628 Host_Frame 629 630 Runs all active servers 631 ================== 632 */ 633 void _Host_Frame (float time) 634 { 635 static double time1 = 0; 636 static double time2 = 0; 637 static double time3 = 0; 638 int pass1, pass2, pass3; 639 640 if (setjmp (host_abortserver) ) 641 return; // something bad happened, or the server disconnected 642 643 // keep the random time dependent 644 rand (); 645 646 // decide the simulation time 647 if (!Host_FilterTime (time)) 648 return; // don't run too fast, or packets will flood out 649 650 // get new key events 651 Sys_SendKeyEvents (); 652 653 // allow mice or other external controllers to add commands 654 IN_Commands (); 655 656 // process console commands 657 Cbuf_Execute (); 658 659 NET_Poll(); 660 661 // if running the server locally, make intentions now 662 if (sv.active) 663 CL_SendCmd (); 664 665 //------------------- 666 // 667 // server operations 668 // 669 //------------------- 670 671 // check for commands typed to the host 672 Host_GetConsoleCommands (); 673 674 if (sv.active) 675 Host_ServerFrame (); 676 677 //------------------- 678 // 679 // client operations 680 // 681 //------------------- 682 683 // if running the server remotely, send intentions now after 684 // the incoming messages have been read 685 if (!sv.active) 686 CL_SendCmd (); 687 688 host_time += host_frametime; 689 690 // fetch results from server 691 if (cls.state == ca_connected) 692 { 693 CL_ReadFromServer (); 694 } 695 696 // update video 697 if (host_speeds.value) 698 time1 = Sys_FloatTime (); 699 700 SCR_UpdateScreen (); 701 702 if (host_speeds.value) 703 time2 = Sys_FloatTime (); 704 705 // update audio 706 if (cls.signon == SIGNONS) 707 { 708 S_Update (r_origin, vpn, vright, vup); 709 CL_DecayLights (); 710 } 711 else 712 S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); 713 714 CDAudio_Update(); 715 716 if (host_speeds.value) 717 { 718 pass1 = (int) ((time1 - time3)*1000); 719 time3 = Sys_FloatTime (); 720 pass2 = (int) ((time2 - time1)*1000); 721 pass3 = (int) ((time3 - time2)*1000); 722 Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n", 723 pass1+pass2+pass3, pass1, pass2, pass3); 724 } 725 726 host_framecount++; 727 } 728 729 void Host_Frame (float time) 730 { 731 double time1, time2; 732 static double timetotal; 733 static int timecount; 734 int i, c, m; 735 736 if (!serverprofile.value) 737 { 738 _Host_Frame (time); 739 return; 740 } 741 742 time1 = Sys_FloatTime (); 743 _Host_Frame (time); 744 time2 = Sys_FloatTime (); 745 746 timetotal += time2 - time1; 747 timecount++; 748 749 if (timecount < 1000) 750 return; 751 752 m = (int) (timetotal*1000/timecount); 753 timecount = 0; 754 timetotal = 0; 755 c = 0; 756 for (i=0 ; i<svs.maxclients ; i++) 757 { 758 if (svs.clients[i].active) 759 c++; 760 } 761 762 Con_Printf ("serverprofile: %2i clients %2i msec\n", c, m); 763 } 764 765 //============================================================================ 766 767 768 extern int vcrFile; 769 #define VCR_SIGNATURE 0x56435231 770 // "VCR1" 771 772 void Host_InitVCR (quakeparms_t *parms) 773 { 774 int i, len, n; 775 char *p; 776 777 if (COM_CheckParm("-playback")) 778 { 779 if (com_argc != 2) 780 Sys_Error("No other parameters allowed with -playback\n"); 781 782 Sys_FileOpenRead("quake.vcr", &vcrFile); 783 if (vcrFile == -1) 784 Sys_Error("playback file not found\n"); 785 786 Sys_FileRead (vcrFile, &i, sizeof(int)); 787 if (i != VCR_SIGNATURE) 788 Sys_Error("Invalid signature in vcr file\n"); 789 790 Sys_FileRead (vcrFile, &com_argc, sizeof(int)); 791 com_argv = (const char**) malloc(com_argc * sizeof(char *)); 792 com_argv[0] = parms->argv[0]; 793 for (i = 0; i < com_argc; i++) 794 { 795 Sys_FileRead (vcrFile, &len, sizeof(int)); 796 p = (char*) malloc(len); 797 Sys_FileRead (vcrFile, p, len); 798 com_argv[i+1] = p; 799 } 800 com_argc++; /* add one for arg[0] */ 801 parms->argc = com_argc; 802 parms->argv = com_argv; 803 } 804 805 if ( (n = COM_CheckParm("-record")) != 0) 806 { 807 vcrFile = Sys_FileOpenWrite("quake.vcr"); 808 809 i = VCR_SIGNATURE; 810 Sys_FileWrite(vcrFile, &i, sizeof(int)); 811 i = com_argc - 1; 812 Sys_FileWrite(vcrFile, &i, sizeof(int)); 813 for (i = 1; i < com_argc; i++) 814 { 815 if (i == n) 816 { 817 len = 10; 818 Sys_FileWrite(vcrFile, &len, sizeof(int)); 819 Sys_FileWrite(vcrFile, "-playback", len); 820 continue; 821 } 822 len = Q_strlen(com_argv[i]) + 1; 823 Sys_FileWrite(vcrFile, &len, sizeof(int)); 824 Sys_FileWrite(vcrFile, com_argv[i], len); 825 } 826 } 827 828 } 829 830 /* 831 ==================== 832 Host_Init 833 ==================== 834 */ 835 void Host_Init (quakeparms_t *parms) 836 { 837 838 if (standard_quake) 839 minimum_memory = MINIMUM_MEMORY; 840 else 841 minimum_memory = MINIMUM_MEMORY_LEVELPAK; 842 843 if (COM_CheckParm ("-minmemory")) 844 parms->memsize = minimum_memory; 845 846 host_parms = *parms; 847 848 if (parms->memsize < minimum_memory) 849 Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms->memsize / (float)0x100000); 850 851 com_argc = parms->argc; 852 com_argv = parms->argv; 853 854 Memory_Init (parms->membase, parms->memsize); 855 Cbuf_Init (); 856 Cmd_Init (); 857 V_Init (); 858 Chase_Init (); 859 Host_InitVCR (parms); 860 COM_Init (parms->basedir); 861 Host_InitLocal (); 862 W_LoadWadFile ("gfx.wad"); 863 Key_Init (); 864 Con_Init (); 865 M_Init (); 866 PR_Init (); 867 Mod_Init (); 868 NET_Init (); 869 SV_Init (); 870 871 Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); 872 Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0)); 873 874 R_InitTextures (); // needed even for dedicated servers 875 876 if (cls.state != ca_dedicated) 877 { 878 host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp"); 879 if (!host_basepal) 880 Sys_Error ("Couldn't load gfx/palette.lmp"); 881 host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp"); 882 if (!host_colormap) 883 Sys_Error ("Couldn't load gfx/colormap.lmp"); 884 885 #ifndef _WIN32 // on non win32, mouse comes before video for security reasons 886 IN_Init (); 887 #endif 888 VID_Init (host_basepal); 889 890 Draw_Init (); 891 SCR_Init (); 892 R_Init (); 893 894 #ifndef _WIN32 895 // on Win32, sound initialization has to come before video initialization, so we 896 // can put up a popup if the sound hardware is in use 897 898 // Actually S_Init is called from inside VID_Init. So don't call here. 899 // S_Init (); 900 #else 901 902 #ifdef GLQUAKE 903 // FIXME: doesn't use the new one-window approach yet 904 S_Init (); 905 #endif 906 907 #endif // _WIN32 908 CDAudio_Init (); 909 Sbar_Init (); 910 CL_Init (); 911 #ifdef _WIN32 // on non win32, mouse comes before video for security reasons 912 IN_Init (); 913 #endif 914 } 915 916 Cbuf_InsertText ("exec quake.rc\n"); 917 918 Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); 919 host_hunklevel = Hunk_LowMark (); 920 921 host_initialized = true; 922 923 Sys_Printf ("========Quake Initialized=========\n"); 924 } 925 926 927 /* 928 =============== 929 Host_Shutdown 930 931 FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better 932 to run quit through here before the final handoff to the sys code. 933 =============== 934 */ 935 void Host_Shutdown(void) 936 { 937 static qboolean isdown = false; 938 939 if (isdown) 940 { 941 printf ("recursive shutdown\n"); 942 return; 943 } 944 isdown = true; 945 946 // keep Con_Printf from trying to update the screen 947 scr_disabled_for_loading = true; 948 949 Host_WriteConfiguration (); 950 951 CDAudio_Shutdown (); 952 NET_Shutdown (); 953 S_Shutdown(); 954 IN_Shutdown (); 955 956 if (cls.state != ca_dedicated) 957 { 958 VID_Shutdown(); 959 } 960 } 961 962