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