added newmap function to render modules (so explosions and other things are reset...
[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        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 (float time)
524 {
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         if ((!cls.timedemo) && ((realtime - oldrealtime) < (1.0 / host_maxfps.value)))
535                 return false;           // framerate is too high
536
537         host_realframetime = host_frametime = realtime - oldrealtime; // LordHavoc: copy into host_realframetime as well
538         oldrealtime = realtime;
539
540         if (cls.timedemo)
541                 return true; // disable time effects
542
543         if (host_framerate.value > 0)
544                 host_frametime = host_framerate.value;
545         else
546         {
547                 // don't allow really short frames
548                 if (host_frametime > (1.0 / host_minfps.value))
549                         host_frametime = (1.0 / host_minfps.value);
550         }
551
552         host_frametime *= slowmo.value;
553         cl.frametime = host_frametime;
554         
555         return true;
556 }
557
558
559 /*
560 ===================
561 Host_GetConsoleCommands
562
563 Add them exactly as if they had been typed at the console
564 ===================
565 */
566 void Host_GetConsoleCommands (void)
567 {
568         char    *cmd;
569
570         while (1)
571         {
572                 cmd = Sys_ConsoleInput ();
573                 if (!cmd)
574                         break;
575                 Cbuf_AddText (cmd);
576         }
577 }
578
579
580 /*
581 ==================
582 Host_ServerFrame
583
584 ==================
585 */
586 void Host_ServerFrame (void)
587 {
588         static double frametimetotal = 0, lastservertime = 0;
589         frametimetotal += host_frametime;
590         // LordHavoc: cap server at sys_ticrate in listen games
591         if (!isDedicated && svs.maxclients > 1 && ((realtime - lastservertime) < sys_ticrate.value))
592                 return;
593 // run the world state
594         sv.frametime = pr_global_struct->frametime = frametimetotal;
595         frametimetotal = 0;
596         lastservertime = realtime;
597 //      pr_global_struct->frametime = host_frametime;
598
599 // set the time and clear the general datagram
600         SV_ClearDatagram ();
601         
602 // check for new clients
603         SV_CheckForNewClients ();
604
605 // read client messages
606         SV_RunClients ();
607         
608 // move things around and think
609 // always pause in single player if in console or menus
610         if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
611                 SV_Physics ();
612
613 // send all messages to the clients
614         SV_SendClientMessages ();
615 }
616
617
618 /*
619 ==================
620 Host_Frame
621
622 Runs all active servers
623 ==================
624 */
625 void _Host_Frame (float time)
626 {
627         static double           time1 = 0;
628         static double           time2 = 0;
629         static double           time3 = 0;
630         int                     pass1, pass2, pass3;
631
632         if (setjmp (host_abortserver) )
633                 return;                 // something bad happened, or the server disconnected
634         hostloopactive = 1;
635
636 // keep the random time dependent
637         rand ();
638         
639 // decide the simulation time
640         if (!Host_FilterTime (time))
641                 return;                 // don't run too fast, or packets will flood out
642                 
643 // get new key events
644         Sys_SendKeyEvents ();
645
646 // allow mice or other external controllers to add commands
647         IN_Commands ();
648
649 // process console commands
650         Cbuf_Execute ();
651
652         NET_Poll();
653
654 // if running the server locally, make intentions now
655         if (sv.active)
656                 CL_SendCmd ();
657         
658 //-------------------
659 //
660 // server operations
661 //
662 //-------------------
663
664 // check for commands typed to the host
665         Host_GetConsoleCommands ();
666         
667         if (sv.active)
668                 Host_ServerFrame ();
669
670 //-------------------
671 //
672 // client operations
673 //
674 //-------------------
675
676 // if running the server remotely, send intentions now after
677 // the incoming messages have been read
678         if (!sv.active)
679                 CL_SendCmd ();
680
681         host_time += host_frametime;
682
683 // fetch results from server
684         if (cls.state == ca_connected)
685                 CL_ReadFromServer ();
686
687 // update video
688         if (host_speeds.value)
689                 time1 = Sys_FloatTime ();
690                 
691         SCR_UpdateScreen ();
692
693         if (host_speeds.value)
694                 time2 = Sys_FloatTime ();
695                 
696 // update audio
697         if (cls.signon == SIGNONS)
698         {
699                 S_Update (r_origin, vpn, vright, vup);
700                 CL_DecayLights ();
701         }
702         else
703                 S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
704         
705         CDAudio_Update();
706
707         if (host_speeds.value)
708         {
709                 pass1 = (time1 - time3)*1000000;
710                 time3 = Sys_FloatTime ();
711                 pass2 = (time2 - time1)*1000000;
712                 pass3 = (time3 - time2)*1000000;
713                 Con_Printf ("%6ius total %6ius server %6ius gfx %6ius snd\n",
714                                         pass1+pass2+pass3, pass1, pass2, pass3);
715         }
716         
717         host_framecount++;
718 }
719
720 void Host_Frame (float time)
721 {
722         double  time1, time2;
723         static double   timetotal;
724         static int              timecount;
725         int             i, c, m;
726
727         if (!serverprofile.value)
728         {
729                 _Host_Frame (time);
730                 return;
731         }
732         
733         time1 = Sys_FloatTime ();
734         _Host_Frame (time);
735         time2 = Sys_FloatTime ();       
736         
737         timetotal += time2 - time1;
738         timecount++;
739         
740         if (timecount < 1000)
741                 return;
742
743         m = timetotal*1000/timecount;
744         timecount = 0;
745         timetotal = 0;
746         c = 0;
747         for (i=0 ; i<svs.maxclients ; i++)
748         {
749                 if (svs.clients[i].active)
750                         c++;
751         }
752
753         Con_Printf ("serverprofile: %2i clients %2i msec\n",  c,  m);
754 }
755
756 //============================================================================
757
758
759 extern int vcrFile;
760 #define VCR_SIGNATURE   0x56435231
761 // "VCR1"
762
763 void Host_InitVCR ()
764 {
765         int             i, len, n;
766         char    *p;
767         
768         if (COM_CheckParm("-playback"))
769         {
770                 if (com_argc != 2)
771                         Sys_Error("No other parameters allowed with -playback\n");
772
773                 Sys_FileOpenRead("quake.vcr", &vcrFile);
774                 if (vcrFile == -1)
775                         Sys_Error("playback file not found\n");
776
777                 Sys_FileRead (vcrFile, &i, sizeof(int));
778                 if (i != VCR_SIGNATURE)
779                         Sys_Error("Invalid signature in vcr file\n");
780
781                 Sys_FileRead (vcrFile, &com_argc, sizeof(int));
782                 com_argv = qmalloc(com_argc * sizeof(char *));
783                 com_argv[0] = host_parms.argv[0];
784                 for (i = 0; i < com_argc; i++)
785                 {
786                         Sys_FileRead (vcrFile, &len, sizeof(int));
787                         p = qmalloc(len);
788                         Sys_FileRead (vcrFile, p, len);
789                         com_argv[i+1] = p;
790                 }
791                 com_argc++; /* add one for arg[0] */
792                 host_parms.argc = com_argc;
793                 host_parms.argv = com_argv;
794         }
795
796         if ( (n = COM_CheckParm("-record")) != 0)
797         {
798                 vcrFile = Sys_FileOpenWrite("quake.vcr");
799
800                 i = VCR_SIGNATURE;
801                 Sys_FileWrite(vcrFile, &i, sizeof(int));
802                 i = com_argc - 1;
803                 Sys_FileWrite(vcrFile, &i, sizeof(int));
804                 for (i = 1; i < com_argc; i++)
805                 {
806                         if (i == n)
807                         {
808                                 len = 10;
809                                 Sys_FileWrite(vcrFile, &len, sizeof(int));
810                                 Sys_FileWrite(vcrFile, "-playback", len);
811                                 continue;
812                         }
813                         len = strlen(com_argv[i]) + 1;
814                         Sys_FileWrite(vcrFile, &len, sizeof(int));
815                         Sys_FileWrite(vcrFile, com_argv[i], len);
816                 }
817         }
818         
819 }
820
821 void Render_Init();
822
823 /*
824 ====================
825 Host_Init
826 ====================
827 */
828 void Host_Init ()
829 {
830         int i;
831         /*
832         if (standard_quake)
833                 minimum_memory = MINIMUM_MEMORY;
834         else
835                 minimum_memory = MINIMUM_MEMORY_LEVELPAK;
836
837         if (COM_CheckParm ("-minmemory"))
838                 host_parms.memsize = minimum_memory;
839
840         if (host_parms.memsize < minimum_memory)
841                 Sys_Error ("Only %4.1f megs of memory available, can't execute game", host_parms.memsize / (float)0x100000);
842         */
843
844         host_parms.memsize = DEFAULTMEM * 1024 * 1024;
845
846         i = COM_CheckParm("-mem");
847         if (i)
848                 host_parms.memsize = (int) (atof(com_argv[i+1]) * 1024 * 1024);
849
850         i = COM_CheckParm("-winmem");
851         if (i)
852                 host_parms.memsize = (int) (atof(com_argv[i+1]) * 1024 * 1024);
853
854         i = COM_CheckParm("-heapsize");
855         if (i)
856                 host_parms.memsize = (int) (atof(com_argv[i+1]) * 1024);
857
858         host_parms.membase = qmalloc(host_parms.memsize);
859         if (!host_parms.membase)
860                 Sys_Error("Not enough memory free, close some programs and try again, or free disk space\n");
861
862         com_argc = host_parms.argc;
863         com_argv = host_parms.argv;
864
865         Memory_Init (host_parms.membase, host_parms.memsize);
866         Cbuf_Init ();
867         Cmd_Init ();    
868         V_Init ();
869         Chase_Init ();
870         Host_InitVCR ();
871         COM_Init (host_parms.basedir);
872         Host_InitLocal ();
873         W_LoadWadFile ("gfx.wad");
874         Key_Init ();
875         Con_Init ();    
876         M_Init ();      
877         PR_Init ();
878         Mod_Init ();
879         NET_Init ();
880         SV_Init ();
881
882         Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
883         Con_Printf ("%4.1f megabyte heap\n",host_parms.memsize/(1024*1024.0));
884         
885         R_InitTextures ();              // needed even for dedicated servers
886  
887         if (cls.state != ca_dedicated)
888         {
889                 Palette_Init("gfx/palette.lmp");
890
891 #ifndef _WIN32 // on non win32, mouse comes before video for security reasons
892                 IN_Init ();
893 #endif
894                 VID_Init ();
895
896                 Render_Init();
897                 S_Init ();
898                 CDAudio_Init ();
899                 Sbar_Init ();
900                 CL_Init ();
901 #ifdef _WIN32 // on non win32, mouse comes before video for security reasons
902                 IN_Init ();
903 #endif
904         }
905
906         Cbuf_InsertText ("exec quake.rc\n");
907
908         Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
909         host_hunklevel = Hunk_LowMark ();
910
911         host_initialized = true;
912         
913         Sys_Printf ("========Quake Initialized=========\n");    
914 }
915
916
917 /*
918 ===============
919 Host_Shutdown
920
921 FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
922 to run quit through here before the final handoff to the sys code.
923 ===============
924 */
925 void Host_Shutdown(void)
926 {
927         static qboolean isdown = false;
928         
929         if (isdown)
930         {
931                 printf ("recursive shutdown\n");
932                 return;
933         }
934         isdown = true;
935
936 // keep Con_Printf from trying to update the screen
937         scr_disabled_for_loading = true;
938
939         Host_WriteConfiguration (); 
940
941         CDAudio_Shutdown ();
942         NET_Shutdown ();
943         S_Shutdown();
944         IN_Shutdown ();
945
946         if (cls.state != ca_dedicated)
947         {
948                 R_Modules_Shutdown();
949                 VID_Shutdown();
950         }
951 }
952