2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // host.c -- coordinates spawning and killing of local servers
28 A server can allways be started, even if the system started out as a client
31 A client can NOT be started if the system started as a dedicated server.
33 Memory is cleared / released when a server or client begins, not when they end.
37 quakeparms_t host_parms
;
39 qboolean host_initialized
; // true if into command execution
41 double host_frametime
;
43 double realtime
; // without any filtering or bounding
44 double oldrealtime
; // last frame run
51 client_t
*host_client
; // current client
53 jmp_buf host_abortserver
;
58 cvar_t host_framerate
= {"host_framerate","0"}; // set for slow motion
59 cvar_t host_speeds
= {"host_speeds","0"}; // set for running times
61 cvar_t sys_ticrate
= {"sys_ticrate","0.05"};
62 cvar_t serverprofile
= {"serverprofile","0"};
64 cvar_t fraglimit
= {"fraglimit","0",false,true};
65 cvar_t timelimit
= {"timelimit","0",false,true};
66 cvar_t teamplay
= {"teamplay","0",false,true};
68 cvar_t samelevel
= {"samelevel","0"};
69 cvar_t noexit
= {"noexit","0",false,true};
72 cvar_t developer
= {"developer","1"}; // should be 0 for release!
74 cvar_t developer
= {"developer","0"};
77 cvar_t skill
= {"skill","1"}; // 0 - 3
78 cvar_t deathmatch
= {"deathmatch","0"}; // 0, 1, or 2
79 cvar_t coop
= {"coop","0"}; // 0 or 1
81 cvar_t pausable
= {"pausable","1"};
83 cvar_t temp1
= {"temp1","0"};
91 void Host_EndGame (char *message
, ...)
96 va_start (argptr
,message
);
97 vsprintf (string
,message
,argptr
);
99 Con_DPrintf ("Host_EndGame: %s\n",string
);
102 Host_ShutdownServer (false);
104 if (cls
.state
== ca_dedicated
)
105 Sys_Error ("Host_EndGame: %s\n",string
); // dedicated servers exit
107 if (cls
.demonum
!= -1)
112 longjmp (host_abortserver
, 1);
119 This shuts down both the client and server
122 void Host_Error (char *error
, ...)
126 static qboolean inerror
= false;
129 Sys_Error ("Host_Error: recursively entered");
132 SCR_EndLoadingPlaque (); // reenable screen updates
134 va_start (argptr
,error
);
135 vsprintf (string
,error
,argptr
);
137 Con_Printf ("Host_Error: %s\n",string
);
140 Host_ShutdownServer (false);
142 if (cls
.state
== ca_dedicated
)
143 Sys_Error ("Host_Error: %s\n",string
); // dedicated servers exit
150 longjmp (host_abortserver
, 1);
158 void Host_FindMaxClients (void)
164 i
= COM_CheckParm ("-dedicated");
167 cls
.state
= ca_dedicated
;
168 if (i
!= (com_argc
- 1))
170 svs
.maxclients
= Q_atoi (com_argv
[i
+1]);
176 cls
.state
= ca_disconnected
;
178 i
= COM_CheckParm ("-listen");
181 if (cls
.state
== ca_dedicated
)
182 Sys_Error ("Only one of -dedicated or -listen can be specified");
183 if (i
!= (com_argc
- 1))
184 svs
.maxclients
= Q_atoi (com_argv
[i
+1]);
188 if (svs
.maxclients
< 1)
190 else if (svs
.maxclients
> MAX_SCOREBOARD
)
191 svs
.maxclients
= MAX_SCOREBOARD
;
193 svs
.maxclientslimit
= svs
.maxclients
;
194 if (svs
.maxclientslimit
< 4)
195 svs
.maxclientslimit
= 4;
196 svs
.clients
= Hunk_AllocName (svs
.maxclientslimit
*sizeof(client_t
), "clients");
198 if (svs
.maxclients
> 1)
199 Cvar_SetValue ("deathmatch", 1.0);
201 Cvar_SetValue ("deathmatch", 0.0);
206 =======================
208 ======================
210 void Host_InitLocal (void)
212 Host_InitCommands ();
214 Cvar_RegisterVariable (&host_framerate
);
215 Cvar_RegisterVariable (&host_speeds
);
217 Cvar_RegisterVariable (&sys_ticrate
);
218 Cvar_RegisterVariable (&serverprofile
);
220 Cvar_RegisterVariable (&fraglimit
);
221 Cvar_RegisterVariable (&timelimit
);
222 Cvar_RegisterVariable (&teamplay
);
223 Cvar_RegisterVariable (&samelevel
);
224 Cvar_RegisterVariable (&noexit
);
225 Cvar_RegisterVariable (&skill
);
226 Cvar_RegisterVariable (&developer
);
227 Cvar_RegisterVariable (&deathmatch
);
228 Cvar_RegisterVariable (&coop
);
230 Cvar_RegisterVariable (&pausable
);
232 Cvar_RegisterVariable (&temp1
);
234 Host_FindMaxClients ();
236 host_time
= 1.0; // so a think at time 0 won't get called
242 Host_WriteConfiguration
244 Writes key bindings and archived cvars to config.cfg
247 void Host_WriteConfiguration (void)
251 // dedicated servers initialize the host but don't parse and set the
253 if (host_initialized
& !isDedicated
)
255 f
= fopen (va("%s/config.cfg",com_gamedir
), "w");
258 Con_Printf ("Couldn't write config.cfg.\n");
262 Key_WriteBindings (f
);
263 Cvar_WriteVariables (f
);
274 Sends text across to be displayed
275 FIXME: make this just a stuffed echo?
278 void SV_ClientPrintf (char *fmt
, ...)
283 va_start (argptr
,fmt
);
284 vsprintf (string
, fmt
,argptr
);
287 MSG_WriteByte (&host_client
->message
, svc_print
);
288 MSG_WriteString (&host_client
->message
, string
);
295 Sends text to all active clients
298 void SV_BroadcastPrintf (char *fmt
, ...)
304 va_start (argptr
,fmt
);
305 vsprintf (string
, fmt
,argptr
);
308 for (i
=0 ; i
<svs
.maxclients
; i
++)
309 if (svs
.clients
[i
].active
&& svs
.clients
[i
].spawned
)
311 MSG_WriteByte (&svs
.clients
[i
].message
, svc_print
);
312 MSG_WriteString (&svs
.clients
[i
].message
, string
);
320 Send text over to the client to be executed
323 void Host_ClientCommands (char *fmt
, ...)
328 va_start (argptr
,fmt
);
329 vsprintf (string
, fmt
,argptr
);
332 MSG_WriteByte (&host_client
->message
, svc_stufftext
);
333 MSG_WriteString (&host_client
->message
, string
);
337 =====================
340 Called when the player is getting totally kicked off the host
341 if (crash = true), don't bother sending signofs
342 =====================
344 void SV_DropClient (qboolean crash
)
352 // send any final messages (don't check for errors)
353 if (NET_CanSendMessage (host_client
->netconnection
))
355 MSG_WriteByte (&host_client
->message
, svc_disconnect
);
356 NET_SendMessage (host_client
->netconnection
, &host_client
->message
);
359 if (host_client
->edict
&& host_client
->spawned
)
361 // call the prog function for removing a client
362 // this will set the body to a dead frame, among other things
363 saveSelf
= pr_global_struct
->self
;
364 pr_global_struct
->self
= EDICT_TO_PROG(host_client
->edict
);
365 PR_ExecuteProgram (pr_global_struct
->ClientDisconnect
);
366 pr_global_struct
->self
= saveSelf
;
369 Sys_Printf ("Client %s removed\n",host_client
->name
);
372 // break the net connection
373 NET_Close (host_client
->netconnection
);
374 host_client
->netconnection
= NULL
;
376 // free the client (the body stays around)
377 host_client
->active
= false;
378 host_client
->name
[0] = 0;
379 host_client
->old_frags
= -999999;
380 net_activeconnections
--;
382 // send notification to all clients
383 for (i
=0, client
= svs
.clients
; i
<svs
.maxclients
; i
++, client
++)
387 MSG_WriteByte (&client
->message
, svc_updatename
);
388 MSG_WriteByte (&client
->message
, host_client
- svs
.clients
);
389 MSG_WriteString (&client
->message
, "");
390 MSG_WriteByte (&client
->message
, svc_updatefrags
);
391 MSG_WriteByte (&client
->message
, host_client
- svs
.clients
);
392 MSG_WriteShort (&client
->message
, 0);
393 MSG_WriteByte (&client
->message
, svc_updatecolors
);
394 MSG_WriteByte (&client
->message
, host_client
- svs
.clients
);
395 MSG_WriteByte (&client
->message
, 0);
403 This only happens at the end of a game, not between levels
406 void Host_ShutdownServer(qboolean crash
)
419 // stop all client sounds immediately
420 if (cls
.state
== ca_connected
)
423 // flush any pending messages - like the score!!!
424 start
= Sys_FloatTime();
428 for (i
=0, host_client
= svs
.clients
; i
<svs
.maxclients
; i
++, host_client
++)
430 if (host_client
->active
&& host_client
->message
.cursize
)
432 if (NET_CanSendMessage (host_client
->netconnection
))
434 NET_SendMessage(host_client
->netconnection
, &host_client
->message
);
435 SZ_Clear (&host_client
->message
);
439 NET_GetMessage(host_client
->netconnection
);
444 if ((Sys_FloatTime() - start
) > 3.0)
449 // make sure all the clients know we're disconnecting
453 MSG_WriteByte(&buf
, svc_disconnect
);
454 count
= NET_SendToAll(&buf
, 5);
456 Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count
);
458 for (i
=0, host_client
= svs
.clients
; i
<svs
.maxclients
; i
++, host_client
++)
459 if (host_client
->active
)
460 SV_DropClient(crash
);
465 memset (&sv
, 0, sizeof(sv
));
466 memset (svs
.clients
, 0, svs
.maxclientslimit
*sizeof(client_t
));
474 This clears all the memory used by both the client and server, but does
475 not reinitialize anything.
478 void Host_ClearMemory (void)
480 Con_DPrintf ("Clearing memory\n");
484 Hunk_FreeToLowMark (host_hunklevel
);
487 memset (&sv
, 0, sizeof(sv
));
488 memset (&cl
, 0, sizeof(cl
));
492 //============================================================================
499 Returns false if the time is too short to run a frame
502 qboolean
Host_FilterTime (float time
)
506 if (!cls
.timedemo
&& realtime
- oldrealtime
< 1.0/72.0)
507 return false; // framerate is too high
509 host_frametime
= realtime
- oldrealtime
;
510 oldrealtime
= realtime
;
512 if (host_framerate
.value
> 0)
513 host_frametime
= host_framerate
.value
;
515 { // don't allow really long or short frames
516 if (host_frametime
> 0.1)
517 host_frametime
= 0.1;
518 if (host_frametime
< 0.001)
519 host_frametime
= 0.001;
528 Host_GetConsoleCommands
530 Add them exactly as if they had been typed at the console
533 void Host_GetConsoleCommands (void)
539 cmd
= Sys_ConsoleInput ();
555 void _Host_ServerFrame (void)
557 // run the world state
558 pr_global_struct
->frametime
= host_frametime
;
560 // read client messages
563 // move things around and think
564 // always pause in single player if in console or menus
565 if (!sv
.paused
&& (svs
.maxclients
> 1 || key_dest
== key_game
) )
569 void Host_ServerFrame (void)
571 float save_host_frametime
;
572 float temp_host_frametime
;
574 // run the world state
575 pr_global_struct
->frametime
= host_frametime
;
577 // set the time and clear the general datagram
580 // check for new clients
581 SV_CheckForNewClients ();
583 temp_host_frametime
= save_host_frametime
= host_frametime
;
584 while(temp_host_frametime
> (1.0/72.0))
586 if (temp_host_frametime
> 0.05)
587 host_frametime
= 0.05;
589 host_frametime
= temp_host_frametime
;
590 temp_host_frametime
-= host_frametime
;
591 _Host_ServerFrame ();
593 host_frametime
= save_host_frametime
;
595 // send all messages to the clients
596 SV_SendClientMessages ();
601 void Host_ServerFrame (void)
603 // run the world state
604 pr_global_struct
->frametime
= host_frametime
;
606 // set the time and clear the general datagram
609 // check for new clients
610 SV_CheckForNewClients ();
612 // read client messages
615 // move things around and think
616 // always pause in single player if in console or menus
617 if (!sv
.paused
&& (svs
.maxclients
> 1 || key_dest
== key_game
) )
620 // send all messages to the clients
621 SV_SendClientMessages ();
631 Runs all active servers
634 void _Host_Frame (float time
)
636 static double time1
= 0;
637 static double time2
= 0;
638 static double time3
= 0;
639 int pass1
, pass2
, pass3
;
641 if (setjmp (host_abortserver
) )
642 return; // something bad happened, or the server disconnected
644 // keep the random time dependent
647 // decide the simulation time
648 if (!Host_FilterTime (time
))
649 return; // don't run too fast, or packets will flood out
651 // get new key events
652 Sys_SendKeyEvents ();
654 // allow mice or other external controllers to add commands
657 // process console commands
662 // if running the server locally, make intentions now
666 //-------------------
670 //-------------------
672 // check for commands typed to the host
673 Host_GetConsoleCommands ();
678 //-------------------
682 //-------------------
684 // if running the server remotely, send intentions now after
685 // the incoming messages have been read
689 host_time
+= host_frametime
;
691 // fetch results from server
692 if (cls
.state
== ca_connected
)
694 CL_ReadFromServer ();
698 if (host_speeds
.value
)
699 time1
= Sys_FloatTime ();
703 if (host_speeds
.value
)
704 time2
= Sys_FloatTime ();
707 if (cls
.signon
== SIGNONS
)
709 S_Update (r_origin
, vpn
, vright
, vup
);
713 S_Update (vec3_origin
, vec3_origin
, vec3_origin
, vec3_origin
);
717 if (host_speeds
.value
)
719 pass1
= (time1
- time3
)*1000;
720 time3
= Sys_FloatTime ();
721 pass2
= (time2
- time1
)*1000;
722 pass3
= (time3
- time2
)*1000;
723 Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
724 pass1
+pass2
+pass3
, pass1
, pass2
, pass3
);
730 void Host_Frame (float time
)
733 static double timetotal
;
734 static int timecount
;
737 if (!serverprofile
.value
)
743 time1
= Sys_FloatTime ();
745 time2
= Sys_FloatTime ();
747 timetotal
+= time2
- time1
;
750 if (timecount
< 1000)
753 m
= timetotal
*1000/timecount
;
757 for (i
=0 ; i
<svs
.maxclients
; i
++)
759 if (svs
.clients
[i
].active
)
763 Con_Printf ("serverprofile: %2i clients %2i msec\n", c
, m
);
766 //============================================================================
770 #define VCR_SIGNATURE 0x56435231
773 void Host_InitVCR (quakeparms_t
*parms
)
778 if (COM_CheckParm("-playback"))
781 Sys_Error("No other parameters allowed with -playback\n");
783 Sys_FileOpenRead("quake.vcr", &vcrFile
);
785 Sys_Error("playback file not found\n");
787 Sys_FileRead (vcrFile
, &i
, sizeof(int));
788 if (i
!= VCR_SIGNATURE
)
789 Sys_Error("Invalid signature in vcr file\n");
791 Sys_FileRead (vcrFile
, &com_argc
, sizeof(int));
792 com_argv
= malloc(com_argc
* sizeof(char *));
793 com_argv
[0] = parms
->argv
[0];
794 for (i
= 0; i
< com_argc
; i
++)
796 Sys_FileRead (vcrFile
, &len
, sizeof(int));
798 Sys_FileRead (vcrFile
, p
, len
);
801 com_argc
++; /* add one for arg[0] */
802 parms
->argc
= com_argc
;
803 parms
->argv
= com_argv
;
806 if ( (n
= COM_CheckParm("-record")) != 0)
808 vcrFile
= Sys_FileOpenWrite("quake.vcr");
811 Sys_FileWrite(vcrFile
, &i
, sizeof(int));
813 Sys_FileWrite(vcrFile
, &i
, sizeof(int));
814 for (i
= 1; i
< com_argc
; i
++)
819 Sys_FileWrite(vcrFile
, &len
, sizeof(int));
820 Sys_FileWrite(vcrFile
, "-playback", len
);
823 len
= Q_strlen(com_argv
[i
]) + 1;
824 Sys_FileWrite(vcrFile
, &len
, sizeof(int));
825 Sys_FileWrite(vcrFile
, com_argv
[i
], len
);
836 void Host_Init (quakeparms_t
*parms
)
840 minimum_memory
= MINIMUM_MEMORY
;
842 minimum_memory
= MINIMUM_MEMORY_LEVELPAK
;
844 if (COM_CheckParm ("-minmemory"))
845 parms
->memsize
= minimum_memory
;
849 if (parms
->memsize
< minimum_memory
)
850 Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms
->memsize
/ (float)0x100000);
852 com_argc
= parms
->argc
;
853 com_argv
= parms
->argv
;
855 printf("memsize: %d\n", parms
->memsize
);
856 Memory_Init (parms
->membase
, parms
->memsize
);
858 printf("Memroy inited\n");
860 printf("Cbuf inited\n");
862 printf("Cmd inited\n");
864 printf("V inited\n");
866 printf("Chase inited\n");
867 Host_InitVCR (parms
);
868 printf("VCR inited\n");
869 COM_Init (parms
->basedir
);
870 printf("COM inited\n");
872 printf("Host local inited\n");
873 W_LoadWadFile ("gfx.wad");
874 printf("wadfile loaded\n");
877 printf("keyboard inited\n");
879 printf("console inited\n");
881 printf("M inited\n");
883 printf("PR inited\n");
885 printf("Mod inited\n");
887 printf("Net inited\n");
889 printf("SV inited\n");
891 Con_Printf ("Exe: "__TIME__
" "__DATE__
"\n");
892 Con_Printf ("%4.1f megabyte heap\n",parms
->memsize
/ (1024*1024.0));
894 R_InitTextures (); // needed even for dedicated servers
896 if (cls
.state
!= ca_dedicated
)
898 host_basepal
= (byte
*)COM_LoadHunkFile ("gfx/palette.lmp");
900 Sys_Error ("Couldn't load gfx/palette.lmp");
901 host_colormap
= (byte
*)COM_LoadHunkFile ("gfx/colormap.lmp");
903 Sys_Error ("Couldn't load gfx/colormap.lmp");
905 #ifndef _WIN32 // on non win32, mouse comes before video for security reasons
908 VID_Init (host_basepal
);
914 // on Win32, sound initialization has to come before video initialization, so we
915 // can put up a popup if the sound hardware is in use
916 printf("Initing sound\n");
918 printf("Sound inited\n");
922 // FIXME: doesn't use the new one-window approach yet
930 #ifdef _WIN32 // on non win32, mouse comes before video for security reasons
935 Cbuf_InsertText ("exec quake.rc\n");
937 Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
938 host_hunklevel
= Hunk_LowMark ();
940 host_initialized
= true;
942 Sys_Printf ("========Quake Initialized=========\n");
950 FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
951 to run quit through here before the final handoff to the sys code.
954 void Host_Shutdown(void)
956 static qboolean isdown
= false;
960 printf ("recursive shutdown\n");
965 // keep Con_Printf from trying to update the screen
966 scr_disabled_for_loading
= true;
968 Host_WriteConfiguration ();
975 if (cls
.state
!= ca_dedicated
)