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