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