blob: 6e30503d7733b1efb723b7a216178e47d6340c82 [file] [log] [blame]
Franklin Wei5d05b9d2018-02-11 15:34:30 -05001/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
27A server can allways be started, even if the system started out as a client
28to a remote system.
29
30A client can NOT be started if the system started as a dedicated server.
31
32Memory is cleared / released when a server or client begins, not when they end.
33
34*/
35
36quakeparms_t host_parms;
37
38qboolean host_initialized; // true if into command execution
39
40double host_frametime;
41double host_time;
42double realtime; // without any filtering or bounding
43double oldrealtime; // last frame run
44int host_framecount;
45
46int host_hunklevel;
47
48int minimum_memory;
49
50client_t *host_client; // current client
51
52jmp_buf host_abortserver;
53
54byte *host_basepal;
55byte *host_colormap;
56
57cvar_t host_framerate = {"host_framerate","0"}; // set for slow motion
58cvar_t host_speeds = {"host_speeds","0"}; // set for running times
59
60cvar_t sys_ticrate = {"sys_ticrate","0.05"};
61cvar_t serverprofile = {"serverprofile","0"};
62
63cvar_t fraglimit = {"fraglimit","0",false,true};
64cvar_t timelimit = {"timelimit","0",false,true};
65cvar_t teamplay = {"teamplay","0",false,true};
66
67cvar_t samelevel = {"samelevel","0"};
68cvar_t noexit = {"noexit","0",false,true};
69
70#ifdef QUAKE2
71cvar_t developer = {"developer","1"}; // should be 0 for release!
72#else
73cvar_t developer = {"developer","0"};
74#endif
75
76cvar_t skill = {"skill","1"}; // 0 - 3
77cvar_t deathmatch = {"deathmatch","0"}; // 0, 1, or 2
78cvar_t coop = {"coop","0"}; // 0 or 1
79
80cvar_t pausable = {"pausable","1"};
81
82cvar_t temp1 = {"temp1","0"};
83
84
85/*
86================
87Host_EndGame
88================
89*/
90void Host_EndGame (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================
116Host_Error
117
118This shuts down both the client and server
119================
120*/
121void Host_Error (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================
154Host_FindMaxClients
155================
156*/
157void 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 = 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=======================
206Host_InitLocal
207======================
208*/
209void 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===============
241Host_WriteConfiguration
242
243Writes key bindings and archived cvars to config.cfg
244===============
245*/
246void 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=================
271SV_ClientPrintf
272
273Sends text across to be displayed
274FIXME: make this just a stuffed echo?
275=================
276*/
277void SV_ClientPrintf (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=================
292SV_BroadcastPrintf
293
294Sends text to all active clients
295=================
296*/
297void SV_BroadcastPrintf (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=================
317Host_ClientCommands
318
319Send text over to the client to be executed
320=================
321*/
322void Host_ClientCommands (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=====================
337SV_DropClient
338
339Called when the player is getting totally kicked off the host
340if (crash = true), don't bother sending signofs
341=====================
342*/
343void 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==================
400Host_ShutdownServer
401
402This only happens at the end of a game, not between levels
403==================
404*/
405void 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 = 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================
471Host_ClearMemory
472
473This clears all the memory used by both the client and server, but does
474not reinitialize anything.
475================
476*/
477void 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===================
496Host_FilterTime
497
498Returns false if the time is too short to run a frame
499===================
500*/
501qboolean 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===================
527Host_GetConsoleCommands
528
529Add them exactly as if they had been typed at the console
530===================
531*/
532void 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==================
548Host_ServerFrame
549
550==================
551*/
552#ifdef FPS_20
553
554void _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
568void 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
600void 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==================
628Host_Frame
629
630Runs all active servers
631==================
632*/
633void _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 = (time1 - time3)*1000;
719 time3 = Sys_FloatTime ();
720 pass2 = (time2 - time1)*1000;
721 pass3 = (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
729void 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 = 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
768extern int vcrFile;
769#define VCR_SIGNATURE 0x56435231
770// "VCR1"
771
772void 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 = 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 = 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====================
832Host_Init
833====================
834*/
835void 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 //printf("init 1");
855
856 Memory_Init (parms->membase, parms->memsize);
857 //printf("init 2");
858 Cbuf_Init ();
859 //printf("init 3");
860 Cmd_Init ();
861 //printf("init 4");
862 V_Init ();
863 //printf("init 5");
864 Chase_Init ();
865 //printf("init 6");
866 Host_InitVCR (parms);
867 //printf("init 7");
868 COM_Init (parms->basedir);
869 //printf("init 8");
870 Host_InitLocal ();
871 //printf("init 9");
872 W_LoadWadFile ("gfx.wad");
873 //printf("init 10");
874 Key_Init ();
875 Con_Init ();
876 M_Init ();
877 PR_Init ();
878 Mod_Init ();
879 NET_Init ();
880 //printf("init 11");
881 SV_Init ();
882 //printf("init 12");
883
884 Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
885 Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
886
887 R_InitTextures (); // needed even for dedicated servers
888
889 //printf("init 13");
890 if (cls.state != ca_dedicated)
891 {
892 host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp");
893 if (!host_basepal)
894 Sys_Error ("Couldn't load gfx/palette.lmp");
895 host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp");
896 if (!host_colormap)
897 Sys_Error ("Couldn't load gfx/colormap.lmp");
898
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500899 IN_Init ();
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500900 VID_Init (host_basepal);
901
902 Draw_Init ();
903 SCR_Init ();
904 R_Init ();
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500905 S_Init ();
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500906
907#ifdef GLQUAKE
908 // FIXME: doesn't use the new one-window approach yet
909 S_Init ();
910#endif
911
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500912 CDAudio_Init ();
913 Sbar_Init ();
914 CL_Init ();
Franklin Wei5d05b9d2018-02-11 15:34:30 -0500915 }
916
917 Cbuf_InsertText ("exec quake.rc\n");
918
919 Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
920 host_hunklevel = Hunk_LowMark ();
921
922 host_initialized = true;
923
924 Sys_Printf ("========Quake Initialized=========\n");
925
926 extern int enable_printf;
927 enable_printf = 0;
928}
929
930
931/*
932===============
933Host_Shutdown
934
935FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
936to run quit through here before the final handoff to the sys code.
937===============
938*/
939void Host_Shutdown(void)
940{
941 static qboolean isdown = false;
942
943 if (isdown)
944 {
945 printf ("recursive shutdown\n");
946 return;
947 }
948 isdown = true;
949
950// keep Con_Printf from trying to update the screen
951 scr_disabled_for_loading = true;
952
953 Host_WriteConfiguration ();
954
955 CDAudio_Shutdown ();
956 NET_Shutdown ();
957 S_Shutdown();
958 IN_Shutdown ();
959
960 Sys_Shutdown();
961
962 if (cls.state != ca_dedicated)
963 {
964 VID_Shutdown();
965 }
966}
967