Added in_mouse_x and in_mouse_y, which contain the rel. coords of the mouse.
[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 <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_InitLocal (void)
263 {
264         Host_InitCommands ();
265
266         Cvar_RegisterVariable (&host_framerate);
267         Cvar_RegisterVariable (&host_speeds);
268         Cvar_RegisterVariable (&slowmo);
269         Cvar_RegisterVariable (&host_minfps);
270         Cvar_RegisterVariable (&host_maxfps);
271
272         Cvar_RegisterVariable (&sv_echobprint);
273
274         Cvar_RegisterVariable (&sys_ticrate);
275         Cvar_RegisterVariable (&serverprofile);
276
277         Cvar_RegisterVariable (&fraglimit);
278         Cvar_RegisterVariable (&timelimit);
279         Cvar_RegisterVariable (&teamplay);
280         Cvar_RegisterVariable (&samelevel);
281         Cvar_RegisterVariable (&noexit);
282         Cvar_RegisterVariable (&skill);
283         Cvar_RegisterVariable (&developer);
284         if (forcedeveloper) // make it real now that the cvar is registered
285                 Cvar_SetValue("developer", 1);
286         Cvar_RegisterVariable (&deathmatch);
287         Cvar_RegisterVariable (&coop);
288
289         Cvar_RegisterVariable (&pausable);
290
291         Cvar_RegisterVariable (&temp1);
292
293         Cvar_RegisterVariable (&timestamps);
294         Cvar_RegisterVariable (&timeformat);
295
296         Host_ServerOptions ();
297 }
298
299
300 /*
301 ===============
302 Host_WriteConfiguration
303
304 Writes key bindings and archived cvars to config.cfg
305 ===============
306 */
307 void Host_WriteConfiguration (void)
308 {
309         qfile_t *f;
310
311 // dedicated servers initialize the host but don't parse and set the
312 // config.cfg cvars
313         if (host_initialized && cls.state != ca_dedicated)
314         {
315                 f = FS_Open ("config.cfg", "w", false);
316                 if (!f)
317                 {
318                         Con_Printf ("Couldn't write config.cfg.\n");
319                         return;
320                 }
321
322                 Key_WriteBindings (f);
323                 Cvar_WriteVariables (f);
324
325                 FS_Close (f);
326         }
327 }
328
329
330 /*
331 =================
332 SV_ClientPrintf
333
334 Sends text across to be displayed
335 FIXME: make this just a stuffed echo?
336 =================
337 */
338 void SV_ClientPrintf(const char *fmt, ...)
339 {
340         va_list argptr;
341         char string[1024];
342
343         va_start (argptr,fmt);
344         vsprintf (string, fmt,argptr);
345         va_end (argptr);
346
347         MSG_WriteByte (&host_client->message, svc_print);
348         MSG_WriteString (&host_client->message, string);
349 }
350
351 /*
352 =================
353 SV_BroadcastPrintf
354
355 Sends text to all active clients
356 =================
357 */
358 void SV_BroadcastPrintf(const char *fmt, ...)
359 {
360         va_list argptr;
361         char string[4096];
362         int i;
363         client_t *client;
364
365         va_start(argptr,fmt);
366         vsnprintf(string, sizeof(string), fmt,argptr);
367         va_end(argptr);
368
369         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
370         {
371                 if (client->spawned)
372                 {
373                         MSG_WriteByte(&client->message, svc_print);
374                         MSG_WriteString(&client->message, string);
375                 }
376         }
377
378         if (sv_echobprint.integer && cls.state == ca_dedicated)
379                 Sys_Printf("%s", string);
380 }
381
382 /*
383 =================
384 Host_ClientCommands
385
386 Send text over to the client to be executed
387 =================
388 */
389 void Host_ClientCommands(const char *fmt, ...)
390 {
391         va_list argptr;
392         char string[1024];
393
394         va_start(argptr,fmt);
395         vsprintf(string, fmt,argptr);
396         va_end(argptr);
397
398         MSG_WriteByte(&host_client->message, svc_stufftext);
399         MSG_WriteString(&host_client->message, string);
400 }
401
402 /*
403 =====================
404 SV_DropClient
405
406 Called when the player is getting totally kicked off the host
407 if (crash = true), don't bother sending signofs
408 =====================
409 */
410 void SV_DropClient(qboolean crash)
411 {
412         int saveSelf;
413         int i;
414         client_t *client;
415
416         Con_Printf("Client \"%s\" dropped\n", host_client->name);
417
418         // send any final messages (don't check for errors)
419         if (host_client->netconnection)
420         {
421                 // free the client (the body stays around)
422                 if (!crash)
423                 {
424                         // LordHavoc: no opportunity for resending, so use unreliable
425                         MSG_WriteByte(&host_client->message, svc_disconnect);
426                         NetConn_SendUnreliableMessage(host_client->netconnection, &host_client->message);
427                 }
428
429                 // break the net connection
430                 NetConn_Close(host_client->netconnection);
431                 host_client->netconnection = NULL;
432
433                 // LordHavoc: don't call QC if server is dead (avoids recursive
434                 // Host_Error in some mods when they run out of edicts)
435                 if (sv.active && host_client->edict && host_client->spawned)
436                 {
437                         // call the prog function for removing a client
438                         // this will set the body to a dead frame, among other things
439                         saveSelf = pr_global_struct->self;
440                         pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
441                         PR_ExecuteProgram(pr_global_struct->ClientDisconnect, "QC function ClientDisconnect is missing");
442                         pr_global_struct->self = saveSelf;
443                 }
444         }
445
446         // send notification to all clients
447         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
448         {
449                 if (!client->active)
450                         continue;
451                 MSG_WriteByte(&client->message, svc_updatename);
452                 MSG_WriteByte(&client->message, host_client->number);
453                 MSG_WriteString(&client->message, "");
454                 MSG_WriteByte(&client->message, svc_updatefrags);
455                 MSG_WriteByte(&client->message, host_client->number);
456                 MSG_WriteShort(&client->message, 0);
457                 MSG_WriteByte(&client->message, svc_updatecolors);
458                 MSG_WriteByte(&client->message, host_client->number);
459                 MSG_WriteByte(&client->message, 0);
460         }
461
462         NetConn_Heartbeat(1);
463
464         // free the client now
465         if (host_client->entitydatabase4)
466                 EntityFrame4_FreeDatabase(host_client->entitydatabase4);
467         // clear the client struct (this sets active to false)
468         memset(host_client, 0, sizeof(*host_client));
469 }
470
471 /*
472 ==================
473 Host_ShutdownServer
474
475 This only happens at the end of a game, not between levels
476 ==================
477 */
478 void Host_ShutdownServer(qboolean crash)
479 {
480         int i, count;
481         sizebuf_t buf;
482         char message[4];
483
484         if (!sv.active)
485                 return;
486
487         // print out where the crash happened, if it was caused by QC
488         PR_Crash();
489
490         sv.active = false;
491
492 // stop all client sounds immediately
493         CL_Disconnect();
494
495         NetConn_Heartbeat(2);
496         NetConn_Heartbeat(2);
497
498 // make sure all the clients know we're disconnecting
499         buf.data = message;
500         buf.maxsize = 4;
501         buf.cursize = 0;
502         MSG_WriteByte(&buf, svc_disconnect);
503         count = NetConn_SendToAll(&buf, 5);
504         if (count)
505                 Con_Printf("Host_ShutdownServer: NetConn_SendToAll failed for %u clients\n", count);
506
507         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
508                 if (host_client->active)
509                         SV_DropClient(crash); // server shutdown
510
511         NetConn_CloseServerPorts();
512
513 //
514 // clear structures
515 //
516         memset(&sv, 0, sizeof(sv));
517         memset(svs.clients, 0, svs.maxclients*sizeof(client_t));
518 }
519
520
521 /*
522 ================
523 Host_ClearMemory
524
525 This clears all the memory used by both the client and server, but does
526 not reinitialize anything.
527 ================
528 */
529 void Host_ClearMemory (void)
530 {
531         Con_DPrintf ("Clearing memory\n");
532         Mod_ClearAll ();
533
534         cls.signon = 0;
535         memset (&sv, 0, sizeof(sv));
536         memset (&cl, 0, sizeof(cl));
537 }
538
539
540 //============================================================================
541
542 /*
543 ===================
544 Host_FilterTime
545
546 Returns false if the time is too short to run a frame
547 ===================
548 */
549 extern cvar_t cl_avidemo;
550 qboolean Host_FilterTime (double time)
551 {
552         double timecap, timeleft;
553         realtime += time;
554
555         if (slowmo.value < 0.0f)
556                 Cvar_SetValue("slowmo", 0.0f);
557         if (host_minfps.value < 10.0f)
558                 Cvar_SetValue("host_minfps", 10.0f);
559         if (host_maxfps.value < host_minfps.value)
560                 Cvar_SetValue("host_maxfps", host_minfps.value);
561         if (cl_avidemo.value < 0.1f && cl_avidemo.value != 0.0f)
562                 Cvar_SetValue("cl_avidemo", 0.0f);
563
564         // check if framerate is too high
565         if (!cls.timedemo)
566         {
567                 // default to sys_ticrate (server framerate - presumably low) unless we
568                 // have a good reason to run faster
569                 timecap = sys_ticrate.value;
570                 if (cl_avidemo.value >= 0.1f)
571                         timecap = 1.0 / (double)cl_avidemo.value;
572                 else if (vid_activewindow && !scr_con_current)
573                         timecap = 1.0 / host_maxfps.value;
574
575                 timeleft = oldrealtime + timecap - realtime;
576                 if (timeleft > 0)
577                 {
578                         // don't totally hog the CPU
579                         if (timeleft >= 0.02)
580                                 Sys_Sleep();
581                         return false;
582                 }
583         }
584
585         // LordHavoc: copy into host_realframetime as well
586         host_realframetime = host_frametime = realtime - oldrealtime;
587         oldrealtime = realtime;
588
589         if (cls.timedemo)
590         {
591                 // disable time effects
592                 cl.frametime = host_frametime;
593                 return true;
594         }
595
596         if (host_framerate.value > 0)
597                 host_frametime = host_framerate.value;
598         else if (cl_avidemo.value >= 0.1f)
599                 host_frametime = (1.0 / cl_avidemo.value);
600         else
601         {
602                 // don't allow really short frames
603                 if (host_frametime > (1.0 / host_minfps.value))
604                         host_frametime = (1.0 / host_minfps.value);
605         }
606
607         cl.frametime = host_frametime = bound(0, host_frametime * slowmo.value, 0.1f); // LordHavoc: the QC code relies on no less than 10fps
608
609         return true;
610 }
611
612
613 /*
614 ===================
615 Host_GetConsoleCommands
616
617 Add them exactly as if they had been typed at the console
618 ===================
619 */
620 void Host_GetConsoleCommands (void)
621 {
622         char *cmd;
623
624         while (1)
625         {
626                 cmd = Sys_ConsoleInput ();
627                 if (!cmd)
628                         break;
629                 Cbuf_AddText (cmd);
630         }
631 }
632
633
634 /*
635 ==================
636 Host_ServerFrame
637
638 ==================
639 */
640 void Host_ServerFrame (void)
641 {
642         static double frametimetotal = 0, lastservertime = 0;
643         frametimetotal += host_frametime;
644         // LordHavoc: cap server at sys_ticrate in networked games
645         if (!cl.islocalgame && ((realtime - lastservertime) < sys_ticrate.value))
646                 return;
647
648         NetConn_ServerFrame();
649
650 // run the world state
651         if (!sv.paused && (!cl.islocalgame || (key_dest == key_game && !key_consoleactive)))
652                 sv.frametime = pr_global_struct->frametime = frametimetotal;
653         else
654                 sv.frametime = 0;
655         frametimetotal = 0;
656         lastservertime = realtime;
657
658 // set the time and clear the general datagram
659         SV_ClearDatagram();
660
661 // read client messages
662         SV_RunClients();
663
664 // move things around and think
665 // always pause in single player if in console or menus
666         if (sv.frametime)
667                 SV_Physics();
668
669 // send all messages to the clients
670         SV_SendClientMessages();
671
672 // send an heartbeat if enough time has passed since the last one
673         NetConn_Heartbeat(0);
674 }
675
676
677 /*
678 ==================
679 Host_Frame
680
681 Runs all active servers
682 ==================
683 */
684 void _Host_Frame (float time)
685 {
686         static double time1 = 0;
687         static double time2 = 0;
688         static double time3 = 0;
689         int pass1, pass2, pass3;
690         usercmd_t cmd; // Used for receiving input
691         
692         if (setjmp(host_abortserver))
693                 return;                 // something bad happened, or the server disconnected
694
695         // keep the random time dependent
696         rand();
697
698         // decide the simulation time
699         if (!Host_FilterTime(time))
700                 return;
701
702         cl.islocalgame = NetConn_IsLocalGame();
703
704         // get new key events
705         Sys_SendKeyEvents();
706
707         // allow mice or other external controllers to add commands
708         IN_Commands();
709
710         // Collect input into cmd
711         IN_ProcessMove(&cmd);
712         
713         // process console commands
714         Cbuf_Execute();
715
716         // LordHavoc: map and load are delayed until video is initialized
717         Host_PerformSpawnServerAndLoadGame();
718
719         // if running the server locally, make intentions now
720         if (cls.state == ca_connected && sv.active)
721                 CL_SendCmd(&cmd);
722
723 //-------------------
724 //
725 // server operations
726 //
727 //-------------------
728
729         // check for commands typed to the host
730         Host_GetConsoleCommands();
731
732         if (sv.active)
733                 Host_ServerFrame();
734
735 //-------------------
736 //
737 // client operations
738 //
739 //-------------------
740
741         cl.oldtime = cl.time;
742         cl.time += cl.frametime;
743
744         NetConn_ClientFrame();
745
746         if (cls.state == ca_connected)
747         {
748                 // if running the server remotely, send intentions now after
749                 // the incoming messages have been read
750                 if (!sv.active)
751                         CL_SendCmd(&cmd);
752                 CL_ReadFromServer();
753         }
754
755         ui_update();
756
757         CL_VideoFrame();
758
759         // update video
760         if (host_speeds.integer)
761                 time1 = Sys_DoubleTime();
762
763         CL_UpdateScreen();
764
765         if (host_speeds.integer)
766                 time2 = Sys_DoubleTime();
767
768         // update audio
769         if (cls.signon == SIGNONS)
770         {
771                 // LordHavoc: this used to use renderer variables (eww)
772                 vec3_t forward, right, up;
773                 AngleVectors(cl.viewangles, forward, right, up);
774                 S_Update(cl_entities[cl.viewentity].render.origin, forward, right, up);
775         }
776         else
777                 S_Update(vec3_origin, vec3_origin, vec3_origin, vec3_origin);
778
779         CDAudio_Update();
780
781         if (host_speeds.integer)
782         {
783                 pass1 = (time1 - time3)*1000000;
784                 time3 = Sys_DoubleTime();
785                 pass2 = (time2 - time1)*1000000;
786                 pass3 = (time3 - time2)*1000000;
787                 Con_Printf("%6ius total %6ius server %6ius gfx %6ius snd\n",
788                                         pass1+pass2+pass3, pass1, pass2, pass3);
789         }
790
791         host_framecount++;
792         host_loopactive = true;
793
794 }
795
796 void Host_Frame (float time)
797 {
798         double time1, time2;
799         static double timetotal;
800         static int timecount;
801         int i, c, m;
802
803         if (!serverprofile.integer)
804         {
805                 _Host_Frame (time);
806                 return;
807         }
808
809         time1 = Sys_DoubleTime ();
810         _Host_Frame (time);
811         time2 = Sys_DoubleTime ();
812
813         timetotal += time2 - time1;
814         timecount++;
815
816         if (timecount < 1000)
817                 return;
818
819         m = timetotal*1000/timecount;
820         timecount = 0;
821         timetotal = 0;
822         c = 0;
823         for (i=0 ; i<svs.maxclients ; i++)
824         {
825                 if (svs.clients[i].active)
826                         c++;
827         }
828
829         Con_Printf ("serverprofile: %2i clients %2i msec\n",  c,  m);
830 }
831
832 //============================================================================
833
834 void Render_Init(void);
835 void M_Scipt_Init ();
836
837 /*
838 ====================
839 Host_Init
840 ====================
841 */
842 void Host_Init (void)
843 {
844         // LordHavoc: quake never seeded the random number generator before... heh
845         srand(time(NULL));
846
847         // FIXME: this is evil, but possibly temporary
848         if (COM_CheckParm("-developer"))
849         {
850                 forcedeveloper = true;
851                 developer.integer = 1;
852                 developer.value = 1;
853         }
854
855         Cmd_Init();
856         Memory_Init_Commands();
857         R_Modules_Init();
858         Cbuf_Init();
859         V_Init();
860         COM_Init();
861         Host_InitLocal();
862         Key_Init();
863         Con_Init();
864         Chase_Init();
865         PR_Init();
866         PRVM_Init();
867         Mod_Init();
868         NetConn_Init();
869         SV_Init();
870
871         Con_Printf ("Builddate: %s\n", buildstring);
872
873         if (cls.state != ca_dedicated)
874         {
875                 Palette_Init();
876                 VID_Shared_Init();
877                 VID_Init();
878
879                 Render_Init();
880                 S_Init();
881                 CDAudio_Init();
882                 CL_Init();
883         }
884
885         Cbuf_InsertText ("exec quake.rc\n");
886         Cbuf_Execute();
887         Cbuf_Execute();
888         Cbuf_Execute();
889         Cbuf_Execute();
890
891         host_initialized = true;
892
893         Con_DPrintf ("========Initialized=========\n");
894
895         if (cls.state != ca_dedicated)
896         {
897                 VID_Open();
898                 SCR_BeginLoadingPlaque();
899         }
900         
901         MR_Init();
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_WriteConfiguration ();
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