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 */
Host_EndGame(const char * message,...)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 */
Host_Error(const char * error,...)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 */
Host_FindMaxClients(void)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 */
Host_InitLocal(void)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 */
Host_WriteConfiguration(void)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 */
SV_ClientPrintf(const char * fmt,...)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 */
SV_BroadcastPrintf(const char * fmt,...)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 */
Host_ClientCommands(const char * fmt,...)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 */
SV_DropClient(qboolean crash)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 */
Host_ShutdownServer(qboolean crash)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 */
Host_ClearMemory(void)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 */
Host_FilterTime(float time)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 */
Host_GetConsoleCommands(void)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
_Host_ServerFrame(void)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
Host_ServerFrame(void)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
Host_ServerFrame(void)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 */
_Host_Frame(float time)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
Host_Frame(float time)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
Host_InitVCR(quakeparms_t * parms)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 */
Host_Init(quakeparms_t * parms)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 */
Host_Shutdown(void)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