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