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