1 /*
2 Copyright (C) 1996-2001 Id Software, Inc.
3 Copyright (C) 2002-2009 John Fitzgibbons and others
4 Copyright (C) 2007-2008 Kristian Duske
5 Copyright (C) 2010-2014 QuakeSpasm developers
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
22 */
23 // host.c -- coordinates spawning and killing of local servers
24
25 #include "quakedef.h"
26 #include "bgmusic.h"
27 #include <setjmp.h>
28
29 #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG)
30 #include <SDL2/SDL.h>
31 #else
32 #include "SDL.h"
33 #endif
34
35 /*
36
37 A server can allways be started, even if the system started out as a client
38 to a remote system.
39
40 A client can NOT be started if the system started as a dedicated server.
41
42 Memory is cleared / released when a server or client begins, not when they end.
43
44 */
45
46 quakeparms_t *host_parms;
47
48 qboolean host_initialized; // true if into command execution
49
50 double host_frametime;
51 double realtime; // without any filtering or bounding
52 double oldrealtime; // last frame run
53
54 int host_framecount;
55
56 int host_hunklevel;
57
58 int minimum_memory;
59
60 client_t *host_client; // current client
61
62 jmp_buf host_abortserver;
63
64 byte *host_colormap;
65 float host_netinterval = 1.0/72;
66 cvar_t host_framerate = {"host_framerate","0",CVAR_NONE}; // set for slow motion
67 cvar_t host_speeds = {"host_speeds","0",CVAR_NONE}; // set for running times
68 cvar_t host_maxfps = {"host_maxfps", "200", CVAR_ARCHIVE}; //johnfitz
69 cvar_t host_timescale = {"host_timescale", "0", CVAR_NONE}; //johnfitz
70 cvar_t max_edicts = {"max_edicts", "8192", CVAR_NONE}; //johnfitz //ericw -- changed from 2048 to 8192, removed CVAR_ARCHIVE
71 cvar_t cl_nocsqc = {"cl_nocsqc", "0", CVAR_NONE}; //spike -- blocks the loading of any csqc modules
72
73 cvar_t sys_ticrate = {"sys_ticrate","0.05",CVAR_NONE}; // dedicated server
74 cvar_t serverprofile = {"serverprofile","0",CVAR_NONE};
75
76 cvar_t fraglimit = {"fraglimit","0",CVAR_NOTIFY|CVAR_SERVERINFO};
77 cvar_t timelimit = {"timelimit","0",CVAR_NOTIFY|CVAR_SERVERINFO};
78 cvar_t teamplay = {"teamplay","0",CVAR_NOTIFY|CVAR_SERVERINFO};
79 cvar_t samelevel = {"samelevel","0",CVAR_NONE};
80 cvar_t noexit = {"noexit","0",CVAR_NOTIFY|CVAR_SERVERINFO};
81 cvar_t skill = {"skill","1",CVAR_NONE}; // 0 - 3
82 cvar_t deathmatch = {"deathmatch","0",CVAR_NONE}; // 0, 1, or 2
83 cvar_t coop = {"coop","0",CVAR_NONE}; // 0 or 1
84
85 cvar_t pausable = {"pausable","1",CVAR_NONE};
86
87 cvar_t developer = {"developer","0",CVAR_NONE};
88
89 static cvar_t pr_engine = {"pr_engine", ENGINE_NAME_AND_VER, CVAR_NONE};
90 cvar_t temp1 = {"temp1","0",CVAR_NONE};
91
92 cvar_t devstats = {"devstats","0",CVAR_NONE}; //johnfitz -- track developer statistics that vary every frame
93
94 cvar_t campaign = {"campaign","0",CVAR_NONE}; // for the 2021 rerelease
95
96 devstats_t dev_stats, dev_peakstats;
97 overflowtimes_t dev_overflows; //this stores the last time overflow messages were displayed, not the last time overflows occured
98
99 /*
100 ================
101 Max_Edicts_f -- johnfitz
102 ================
103 */
Max_Edicts_f(cvar_t * var)104 static void Max_Edicts_f (cvar_t *var)
105 {
106 //TODO: clamp it here?
107 if (cls.state == ca_connected || sv.active)
108 Con_Printf ("Changes to max_edicts will not take effect until the next time a map is loaded.\n");
109 }
110
111 /*
112 ================
113 Max_Fps_f -- ericw
114 ================
115 */
Max_Fps_f(cvar_t * var)116 static void Max_Fps_f (cvar_t *var)
117 {
118 if (var->value > 72 || var->value <= 0)
119 {
120 if (!host_netinterval)
121 Con_Printf ("Using renderer/network isolation.\n");
122 host_netinterval = 1.0/72;
123 }
124 else
125 {
126 if (host_netinterval)
127 Con_Printf ("Disabling renderer/network isolation.\n");
128 host_netinterval = 0;
129
130 if (var->value > 72)
131 Con_Warning ("host_maxfps above 72 breaks physics.\n");
132 }
133 }
134
135 /*
136 ================
137 Host_EndGame
138 ================
139 */
Host_EndGame(const char * message,...)140 void Host_EndGame (const char *message, ...)
141 {
142 va_list argptr;
143 char string[1024];
144
145 va_start (argptr,message);
146 q_vsnprintf (string, sizeof(string), message, argptr);
147 va_end (argptr);
148 Con_DPrintf ("Host_EndGame: %s\n",string);
149
150 PR_SwitchQCVM(NULL);
151
152 if (sv.active)
153 Host_ShutdownServer (false);
154
155 if (cls.state == ca_dedicated)
156 Sys_Error ("Host_EndGame: %s\n",string); // dedicated servers exit
157
158 if (cls.demonum != -1)
159 CL_NextDemo ();
160 else
161 CL_Disconnect ();
162
163 longjmp (host_abortserver, 1);
164 }
165
166 /*
167 ================
168 Host_Error
169
170 This shuts down both the client and server
171 ================
172 */
Host_Error(const char * error,...)173 void Host_Error (const char *error, ...)
174 {
175 va_list argptr;
176 char string[1024];
177 static qboolean inerror = false;
178
179 if (inerror)
180 Sys_Error ("Host_Error: recursively entered");
181 inerror = true;
182
183 PR_SwitchQCVM(NULL);
184
185 SCR_EndLoadingPlaque (); // reenable screen updates
186
187 va_start (argptr,error);
188 q_vsnprintf (string, sizeof(string), error, argptr);
189 va_end (argptr);
190 Con_Printf ("Host_Error: %s\n",string);
191
192 if (sv.active)
193 Host_ShutdownServer (false);
194
195 if (cls.state == ca_dedicated)
196 Sys_Error ("Host_Error: %s\n",string); // dedicated servers exit
197
198 CL_Disconnect ();
199 cls.demonum = -1;
200 cl.intermission = 0; //johnfitz -- for errors during intermissions (changelevel with no map found, etc.)
201
202 inerror = false;
203
204 longjmp (host_abortserver, 1);
205 }
206
207 /*
208 ================
209 Host_FindMaxClients
210 ================
211 */
Host_FindMaxClients(void)212 void Host_FindMaxClients (void)
213 {
214 int i;
215
216 svs.maxclients = 1;
217
218 i = COM_CheckParm ("-dedicated");
219 if (i)
220 {
221 cls.state = ca_dedicated;
222 if (i != (com_argc - 1))
223 {
224 svs.maxclients = Q_atoi (com_argv[i+1]);
225 }
226 else
227 svs.maxclients = 8;
228 }
229 else
230 cls.state = ca_disconnected;
231
232 i = COM_CheckParm ("-listen");
233 if (i)
234 {
235 if (cls.state == ca_dedicated)
236 Sys_Error ("Only one of -dedicated or -listen can be specified");
237 if (i != (com_argc - 1))
238 svs.maxclients = Q_atoi (com_argv[i+1]);
239 else
240 svs.maxclients = 8;
241 }
242 if (svs.maxclients < 1)
243 svs.maxclients = 8;
244 else if (svs.maxclients > MAX_SCOREBOARD)
245 svs.maxclients = MAX_SCOREBOARD;
246
247 svs.maxclientslimit = svs.maxclients;
248 if (svs.maxclientslimit < 4)
249 svs.maxclientslimit = 4;
250 svs.clients = (struct client_s *) Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients");
251
252 if (svs.maxclients > 1)
253 Cvar_SetQuick (&deathmatch, "1");
254 else
255 Cvar_SetQuick (&deathmatch, "0");
256 }
257
Host_Version_f(void)258 void Host_Version_f (void)
259 {
260 Con_Printf ("Quake Version %1.2f\n", VERSION);
261 Con_Printf ("QuakeSpasm Version " QUAKESPASM_VER_STRING "\n");
262 Con_Printf ("vkQuake Version " VKQUAKE_VER_STRING "\n");
263 Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
264 }
265
266 /* cvar callback functions : */
Host_Callback_Notify(cvar_t * var)267 void Host_Callback_Notify (cvar_t *var)
268 {
269 if (sv.active)
270 SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string);
271 }
272
273 /*
274 =======================
275 Host_InitLocal
276 ======================
277 */
Host_InitLocal(void)278 void Host_InitLocal (void)
279 {
280 Cmd_AddCommand ("version", Host_Version_f);
281
282 Host_InitCommands ();
283
284 Cvar_RegisterVariable (&pr_engine);
285 Cvar_RegisterVariable (&host_framerate);
286 Cvar_RegisterVariable (&host_speeds);
287 Cvar_RegisterVariable (&host_maxfps); //johnfitz
288 Cvar_SetCallback (&host_maxfps, Max_Fps_f);
289 Cvar_RegisterVariable (&host_timescale); //johnfitz
290
291 Cvar_RegisterVariable (&cl_nocsqc); //spike
292 Cvar_RegisterVariable (&max_edicts); //johnfitz
293 Cvar_SetCallback (&max_edicts, Max_Edicts_f);
294 Cvar_RegisterVariable (&devstats); //johnfitz
295
296 Cvar_RegisterVariable (&sys_ticrate);
297 Cvar_RegisterVariable (&serverprofile);
298
299 Cvar_RegisterVariable (&fraglimit);
300 Cvar_RegisterVariable (&timelimit);
301 Cvar_RegisterVariable (&teamplay);
302 Cvar_SetCallback (&fraglimit, Host_Callback_Notify);
303 Cvar_SetCallback (&timelimit, Host_Callback_Notify);
304 Cvar_SetCallback (&teamplay, Host_Callback_Notify);
305 Cvar_RegisterVariable (&samelevel);
306 Cvar_RegisterVariable (&noexit);
307 Cvar_SetCallback (&noexit, Host_Callback_Notify);
308 Cvar_RegisterVariable (&skill);
309 Cvar_RegisterVariable (&developer);
310 Cvar_RegisterVariable (&coop);
311 Cvar_RegisterVariable (&deathmatch);
312
313 Cvar_RegisterVariable (&campaign);
314
315 Cvar_RegisterVariable (&pausable);
316
317 Cvar_RegisterVariable (&temp1);
318
319 Host_FindMaxClients ();
320 }
321
322
323 /*
324 ===============
325 Host_WriteConfiguration
326
327 Writes key bindings and archived cvars to config.cfg
328 ===============
329 */
Host_WriteConfiguration(void)330 void Host_WriteConfiguration (void)
331 {
332 FILE *f;
333
334 // dedicated servers initialize the host but don't parse and set the
335 // config.cfg cvars
336 if (host_initialized && !isDedicated && !host_parms->errstate)
337 {
338 f = fopen (va("%s/config.cfg", com_gamedir), "w");
339 if (!f)
340 {
341 Con_Printf ("Couldn't write config.cfg.\n");
342 return;
343 }
344
345 //VID_SyncCvars (); //johnfitz -- write actual current mode to config file, in case cvars were messed with
346
347 Key_WriteBindings (f);
348 Cvar_WriteVariables (f);
349
350 //johnfitz -- extra commands to preserve state
351 fprintf (f, "vid_restart\n");
352 if (in_mlook.state & 1) fprintf (f, "+mlook\n");
353 //johnfitz
354
355 fclose (f);
356 }
357 }
358
359
360 /*
361 =================
362 SV_ClientPrintf
363
364 Sends text across to be displayed
365 FIXME: make this just a stuffed echo?
366 =================
367 */
SV_ClientPrintf(const char * fmt,...)368 void SV_ClientPrintf (const char *fmt, ...)
369 {
370 va_list argptr;
371 char string[1024];
372
373 va_start (argptr,fmt);
374 q_vsnprintf (string, sizeof(string), fmt,argptr);
375 va_end (argptr);
376
377 MSG_WriteByte (&host_client->message, svc_print);
378 MSG_WriteString (&host_client->message, string);
379 }
380
381 /*
382 =================
383 SV_BroadcastPrintf
384
385 Sends text to all active clients
386 =================
387 */
SV_BroadcastPrintf(const char * fmt,...)388 void SV_BroadcastPrintf (const char *fmt, ...)
389 {
390 va_list argptr;
391 char string[1024];
392 int i;
393
394 va_start (argptr,fmt);
395 q_vsnprintf (string, sizeof(string), fmt, argptr);
396 va_end (argptr);
397
398 for (i = 0; i < svs.maxclients; i++)
399 {
400 if (svs.clients[i].active && svs.clients[i].spawned)
401 {
402 MSG_WriteByte (&svs.clients[i].message, svc_print);
403 MSG_WriteString (&svs.clients[i].message, string);
404 }
405 }
406 }
407
408 /*
409 =================
410 Host_ClientCommands
411
412 Send text over to the client to be executed
413 =================
414 */
Host_ClientCommands(const char * fmt,...)415 void Host_ClientCommands (const char *fmt, ...)
416 {
417 va_list argptr;
418 char string[1024];
419
420 va_start (argptr,fmt);
421 q_vsnprintf (string, sizeof(string), fmt, argptr);
422 va_end (argptr);
423
424 MSG_WriteByte (&host_client->message, svc_stufftext);
425 MSG_WriteString (&host_client->message, string);
426 }
427
428 /*
429 =====================
430 SV_DropClient
431
432 Called when the player is getting totally kicked off the host
433 if (crash = true), don't bother sending signofs
434 =====================
435 */
SV_DropClient(qboolean crash)436 void SV_DropClient (qboolean crash)
437 {
438 int saveSelf;
439 int i;
440 client_t *client;
441
442 if (!crash)
443 {
444 // send any final messages (don't check for errors)
445 if (NET_CanSendMessage (host_client->netconnection))
446 {
447 MSG_WriteByte (&host_client->message, svc_disconnect);
448 NET_SendMessage (host_client->netconnection, &host_client->message);
449 }
450
451 if (host_client->edict && host_client->spawned)
452 {
453 // call the prog function for removing a client
454 // this will set the body to a dead frame, among other things
455 qcvm_t *oldvm = qcvm;
456 PR_SwitchQCVM(NULL);
457 PR_SwitchQCVM(&sv.qcvm);
458 saveSelf = pr_global_struct->self;
459 pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
460 PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
461 pr_global_struct->self = saveSelf;
462 PR_SwitchQCVM(NULL);
463 PR_SwitchQCVM(oldvm);
464 }
465
466 Sys_Printf ("Client %s removed\n",host_client->name);
467 }
468
469 // break the net connection
470 NET_Close (host_client->netconnection);
471 host_client->netconnection = NULL;
472
473 SVFTE_DestroyFrames(host_client); //release any delta state
474
475 // free the client (the body stays around)
476 host_client->active = false;
477 host_client->name[0] = 0;
478 host_client->old_frags = -999999;
479 net_activeconnections--;
480
481 // send notification to all clients
482 for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++)
483 {
484 if (!client->knowntoqc)
485 continue;
486
487 MSG_WriteByte (&client->message, svc_updatename);
488 MSG_WriteByte (&client->message, host_client - svs.clients);
489 MSG_WriteString (&client->message, "");
490 MSG_WriteByte (&client->message, svc_updatecolors);
491 MSG_WriteByte (&client->message, host_client - svs.clients);
492 MSG_WriteByte (&client->message, 0);
493
494 MSG_WriteByte (&client->message, svc_updatefrags);
495 MSG_WriteByte (&client->message, host_client - svs.clients);
496 MSG_WriteShort (&client->message, 0);
497 }
498 }
499
500 /*
501 ==================
502 Host_ShutdownServer
503
504 This only happens at the end of a game, not between levels
505 ==================
506 */
Host_ShutdownServer(qboolean crash)507 void Host_ShutdownServer(qboolean crash)
508 {
509 int i;
510 int count;
511 sizebuf_t buf;
512 byte message[4];
513 double start;
514
515 if (!sv.active)
516 return;
517
518 sv.active = false;
519
520 // stop all client sounds immediately
521 if (cls.state == ca_connected)
522 CL_Disconnect ();
523
524 // flush any pending messages - like the score!!!
525 start = Sys_DoubleTime();
526 do
527 {
528 count = 0;
529 for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
530 {
531 if (host_client->active && host_client->message.cursize && host_client->netconnection)
532 {
533 if (NET_CanSendMessage (host_client->netconnection))
534 {
535 NET_SendMessage(host_client->netconnection, &host_client->message);
536 SZ_Clear (&host_client->message);
537 }
538 else
539 {
540 NET_GetMessage(host_client->netconnection);
541 count++;
542 }
543 }
544 }
545 if ((Sys_DoubleTime() - start) > 3.0)
546 break;
547 }
548 while (count);
549
550 // make sure all the clients know we're disconnecting
551 buf.data = message;
552 buf.maxsize = 4;
553 buf.cursize = 0;
554 MSG_WriteByte(&buf, svc_disconnect);
555 count = NET_SendToAll(&buf, 5.0);
556 if (count)
557 Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
558
559 PR_SwitchQCVM(&sv.qcvm);
560 for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
561 if (host_client->active)
562 SV_DropClient(crash);
563
564 qcvm->worldmodel = NULL;
565 PR_SwitchQCVM(NULL);
566
567 //
568 // clear structures
569 //
570 // memset (&sv, 0, sizeof(sv)); // ServerSpawn already do this by Host_ClearMemory
571 memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t));
572 }
573
574
575 /*
576 ================
577 Host_ClearMemory
578
579 This clears all the memory used by both the client and server, but does
580 not reinitialize anything.
581 ================
582 */
Host_ClearMemory(void)583 void Host_ClearMemory (void)
584 {
585 if (cl.qcvm.extfuncs.CSQC_Shutdown)
586 {
587 PR_SwitchQCVM(&cl.qcvm);
588 PR_ExecuteProgram(qcvm->extfuncs.CSQC_Shutdown);
589 qcvm->extfuncs.CSQC_Shutdown = 0;
590 PR_SwitchQCVM(NULL);
591 }
592
593 Con_DPrintf ("Clearing memory\n");
594 Mod_ClearAll ();
595 Sky_ClearAll();
596 /* host_hunklevel MUST be set at this point */
597 Hunk_FreeToLowMark (host_hunklevel);
598 cls.signon = 0;
599 PR_ClearProgs(&sv.qcvm);
600 free(sv.static_entities); //spike -- this is dynamic too, now
601
602 memset (&sv, 0, sizeof(sv));
603
604 CL_FreeState();
605 }
606
607
608 //==============================================================================
609 //
610 // Host Frame
611 //
612 //==============================================================================
613
614 /*
615 ===================
616 Host_FilterTime
617
618 Returns false if the time is too short to run a frame
619 ===================
620 */
Host_FilterTime(float time)621 qboolean Host_FilterTime (float time)
622 {
623 float maxfps; //johnfitz
624 float min_frame_time;
625 float delta_since_last_frame;
626
627 realtime += time;
628 delta_since_last_frame = realtime - oldrealtime;
629
630 if (host_maxfps.value)
631 {
632 //johnfitz -- max fps cvar
633 maxfps = CLAMP (10.0, host_maxfps.value, 1000.0);
634
635 // Check if we still have more than 2ms till next frame and if so wait for "1ms"
636 // E.g. Windows is not a real time OS and the sleeps can vary in length even with timeBeginPeriod(1)
637 min_frame_time = 1.0f / maxfps;
638 if((min_frame_time - delta_since_last_frame) > (2.0f/1000.0f))
639 SDL_Delay(1);
640
641 if (!cls.timedemo && (delta_since_last_frame < min_frame_time))
642 return false; // framerate is too high
643 //johnfitz
644 }
645
646 host_frametime = delta_since_last_frame;
647 oldrealtime = realtime;
648
649 //johnfitz -- host_timescale is more intuitive than host_framerate
650 if (host_timescale.value > 0)
651 host_frametime *= host_timescale.value;
652 //johnfitz
653 else if (host_framerate.value > 0)
654 host_frametime = host_framerate.value;
655 else if (host_maxfps.value)// don't allow really long or short frames
656 host_frametime = CLAMP (0.0001, host_frametime, 0.1); //johnfitz -- use CLAMP
657
658 return true;
659 }
660
661 /*
662 ===================
663 Host_GetConsoleCommands
664
665 Add them exactly as if they had been typed at the console
666 ===================
667 */
Host_GetConsoleCommands(void)668 void Host_GetConsoleCommands (void)
669 {
670 const char *cmd;
671
672 if (!isDedicated)
673 return; // no stdin necessary in graphical mode
674
675 while (1)
676 {
677 cmd = Sys_ConsoleInput ();
678 if (!cmd)
679 break;
680 Cbuf_AddText (cmd);
681 }
682 }
683
684 /*
685 ==================
686 Host_ServerFrame
687 ==================
688 */
Host_ServerFrame(void)689 void Host_ServerFrame (void)
690 {
691 int i, active; //johnfitz
692 edict_t *ent; //johnfitz
693
694 // run the world state
695 pr_global_struct->frametime = host_frametime;
696
697 // set the time and clear the general datagram
698 SV_ClearDatagram ();
699
700 // check for new clients
701 SV_CheckForNewClients ();
702
703 // read client messages
704 SV_RunClients ();
705
706 // move things around and think
707 // always pause in single player if in console or menus
708 if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
709 SV_Physics ();
710
711 //johnfitz -- devstats
712 if (cls.signon == SIGNONS)
713 {
714 for (i=0, active=0; i<qcvm->num_edicts; i++)
715 {
716 ent = EDICT_NUM(i);
717 if (!ent->free)
718 active++;
719 }
720 if (active > 600 && dev_peakstats.edicts <= 600)
721 Con_DWarning ("%i edicts exceeds standard limit of 600 (max = %d).\n", active, qcvm->max_edicts);
722 dev_stats.edicts = active;
723 dev_peakstats.edicts = q_max(active, dev_peakstats.edicts);
724 }
725 //johnfitz
726
727 // send all messages to the clients
728 SV_SendClientMessages ();
729 }
730
CL_LoadCSProgs(void)731 static void CL_LoadCSProgs(void)
732 {
733 PR_ClearProgs(&cl.qcvm);
734 if (pr_checkextension.value && !cl_nocsqc.value)
735 { //only try to use csqc if qc extensions are enabled.
736 char versionedname[MAX_QPATH];
737 unsigned int csqchash;
738 PR_SwitchQCVM(&cl.qcvm);
739 csqchash = strtoul(Info_GetKey(cl.serverinfo, "*csprogs", versionedname, sizeof(versionedname)), NULL, 0);
740
741 q_snprintf(versionedname, MAX_QPATH, "csprogsvers/%x.dat", csqchash);
742
743 //try csprogs.dat first, then fall back on progs.dat in case someone tried merging the two.
744 //we only care about it if it actually contains a CSQC_DrawHud, otherwise its either just a (misnamed) ssqc progs or a full csqc progs that would just crash us on 3d stuff.
745 if ((PR_LoadProgs(versionedname, false, PROGHEADER_CRC, pr_csqcbuiltins, pr_csqcnumbuiltins) && qcvm->extfuncs.CSQC_DrawHud)||
746 (PR_LoadProgs("csprogs.dat", false, PROGHEADER_CRC, pr_csqcbuiltins, pr_csqcnumbuiltins) && qcvm->extfuncs.CSQC_DrawHud)||
747 (PR_LoadProgs("progs.dat", false, PROGHEADER_CRC, pr_csqcbuiltins, pr_csqcnumbuiltins) && qcvm->extfuncs.CSQC_DrawHud))
748 {
749 qcvm->max_edicts = CLAMP (MIN_EDICTS,(int)max_edicts.value,MAX_EDICTS);
750 qcvm->edicts = (edict_t *) malloc (qcvm->max_edicts*qcvm->edict_size);
751 qcvm->num_edicts = qcvm->reserved_edicts = 1;
752 memset(qcvm->edicts, 0, qcvm->num_edicts*qcvm->edict_size);
753
754 if (!qcvm->extfuncs.CSQC_DrawHud)
755 { //no simplecsqc entry points... abort entirely!
756 PR_ClearProgs(qcvm);
757 PR_SwitchQCVM(NULL);
758 return;
759 }
760
761 //set a few globals, if they exist
762 if (qcvm->extglobals.maxclients)
763 *qcvm->extglobals.maxclients = cl.maxclients;
764 pr_global_struct->time = cl.time;
765 pr_global_struct->mapname = PR_SetEngineString(cl.mapname);
766 pr_global_struct->total_monsters = cl.statsf[STAT_TOTALMONSTERS];
767 pr_global_struct->total_secrets = cl.statsf[STAT_TOTALSECRETS];
768 pr_global_struct->deathmatch = cl.gametype;
769 pr_global_struct->coop = (cl.gametype == GAME_COOP) && cl.maxclients != 1;
770 if (qcvm->extglobals.player_localnum)
771 *qcvm->extglobals.player_localnum = cl.viewentity-1; //this is a guess, but is important for scoreboards.
772
773 //set a few worldspawn fields too
774 qcvm->edicts->v.solid = SOLID_BSP;
775 qcvm->edicts->v.modelindex = 1;
776 qcvm->edicts->v.model = PR_SetEngineString(cl.worldmodel->name);
777 VectorCopy(cl.worldmodel->mins, qcvm->edicts->v.mins);
778 VectorCopy(cl.worldmodel->maxs, qcvm->edicts->v.maxs);
779 qcvm->edicts->v.message = PR_SetEngineString(cl.levelname);
780
781 //and call the init function... if it exists.
782 qcvm->worldmodel = cl.worldmodel;
783 SV_ClearWorld();
784 if (qcvm->extfuncs.CSQC_Init)
785 {
786 int maj = (int)VKQUAKE_VERSION;
787 int min = (VKQUAKE_VERSION-maj) * 100;
788 G_FLOAT(OFS_PARM0) = false;
789 G_INT(OFS_PARM1) = PR_SetEngineString("vkQuake");
790 G_FLOAT(OFS_PARM2) = 10000*maj + 100*(min) + VKQUAKE_VER_PATCH;
791 PR_ExecuteProgram(qcvm->extfuncs.CSQC_Init);
792 }
793 }
794 else
795 PR_ClearProgs(qcvm);
796 PR_SwitchQCVM(NULL);
797 }
798 }
799
800 /*
801 ==================
802 Host_Frame
803
804 Runs all active servers
805 ==================
806 */
_Host_Frame(double time)807 void _Host_Frame (double time)
808 {
809 static double accumtime = 0;
810 static double time1 = 0;
811 static double time2 = 0;
812 static double time3 = 0;
813 int pass1, pass2, pass3;
814
815 if (setjmp (host_abortserver) )
816 return; // something bad happened, or the server disconnected
817
818 // keep the random time dependent
819 rand ();
820
821 // decide the simulation time
822 accumtime += host_netinterval?CLAMP(0, time, 0.2):0; //for renderer/server isolation
823 if (!Host_FilterTime (time))
824 return; // don't run too fast, or packets will flood out
825
826 // get new key events
827 Key_UpdateForDest ();
828 IN_UpdateInputMode ();
829 Sys_SendKeyEvents ();
830
831 // allow mice or other external controllers to add commands
832 IN_Commands ();
833
834 //check the stdin for commands (dedicated servers)
835 Host_GetConsoleCommands ();
836
837 // process console commands
838 Cbuf_Execute ();
839
840 NET_Poll();
841
842 if (cl.sendprespawn)
843 {
844 CL_LoadCSProgs();
845
846 cl.sendprespawn = false;
847 MSG_WriteByte (&cls.message, clc_stringcmd);
848 MSG_WriteString (&cls.message, "prespawn");
849 vid.recalc_refdef = true;
850 }
851
852 CL_AccumulateCmd ();
853
854 //Run the server+networking (client->server->client), at a different rate from everyt
855 while ((host_netinterval == 0) || (accumtime >= host_netinterval))
856 {
857 float realframetime = host_frametime;
858 if (host_netinterval)
859 {
860 host_frametime = host_netinterval;
861 accumtime -= host_frametime;
862 if (host_timescale.value > 0)
863 host_frametime *= host_timescale.value;
864 else if (host_framerate.value)
865 host_frametime = host_framerate.value;
866 }
867
868 CL_SendCmd ();
869 if (sv.active)
870 {
871 PR_SwitchQCVM(&sv.qcvm);
872 Host_ServerFrame ();
873 PR_SwitchQCVM(NULL);
874 }
875 host_frametime = realframetime;
876 Cbuf_Waited();
877
878 if (host_netinterval == 0)
879 break;
880 }
881
882 if (cl.qcvm.progs)
883 {
884 PR_SwitchQCVM(&cl.qcvm);
885 pr_global_struct->frametime = host_frametime;
886 SV_Physics();
887 PR_SwitchQCVM(NULL);
888 }
889
890 // fetch results from server
891 if (cls.state == ca_connected)
892 CL_ReadFromServer ();
893
894 // update video
895 if (host_speeds.value)
896 time1 = Sys_DoubleTime ();
897
898 SCR_UpdateScreen ();
899
900 CL_RunParticles (); //johnfitz -- seperated from rendering
901
902 if (host_speeds.value)
903 time2 = Sys_DoubleTime ();
904
905 // update audio
906 BGM_Update(); // adds music raw samples and/or advances midi driver
907 if (cls.signon == SIGNONS)
908 {
909 S_Update (r_origin, vpn, vright, vup);
910 CL_DecayLights ();
911 }
912 else
913 S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
914
915 CDAudio_Update();
916
917 if (host_speeds.value)
918 {
919 pass1 = (time1 - time3)*1000;
920 time3 = Sys_DoubleTime ();
921 pass2 = (time2 - time1)*1000;
922 pass3 = (time3 - time2)*1000;
923 Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
924 pass1+pass2+pass3, pass1, pass2, pass3);
925 }
926
927 host_framecount++;
928
929 }
930
Host_Frame(double time)931 void Host_Frame (double time)
932 {
933 double time1, time2;
934 static double timetotal;
935 static int timecount;
936 int i, c, m;
937
938 if (!serverprofile.value)
939 {
940 _Host_Frame (time);
941 return;
942 }
943
944 time1 = Sys_DoubleTime ();
945 _Host_Frame (time);
946 time2 = Sys_DoubleTime ();
947
948 timetotal += time2 - time1;
949 timecount++;
950
951 if (timecount < 1000)
952 return;
953
954 m = timetotal*1000/timecount;
955 timecount = 0;
956 timetotal = 0;
957 c = 0;
958 for (i = 0; i < svs.maxclients; i++)
959 {
960 if (svs.clients[i].active)
961 c++;
962 }
963
964 Con_Printf ("serverprofile: %2i clients %2i msec\n", c, m);
965 }
966
967 /*
968 ====================
969 Host_Init
970 ====================
971 */
Host_Init(void)972 void Host_Init (void)
973 {
974 if (standard_quake)
975 minimum_memory = MINIMUM_MEMORY;
976 else minimum_memory = MINIMUM_MEMORY_LEVELPAK;
977
978 if (COM_CheckParm ("-minmemory"))
979 host_parms->memsize = minimum_memory;
980
981 if (host_parms->memsize < minimum_memory)
982 Sys_Error ("Only %4.1f megs of memory available, can't execute game", host_parms->memsize / (float)0x100000);
983
984 com_argc = host_parms->argc;
985 com_argv = host_parms->argv;
986
987 Memory_Init (host_parms->membase, host_parms->memsize);
988 Cbuf_Init ();
989 Cmd_Init ();
990 LOG_Init (host_parms);
991 Cvar_Init (); //johnfitz
992 COM_Init ();
993 COM_InitFilesystem ();
994 Host_InitLocal ();
995 W_LoadWadFile (); //johnfitz -- filename is now hard-coded for honesty
996 if (cls.state != ca_dedicated)
997 {
998 Key_Init ();
999 Con_Init ();
1000 }
1001 PR_Init ();
1002 Mod_Init ();
1003 NET_Init ();
1004 SV_Init ();
1005
1006 Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n");
1007 Con_Printf ("%4.1f megabyte heap\n", host_parms->memsize/ (1024*1024.0));
1008
1009 if (cls.state != ca_dedicated)
1010 {
1011 host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp", NULL);
1012 if (!host_colormap)
1013 Sys_Error ("Couldn't load gfx/colormap.lmp");
1014
1015 V_Init ();
1016 Chase_Init ();
1017 M_Init ();
1018 ExtraMaps_Init (); //johnfitz
1019 Modlist_Init (); //johnfitz
1020 DemoList_Init (); //ericw
1021 VID_Init ();
1022 IN_Init ();
1023 TexMgr_Init (); //johnfitz
1024 Draw_Init ();
1025 SCR_Init ();
1026 R_Init ();
1027 S_Init ();
1028 CDAudio_Init ();
1029 BGM_Init();
1030 Sbar_Init ();
1031 CL_Init ();
1032 }
1033
1034 LOC_Init (); // for 2021 rerelease support.
1035
1036 Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
1037 host_hunklevel = Hunk_LowMark ();
1038
1039 host_initialized = true;
1040 Con_Printf ("\n========= Quake Initialized =========\n\n");
1041
1042 if (cls.state != ca_dedicated)
1043 {
1044 Cbuf_InsertText ("exec quake.rc\n");
1045 // johnfitz -- in case the vid mode was locked during vid_init, we can unlock it now.
1046 // note: two leading newlines because the command buffer swallows one of them.
1047 Cbuf_AddText ("\n\nvid_unlock\n");
1048 }
1049
1050 if (cls.state == ca_dedicated)
1051 {
1052 Cbuf_AddText ("exec autoexec.cfg\n");
1053 Cbuf_AddText ("stuffcmds");
1054 Cbuf_Execute ();
1055 if (!sv.active)
1056 Cbuf_AddText ("map start\n");
1057 }
1058 }
1059
1060
1061 /*
1062 ===============
1063 Host_Shutdown
1064
1065 FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
1066 to run quit through here before the final handoff to the sys code.
1067 ===============
1068 */
Host_Shutdown(void)1069 void Host_Shutdown(void)
1070 {
1071 static qboolean isdown = false;
1072
1073 if (isdown)
1074 {
1075 printf ("recursive shutdown\n");
1076 return;
1077 }
1078 isdown = true;
1079
1080 // keep Con_Printf from trying to update the screen
1081 scr_disabled_for_loading = true;
1082
1083 Host_WriteConfiguration ();
1084
1085 NET_Shutdown ();
1086
1087 if (cls.state != ca_dedicated)
1088 {
1089 if (con_initialized)
1090 History_Shutdown ();
1091 BGM_Shutdown();
1092 CDAudio_Shutdown ();
1093 S_Shutdown ();
1094 IN_Shutdown ();
1095 VID_Shutdown();
1096 }
1097
1098 LOG_Close ();
1099
1100 LOC_Shutdown ();
1101 }
1102
1103