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