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