]> icculus.org git repositories - divverent/darkplaces.git/blob - host.c
cleaned up vid_glx.c somewhat, it can now quit properly when the close button is...
[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 <time.h>
23 #include "quakedef.h"
24 #include "cl_video.h"
25
26 /*
27
28 A server can always be started, even if the system started out as a client
29 to a remote system.
30
31 A client can NOT be started if the system started as a dedicated server.
32
33 Memory is cleared / released when a server or client begins, not when they end.
34
35 */
36
37 quakeparms_t host_parms;
38
39 qboolean        host_initialized;               // true if into command execution
40 qboolean        host_loopactive = false;        // LordHavoc: used to turn Host_Error into Sys_Error if starting up or shutting down
41 qboolean        host_shuttingdown = false;      // LordHavoc: set when quit is executed
42
43 double          host_frametime;
44 double          host_realframetime;             // LordHavoc: the real frametime, before slowmo and clamping are applied (used for console scrolling)
45 double          realtime;                               // without any filtering or bounding
46 double          oldrealtime;                    // last frame run
47 int                     host_framecount;
48
49 int                     forcedeveloper;                 // used for -developer commandline parameter, hacky hacky
50
51 client_t        *host_client;                   // current client
52
53 jmp_buf         host_abortserver;
54
55 cvar_t  host_framerate = {0, "host_framerate","0"};     // set for slow motion
56 cvar_t  host_speeds = {0, "host_speeds","0"};                   // set for running times
57 cvar_t  slowmo = {0, "slowmo", "1.0"};                                  // LordHavoc: framerate independent slowmo
58 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)
59 cvar_t  host_maxfps = {CVAR_SAVE, "host_maxfps", "1000"};               // LordHavoc: framerate upper cap
60
61 cvar_t  sys_ticrate = {CVAR_SAVE, "sys_ticrate","0.05"};
62 cvar_t  serverprofile = {0, "serverprofile","0"};
63
64 cvar_t  fraglimit = {CVAR_NOTIFY, "fraglimit","0"};
65 cvar_t  timelimit = {CVAR_NOTIFY, "timelimit","0"};
66 cvar_t  teamplay = {CVAR_NOTIFY, "teamplay","0"};
67
68 cvar_t  samelevel = {0, "samelevel","0"};
69 cvar_t  noexit = {CVAR_NOTIFY, "noexit","0"};
70
71 cvar_t  developer = {0, "developer","0"};
72
73 cvar_t  skill = {0, "skill","1"};                                               // 0 - 3
74 cvar_t  deathmatch = {0, "deathmatch","0"};                     // 0, 1, or 2
75 cvar_t  coop = {0, "coop","0"};                 // 0 or 1
76
77 cvar_t  pausable = {0, "pausable","1"};
78
79 cvar_t  temp1 = {0, "temp1","0"};
80
81 cvar_t  timestamps = {CVAR_SAVE, "timestamps", "0"};
82 cvar_t  timeformat = {CVAR_SAVE, "timeformat", "[%b %e %X] "};
83
84 /*
85 ================
86 Host_EndGame
87 ================
88 */
89 void Host_EndGame (char *message, ...)
90 {
91         va_list         argptr;
92         char            string[1024];
93
94         va_start (argptr,message);
95         vsprintf (string,message,argptr);
96         va_end (argptr);
97         Con_DPrintf ("Host_EndGame: %s\n",string);
98
99         if (sv.active)
100                 Host_ShutdownServer (false);
101
102         if (cls.state == ca_dedicated)
103                 Sys_Error ("Host_EndGame: %s\n",string);        // dedicated servers exit
104
105         if (cls.demonum != -1)
106                 CL_NextDemo ();
107         else
108                 CL_Disconnect ();
109
110         longjmp (host_abortserver, 1);
111 }
112
113 /*
114 ================
115 Host_Error
116
117 This shuts down both the client and server
118 ================
119 */
120 char hosterrorstring[4096];
121 void Host_Error (char *error, ...)
122 {
123         va_list         argptr;
124         static  qboolean inerror = false;
125
126         // LordHavoc: if first frame has not been shown, or currently shutting
127         // down, do Sys_Error instead
128         if (!host_loopactive || host_shuttingdown)
129         {
130                 char string[4096];
131                 va_start (argptr,error);
132                 vsprintf (string,error,argptr);
133                 va_end (argptr);
134                 Sys_Error ("%s", string);
135         }
136
137         if (inerror)
138         {
139                 char string[4096];
140                 va_start (argptr,error);
141                 vsprintf (string,error,argptr);
142                 va_end (argptr);
143                 Sys_Error ("Host_Error: recursively entered (original error was: %s    new error is: %s)", hosterrorstring, string);
144         }
145         inerror = true;
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 extern cvar_t cl_avidemo;
528 qboolean Host_FilterTime (double time)
529 {
530         double timecap;
531         realtime += time;
532
533         if (slowmo.value < 0.0f)
534                 Cvar_SetValue("slowmo", 0.0f);
535         if (host_minfps.value < 10.0f)
536                 Cvar_SetValue("host_minfps", 10.0f);
537         if (host_maxfps.value < host_minfps.value)
538                 Cvar_SetValue("host_maxfps", host_minfps.value);
539         if (cl_avidemo.value < 0.1f && cl_avidemo.value != 0.0f)
540                 Cvar_SetValue("cl_avidemo", 0.0f);
541
542         // check if framerate is too high
543         if (cl_avidemo.value >= 0.1f)
544         {
545                 timecap = 1.0 / (double)cl_avidemo.value;
546                 if ((realtime - oldrealtime) < timecap)
547                         return false;
548         }
549         else if (!cls.timedemo)
550         {
551                 // default to sys_ticrate (server framerate - presumably low) unless we're the active window and either connected to a server or playing a video
552                 timecap = sys_ticrate.value;
553                 if (vid_activewindow && (cls.state == ca_connected || cl_videoplaying))
554                         timecap = 1.0 / host_maxfps.value;
555
556                 if ((realtime - oldrealtime) < timecap)
557                         return false;
558         }
559
560         // LordHavoc: copy into host_realframetime as well
561         host_realframetime = host_frametime = realtime - oldrealtime;
562         oldrealtime = realtime;
563
564         if (cls.timedemo)
565         {
566                 // disable time effects
567                 cl.frametime = host_frametime;
568                 return true;
569         }
570
571         if (host_framerate.value > 0)
572                 host_frametime = host_framerate.value;
573         else if (cl_avidemo.value >= 0.1f)
574                 host_frametime = (1.0 / cl_avidemo.value);
575         else
576         {
577                 // don't allow really short frames
578                 if (host_frametime > (1.0 / host_minfps.value))
579                         host_frametime = (1.0 / host_minfps.value);
580         }
581
582         cl.frametime = host_frametime = bound(0, host_frametime * slowmo.value, 0.1f); // LordHavoc: the QC code relies on no less than 10fps
583
584         return true;
585 }
586
587
588 /*
589 ===================
590 Host_GetConsoleCommands
591
592 Add them exactly as if they had been typed at the console
593 ===================
594 */
595 void Host_GetConsoleCommands (void)
596 {
597         char    *cmd;
598
599         while (1)
600         {
601                 cmd = Sys_ConsoleInput ();
602                 if (!cmd)
603                         break;
604                 Cbuf_AddText (cmd);
605         }
606 }
607
608
609 /*
610 ==================
611 Host_ServerFrame
612
613 ==================
614 */
615 void Host_ServerFrame (void)
616 {
617         static double frametimetotal = 0, lastservertime = 0;
618         frametimetotal += host_frametime;
619         // LordHavoc: cap server at sys_ticrate in listen games
620         if (cls.state != ca_dedicated && svs.maxclients > 1 && ((realtime - lastservertime) < sys_ticrate.value))
621                 return;
622 // run the world state
623         if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
624                 sv.frametime = pr_global_struct->frametime = frametimetotal;
625         else
626                 sv.frametime = 0;
627         frametimetotal = 0;
628         lastservertime = realtime;
629
630 // set the time and clear the general datagram
631         SV_ClearDatagram ();
632
633 // check for new clients
634         SV_CheckForNewClients ();
635
636 // read client messages
637         SV_RunClients ();
638
639 // move things around and think
640 // always pause in single player if in console or menus
641         if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
642                 SV_Physics ();
643
644 // send all messages to the clients
645         SV_SendClientMessages ();
646 }
647
648
649 /*
650 ==================
651 Host_Frame
652
653 Runs all active servers
654 ==================
655 */
656 void _Host_Frame (float time)
657 {
658         static double           time1 = 0;
659         static double           time2 = 0;
660         static double           time3 = 0;
661         int                     pass1, pass2, pass3;
662
663         if (setjmp (host_abortserver) )
664                 return;                 // something bad happened, or the server disconnected
665
666 // keep the random time dependent
667         rand ();
668
669 // decide the simulation time
670         if (!Host_FilterTime (time))
671         {
672                 // if time was rejected, don't totally hog the CPU
673                 Sys_Sleep();
674                 return;
675         }
676
677 // get new key events
678         Sys_SendKeyEvents ();
679
680 // allow mice or other external controllers to add commands
681         IN_Commands ();
682
683 // process console commands
684         Cbuf_Execute ();
685
686         NET_Poll();
687
688 // if running the server locally, make intentions now
689         if (sv.active)
690                 CL_SendCmd ();
691
692 //-------------------
693 //
694 // server operations
695 //
696 //-------------------
697
698 // check for commands typed to the host
699         Host_GetConsoleCommands ();
700
701         if (sv.active)
702                 Host_ServerFrame ();
703
704 //-------------------
705 //
706 // client operations
707 //
708 //-------------------
709
710 // if running the server remotely, send intentions now after
711 // the incoming messages have been read
712         if (!sv.active)
713                 CL_SendCmd ();
714
715 // fetch results from server
716         if (cls.state == ca_connected)
717                 CL_ReadFromServer ();
718
719         ui_update();
720
721         CL_VideoFrame();
722
723 // update video
724         if (host_speeds.integer)
725                 time1 = Sys_DoubleTime ();
726
727         CL_UpdateScreen ();
728
729         if (host_speeds.integer)
730                 time2 = Sys_DoubleTime ();
731
732 // update audio
733         if (cls.signon == SIGNONS)
734         {
735                 // LordHavoc: this used to use renderer variables (eww)
736                 vec3_t forward, right, up;
737                 AngleVectors(cl.viewangles, forward, right, up);
738                 S_Update (cl_entities[cl.viewentity].render.origin, forward, right, up);
739         }
740         else
741                 S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
742
743         CDAudio_Update();
744
745         if (host_speeds.integer)
746         {
747                 pass1 = (time1 - time3)*1000000;
748                 time3 = Sys_DoubleTime ();
749                 pass2 = (time2 - time1)*1000000;
750                 pass3 = (time3 - time2)*1000000;
751                 Con_Printf ("%6ius total %6ius server %6ius gfx %6ius snd\n",
752                                         pass1+pass2+pass3, pass1, pass2, pass3);
753         }
754
755         host_framecount++;
756         host_loopactive = true;
757 }
758
759 void Host_Frame (float time)
760 {
761         double  time1, time2;
762         static double   timetotal;
763         static int              timecount;
764         int             i, c, m;
765
766         if (!serverprofile.integer)
767         {
768                 _Host_Frame (time);
769                 return;
770         }
771
772         time1 = Sys_DoubleTime ();
773         _Host_Frame (time);
774         time2 = Sys_DoubleTime ();      
775         
776         timetotal += time2 - time1;
777         timecount++;
778         
779         if (timecount < 1000)
780                 return;
781
782         m = timetotal*1000/timecount;
783         timecount = 0;
784         timetotal = 0;
785         c = 0;
786         for (i=0 ; i<svs.maxclients ; i++)
787         {
788                 if (svs.clients[i].active)
789                         c++;
790         }
791
792         Con_Printf ("serverprofile: %2i clients %2i msec\n",  c,  m);
793 }
794
795 //============================================================================
796
797 void Render_Init(void);
798 void QuakeIO_Init(void);
799
800 /*
801 ====================
802 Host_Init
803 ====================
804 */
805 void Host_Init (void)
806 {
807         // LordHavoc: quake never seeded the random number generator before... heh
808         srand(time(NULL));
809
810         com_argc = host_parms.argc;
811         com_argv = host_parms.argv;
812         // FIXME: this is evil, but possibly temporary
813         if (COM_CheckParm("-developer"))
814         {
815                 forcedeveloper = true;
816                 developer.integer = 1;
817                 developer.value = 1;
818         }
819
820         Memory_Init ();
821         Cmd_Init ();
822         Memory_Init_Commands();
823         R_Modules_Init();
824         Cbuf_Init ();
825         QuakeIO_Init ();
826         V_Init ();
827         COM_Init ();
828         Host_InitLocal ();
829         W_LoadWadFile ("gfx.wad");
830         Key_Init ();
831         Con_Init ();
832         Chase_Init ();
833         M_Init ();
834         PR_Init ();
835         Mod_Init ();
836         NET_Init ();
837         SV_Init ();
838
839         Con_Printf ("Builddate: %s\n", buildstring);
840
841         if (cls.state != ca_dedicated)
842         {
843                 VID_InitCvars();
844
845                 Gamma_Init();
846
847                 Palette_Init();
848
849 #ifndef _WIN32 // on non win32, mouse comes before video for security reasons
850                 IN_Init ();
851 #endif
852                 VID_Init ();
853
854                 Render_Init();
855                 S_Init ();
856                 CDAudio_Init ();
857                 Sbar_Init ();
858                 CL_Init ();
859 #ifdef _WIN32 // on non win32, mouse comes before video for security reasons
860                 IN_Init ();
861 #endif
862         }
863
864         Cbuf_InsertText ("exec quake.rc\n");
865
866         host_initialized = true;
867         
868         Sys_Printf ("========Quake Initialized=========\n");    
869 }
870
871
872 /*
873 ===============
874 Host_Shutdown
875
876 FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
877 to run quit through here before the final handoff to the sys code.
878 ===============
879 */
880 void Host_Shutdown(void)
881 {
882         static qboolean isdown = false;
883         
884         if (isdown)
885         {
886                 printf ("recursive shutdown\n");
887                 return;
888         }
889         isdown = 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_Modules_Shutdown();
901                 VID_Shutdown();
902         }
903 }
904