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