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