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