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