|
|
1.1 root 1:
2: #include "server.h"
3:
4: /*
5: ===============================================================================
6:
7: OPERATOR CONSOLE ONLY COMMANDS
8:
9: These commands can only be entered from stdin or by a remote operator datagram
10: ===============================================================================
11: */
12:
13: /*
14: ====================
15: SV_SetMaster_f
16:
17: Specify a list of master servers
18: ====================
19: */
20: void SV_SetMaster_f (void)
21: {
22: int i, slot;
23:
24: // only dedicated servers send heartbeats
25: if (!dedicated->value)
26: {
27: Com_Printf ("Only dedicated servers use masters.\n");
28: return;
29: }
30:
31: // make sure the server is listed public
32: Cvar_Set ("public", "1");
33:
34: for (i=1 ; i<MAX_MASTERS ; i++)
35: memset (&master_adr[i], 0, sizeof(master_adr[i]));
36:
37: slot = 1; // slot 0 will always contain the id master
38: for (i=1 ; i<Cmd_Argc() ; i++)
39: {
40: if (slot == MAX_MASTERS)
41: break;
42:
43: if (!NET_StringToAdr (Cmd_Argv(i), &master_adr[i]))
44: {
45: Com_Printf ("Bad address: %s\n", Cmd_Argv(i));
46: continue;
47: }
48: if (master_adr[slot].port == 0)
49: master_adr[slot].port = BigShort (PORT_MASTER);
50:
51: Com_Printf ("Master server at %s\n", NET_AdrToString (master_adr[slot]));
52:
53: Com_Printf ("Sending a ping.\n");
54:
55: Netchan_OutOfBandPrint (NS_SERVER, master_adr[slot], "ping");
56:
57: slot++;
58: }
59:
60: svs.last_heartbeat = -9999999;
61: }
62:
63:
64:
65: /*
66: ==================
67: SV_SetPlayer
68:
69: Sets sv_client and sv_player to the player with idnum Cmd_Argv(1)
70: ==================
71: */
72: qboolean SV_SetPlayer (void)
73: {
74: client_t *cl;
75: int i;
76: int idnum;
77: char *s;
78:
79: if (Cmd_Argc() < 2)
80: return false;
81:
82: s = Cmd_Argv(1);
83:
84: // numeric values are just slot numbers
85: if (s[0] >= '0' && s[0] <= '9')
86: {
87: idnum = atoi(Cmd_Argv(1));
88: if (idnum < 0 || idnum >= maxclients->value)
89: {
90: Com_Printf ("Bad client slot: %i\n", idnum);
91: return false;
92: }
93:
94: sv_client = &svs.clients[idnum];
95: sv_player = sv_client->edict;
96: if (!sv_client->state)
97: {
1.1.1.2 ! root 98: Com_Printf ("Client %i is not active\n", idnum);
1.1 root 99: return false;
100: }
101: return true;
102: }
103:
104: // check for a name match
105: for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
106: {
107: if (!cl->state)
108: continue;
109: if (!strcmp(cl->name, s))
110: {
111: sv_client = cl;
112: sv_player = sv_client->edict;
113: return true;
114: }
115: }
116:
117: Com_Printf ("Userid %s is not on the server\n", s);
118: return false;
119: }
120:
121:
122: /*
123: ===============================================================================
124:
125: SAVEGAME FILES
126:
127: ===============================================================================
128: */
129:
130: /*
131: =====================
132: SV_WipeSavegame
133:
134: Delete save/<XXX>/
135: =====================
136: */
137: void SV_WipeSavegame (char *savename)
138: {
139: char name[MAX_OSPATH];
140: char *s;
141:
1.1.1.2 ! root 142: Com_DPrintf("SV_WipeSaveGame(%s)\n", savename);
! 143:
1.1 root 144: Com_sprintf (name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir (), savename);
145: remove (name);
146: Com_sprintf (name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir (), savename);
147: remove (name);
148:
149: Com_sprintf (name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir (), savename);
150: s = Sys_FindFirst( name, 0, 0 );
151: while (s)
152: {
153: remove (s);
154: s = Sys_FindNext( 0, 0 );
155: }
156: Sys_FindClose ();
157: Com_sprintf (name, sizeof(name), "%s/save/%s/*.sv2", FS_Gamedir (), savename);
158: s = Sys_FindFirst(name, 0, 0 );
159: while (s)
160: {
161: remove (s);
162: s = Sys_FindNext( 0, 0 );
163: }
164: Sys_FindClose ();
165: }
166:
167:
168: /*
169: ================
170: CopyFile
171: ================
172: */
173: void CopyFile (char *src, char *dst)
174: {
175: FILE *f1, *f2;
176: int l;
177: byte buffer[65536];
178:
179: Com_DPrintf ("CopyFile (%s, %s)\n", src, dst);
180:
181: f1 = fopen (src, "rb");
182: if (!f1)
183: return;
184: f2 = fopen (dst, "wb");
185: if (!f2)
186: {
187: fclose (f1);
188: return;
189: }
190:
191: while (1)
192: {
193: l = fread (buffer, 1, sizeof(buffer), f1);
194: if (!l)
195: break;
196: fwrite (buffer, 1, l, f2);
197: }
198:
199: fclose (f1);
200: fclose (f2);
201: }
202:
203:
204: /*
205: ================
206: SV_CopySaveGame
207: ================
208: */
209: void SV_CopySaveGame (char *src, char *dst)
210: {
211: char name[MAX_OSPATH], name2[MAX_OSPATH];
212: int l, len;
213: char *found;
214:
1.1.1.2 ! root 215: Com_DPrintf("SV_CopySaveGame(%s, %s)\n", src, dst);
! 216:
1.1 root 217: SV_WipeSavegame (dst);
218:
219: // copy the savegame over
220: Com_sprintf (name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir(), src);
221: Com_sprintf (name2, sizeof(name2), "%s/save/%s/server.ssv", FS_Gamedir(), dst);
222: FS_CreatePath (name2);
223: CopyFile (name, name2);
224:
225: Com_sprintf (name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir(), src);
226: Com_sprintf (name2, sizeof(name2), "%s/save/%s/game.ssv", FS_Gamedir(), dst);
227: CopyFile (name, name2);
228:
229: Com_sprintf (name, sizeof(name), "%s/save/%s/", FS_Gamedir(), src);
230: len = strlen(name);
231: Com_sprintf (name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir(), src);
232: found = Sys_FindFirst(name, 0, 0 );
233: while (found)
234: {
235: strcpy (name+len, found+len);
236:
237: Com_sprintf (name2, sizeof(name2), "%s/save/%s/%s", FS_Gamedir(), dst, found+len);
238: CopyFile (name, name2);
239:
240: // change sav to sv2
241: l = strlen(name);
242: strcpy (name+l-3, "sv2");
243: l = strlen(name2);
244: strcpy (name2+l-3, "sv2");
245: CopyFile (name, name2);
246:
247: found = Sys_FindNext( 0, 0 );
248: }
249: Sys_FindClose ();
250: }
251:
252:
253: /*
254: ==============
255: SV_WriteLevelFile
256:
257: ==============
258: */
259: void SV_WriteLevelFile (void)
260: {
261: char name[MAX_OSPATH];
262: FILE *f;
263:
1.1.1.2 ! root 264: Com_DPrintf("SV_WriteLevelFile()\n");
! 265:
1.1 root 266: Com_sprintf (name, sizeof(name), "%s/save/current/%s.sv2", FS_Gamedir(), sv.name);
267: f = fopen(name, "wb");
268: if (!f)
269: {
270: Com_Printf ("Failed to open %s\n", name);
271: return;
272: }
273: fwrite (sv.configstrings, sizeof(sv.configstrings), 1, f);
274: CM_WritePortalState (f);
275: fclose (f);
276:
277: Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name);
278: ge->WriteLevel (name);
279: }
280:
281: /*
282: ==============
283: SV_ReadLevelFile
284:
285: ==============
286: */
287: void SV_ReadLevelFile (void)
288: {
289: char name[MAX_OSPATH];
290: FILE *f;
291:
1.1.1.2 ! root 292: Com_DPrintf("SV_ReadLevelFile()\n");
! 293:
1.1 root 294: Com_sprintf (name, sizeof(name), "%s/save/current/%s.sv2", FS_Gamedir(), sv.name);
295: f = fopen(name, "rb");
296: if (!f)
297: {
298: Com_Printf ("Failed to open %s\n", name);
299: return;
300: }
301: FS_Read (sv.configstrings, sizeof(sv.configstrings), f);
302: CM_ReadPortalState (f);
303: fclose (f);
304:
305: Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name);
306: ge->ReadLevel (name);
307: }
308:
309: /*
310: ==============
311: SV_WriteServerFile
312:
313: ==============
314: */
315: void SV_WriteServerFile (qboolean autosave)
316: {
317: FILE *f;
318: cvar_t *var;
319: char name[MAX_OSPATH], string[128];
320: char comment[32];
321: time_t aclock;
322: struct tm *newtime;
323:
1.1.1.2 ! root 324: Com_DPrintf("SV_WriteServerFile(%s)\n", autosave ? "true" : "false");
! 325:
1.1 root 326: Com_sprintf (name, sizeof(name), "%s/save/current/server.ssv", FS_Gamedir());
327: f = fopen (name, "wb");
328: if (!f)
329: {
330: Com_Printf ("Couldn't write %s\n", name);
331: return;
332: }
333: // write the comment field
334: memset (comment, 0, sizeof(comment));
335:
336: if (!autosave)
337: {
338: time (&aclock);
339: newtime = localtime (&aclock);
340: Com_sprintf (comment,sizeof(comment), "%2i:%i%i %2i/%2i ", newtime->tm_hour
341: , newtime->tm_min/10, newtime->tm_min%10,
342: newtime->tm_mon+1, newtime->tm_mday);
343: strncat (comment, sv.configstrings[CS_NAME], sizeof(comment)-1-strlen(comment) );
344: }
345: else
346: { // autosaved
347: Com_sprintf (comment, sizeof(comment), "ENTERING %s", sv.configstrings[CS_NAME]);
348: }
349:
350: fwrite (comment, 1, sizeof(comment), f);
351:
352: // write the mapcmd
353: fwrite (svs.mapcmd, 1, sizeof(svs.mapcmd), f);
354:
355: // write all CVAR_LATCH cvars
356: // these will be things like coop, skill, deathmatch, etc
357: for (var = cvar_vars ; var ; var=var->next)
358: {
359: if (!(var->flags & CVAR_LATCH))
360: continue;
361: if (strlen(var->name) >= sizeof(name)-1
362: || strlen(var->string) >= sizeof(string)-1)
363: {
364: Com_Printf ("Cvar too long: %s = %s\n", var->name, var->string);
365: continue;
366: }
367: memset (name, 0, sizeof(name));
368: memset (string, 0, sizeof(string));
369: strcpy (name, var->name);
370: strcpy (string, var->string);
371: fwrite (name, 1, sizeof(name), f);
372: fwrite (string, 1, sizeof(string), f);
373: }
374:
375: fclose (f);
376:
377: // write game state
378: Com_sprintf (name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir());
379: ge->WriteGame (name, autosave);
380: }
381:
382: /*
383: ==============
384: SV_ReadServerFile
385:
386: ==============
387: */
388: void SV_ReadServerFile (void)
389: {
390: FILE *f;
391: char name[MAX_OSPATH], string[128];
392: char comment[32];
393: char mapcmd[MAX_TOKEN_CHARS];
394:
1.1.1.2 ! root 395: Com_DPrintf("SV_ReadServerFile()\n");
! 396:
1.1 root 397: Com_sprintf (name, sizeof(name), "%s/save/current/server.ssv", FS_Gamedir());
398: f = fopen (name, "rb");
399: if (!f)
400: {
401: Com_Printf ("Couldn't read %s\n", name);
402: return;
403: }
404: // read the comment field
405: FS_Read (comment, sizeof(comment), f);
406:
407: // read the mapcmd
408: FS_Read (mapcmd, sizeof(mapcmd), f);
409:
410: // read all CVAR_LATCH cvars
411: // these will be things like coop, skill, deathmatch, etc
412: while (1)
413: {
414: if (!fread (name, 1, sizeof(name), f))
415: break;
416: FS_Read (string, sizeof(string), f);
417: Com_DPrintf ("Set %s = %s\n", name, string);
418: Cvar_ForceSet (name, string);
419: }
420:
421: fclose (f);
422:
423: // start a new game fresh with new cvars
424: SV_InitGame ();
425:
426: strcpy (svs.mapcmd, mapcmd);
427:
428: // read game state
429: Com_sprintf (name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir());
430: ge->ReadGame (name);
431: }
432:
433:
434: //=========================================================
435:
436:
437:
438:
439: /*
440: ==================
441: SV_DemoMap_f
442:
443: Puts the server in demo mode on a specific map/cinematic
444: ==================
445: */
446: void SV_DemoMap_f (void)
447: {
448: SV_Map (true, Cmd_Argv(1), false );
449: }
450:
451: /*
452: ==================
453: SV_GameMap_f
454:
455: Saves the state of the map just being exited and goes to a new map.
456:
457: If the initial character of the map string is '*', the next map is
458: in a new unit, so the current savegame directory is cleared of
459: map files.
460:
461: Example:
462:
463: *inter.cin+jail
464:
465: Clears the archived maps, plays the inter.cin cinematic, then
466: goes to map jail.bsp.
467: ==================
468: */
469: void SV_GameMap_f (void)
470: {
471: char *map;
472: int i;
473: client_t *cl;
474: qboolean *savedInuse;
475:
476: if (Cmd_Argc() != 2)
477: {
478: Com_Printf ("USAGE: gamemap <map>\n");
479: return;
480: }
481:
1.1.1.2 ! root 482: Com_DPrintf("SV_GameMap(%s)\n", Cmd_Argv(1));
! 483:
1.1 root 484: FS_CreatePath (va("%s/save/current/", FS_Gamedir()));
485:
486: // check for clearing the current savegame
487: map = Cmd_Argv(1);
488: if (map[0] == '*')
489: {
490: // wipe all the *.sav files
491: SV_WipeSavegame ("current");
492: }
493: else
494: { // save the map just exited
495: if (sv.state == ss_game)
496: {
497: // clear all the client inuse flags before saving so that
498: // when the level is re-entered, the clients will spawn
499: // at spawn points instead of occupying body shells
500: savedInuse = malloc(maxclients->value * sizeof(qboolean));
501: for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
502: {
503: savedInuse[i] = cl->edict->inuse;
504: cl->edict->inuse = false;
505: }
506:
507: SV_WriteLevelFile ();
508:
509: // we must restore these for clients to transfer over correctly
510: for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
511: cl->edict->inuse = savedInuse[i];
512: free (savedInuse);
513: }
514: }
515:
516: // start up the next map
517: SV_Map (false, Cmd_Argv(1), false );
518:
519: // archive server state
520: strncpy (svs.mapcmd, Cmd_Argv(1), sizeof(svs.mapcmd)-1);
521:
522: // copy off the level to the autosave slot
523: if (!dedicated->value)
524: {
525: SV_WriteServerFile (true);
526: SV_CopySaveGame ("current", "save0");
527: }
528: }
529:
530: /*
531: ==================
532: SV_Map_f
533:
534: Goes directly to a given map without any savegame archiving.
535: For development work
536: ==================
537: */
538: void SV_Map_f (void)
539: {
540: char *map;
541: char expanded[MAX_QPATH];
542:
543: // if not a pcx, demo, or cinematic, check to make sure the level exists
544: map = Cmd_Argv(1);
545: if (!strstr (map, "."))
546: {
547: Com_sprintf (expanded, sizeof(expanded), "maps/%s.bsp", map);
548: if (FS_LoadFile (expanded, NULL) == -1)
549: {
550: Com_Printf ("Can't find %s\n", expanded);
551: return;
552: }
553: }
554:
555: sv.state = ss_dead; // don't save current level when changing
556: SV_WipeSavegame("current");
557: SV_GameMap_f ();
558: }
559:
560: /*
561: =====================================================================
562:
563: SAVEGAMES
564:
565: =====================================================================
566: */
567:
568:
569: /*
570: ==============
571: SV_Loadgame_f
572:
573: ==============
574: */
575: void SV_Loadgame_f (void)
576: {
577: char name[MAX_OSPATH];
578: FILE *f;
579: char *dir;
580:
581: if (Cmd_Argc() != 2)
582: {
583: Com_Printf ("USAGE: loadgame <directory>\n");
584: return;
585: }
586:
587: Com_Printf ("Loading game...\n");
588:
589: dir = Cmd_Argv(1);
590: if (strstr (dir, "..") || strstr (dir, "/") || strstr (dir, "\\") )
591: {
592: Com_Printf ("Bad savedir.\n");
593: }
594:
595: // make sure the server.ssv file exists
596: Com_sprintf (name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir(), Cmd_Argv(1));
597: f = fopen (name, "rb");
598: if (!f)
599: {
600: Com_Printf ("No such savegame: %s\n", name);
601: return;
602: }
603: fclose (f);
604:
605: SV_CopySaveGame (Cmd_Argv(1), "current");
606:
607: SV_ReadServerFile ();
608:
609: // go to the map
610: sv.state = ss_dead; // don't save current level when changing
611: SV_Map (false, svs.mapcmd, true);
612: }
613:
614:
615:
616: /*
617: ==============
618: SV_Savegame_f
619:
620: ==============
621: */
622: void SV_Savegame_f (void)
623: {
624: char *dir;
625:
626: if (sv.state != ss_game)
627: {
628: Com_Printf ("You must be in a game to save.\n");
629: return;
630: }
631:
632: if (Cmd_Argc() != 2)
633: {
634: Com_Printf ("USAGE: savegame <directory>\n");
635: return;
636: }
637:
638: if (Cvar_VariableValue("deathmatch"))
639: {
640: Com_Printf ("Can't savegame in a deathmatch\n");
641: return;
642: }
643:
644: if (!strcmp (Cmd_Argv(1), "current"))
645: {
646: Com_Printf ("Can't save to 'current'\n");
647: return;
648: }
649:
650: if (maxclients->value == 1 && svs.clients[0].edict->client->ps.stats[STAT_HEALTH] <= 0)
651: {
652: Com_Printf ("\nCan't savegame while dead!\n");
653: return;
654: }
655:
656: dir = Cmd_Argv(1);
657: if (strstr (dir, "..") || strstr (dir, "/") || strstr (dir, "\\") )
658: {
659: Com_Printf ("Bad savedir.\n");
660: }
661:
662: Com_Printf ("Saving game...\n");
663:
664: // archive current level, including all client edicts.
665: // when the level is reloaded, they will be shells awaiting
666: // a connecting client
667: SV_WriteLevelFile ();
668:
669: // save server state
670: SV_WriteServerFile (false);
671:
672: // copy it off
673: SV_CopySaveGame ("current", dir);
674:
675: Com_Printf ("Done.\n");
676: }
677:
678: //===============================================================
679:
680: /*
681: ==================
682: SV_Kick_f
683:
684: Kick a user off of the server
685: ==================
686: */
687: void SV_Kick_f (void)
688: {
689: if (!svs.initialized)
690: {
691: Com_Printf ("No server running.\n");
692: return;
693: }
694:
695: if (Cmd_Argc() != 2)
696: {
697: Com_Printf ("Usage: kick <userid>\n");
698: return;
699: }
700:
701: if (!SV_SetPlayer ())
702: return;
703:
704: SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", sv_client->name);
705: // print directly, because the dropped client won't get the
706: // SV_BroadcastPrintf message
707: SV_ClientPrintf (sv_client, PRINT_HIGH, "You were kicked from the game\n");
708: SV_DropClient (sv_client);
709: sv_client->lastmessage = svs.realtime; // min case there is a funny zombie
710: }
711:
712:
713: /*
714: ================
715: SV_Status_f
716: ================
717: */
718: void SV_Status_f (void)
719: {
720: int i, j, l;
721: client_t *cl;
722: char *s;
723: int ping;
724: if (!svs.clients)
725: {
726: Com_Printf ("No server running.\n");
727: return;
728: }
729: Com_Printf ("map : %s\n", sv.name);
730:
731: Com_Printf ("num score ping name lastmsg address qport \n");
732: Com_Printf ("--- ----- ---- --------------- ------- --------------------- ------\n");
733: for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
734: {
735: if (!cl->state)
736: continue;
737: Com_Printf ("%3i ", i);
738: Com_Printf ("%5i ", cl->edict->client->ps.stats[STAT_FRAGS]);
739:
740: if (cl->state == cs_connected)
741: Com_Printf ("CNCT ");
742: else if (cl->state == cs_zombie)
743: Com_Printf ("ZMBI ");
744: else
745: {
746: ping = cl->ping < 9999 ? cl->ping : 9999;
747: Com_Printf ("%4i ", ping);
748: }
749:
750: Com_Printf ("%s", cl->name);
751: l = 16 - strlen(cl->name);
752: for (j=0 ; j<l ; j++)
753: Com_Printf (" ");
754:
755: Com_Printf ("%7i ", svs.realtime - cl->lastmessage );
756:
757: s = NET_AdrToString ( cl->netchan.remote_address);
758: Com_Printf ("%s", s);
759: l = 22 - strlen(s);
760: for (j=0 ; j<l ; j++)
761: Com_Printf (" ");
762:
763: Com_Printf ("%5i", cl->netchan.qport);
764:
765: Com_Printf ("\n");
766: }
767: Com_Printf ("\n");
768: }
769:
770: /*
771: ==================
772: SV_ConSay_f
773: ==================
774: */
775: void SV_ConSay_f(void)
776: {
777: client_t *client;
778: int j;
779: char *p;
780: char text[1024];
781:
782: if (Cmd_Argc () < 2)
783: return;
784:
785: strcpy (text, "console: ");
786: p = Cmd_Args();
787:
788: if (*p == '"')
789: {
790: p++;
791: p[strlen(p)-1] = 0;
792: }
793:
794: strcat(text, p);
795:
796: for (j = 0, client = svs.clients; j < maxclients->value; j++, client++)
797: {
798: if (client->state != cs_spawned)
799: continue;
800: SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text);
801: }
802: }
803:
804:
805: /*
806: ==================
807: SV_Heartbeat_f
808: ==================
809: */
810: void SV_Heartbeat_f (void)
811: {
812: svs.last_heartbeat = -9999999;
813: }
814:
815:
816: /*
817: ===========
818: SV_Serverinfo_f
819:
820: Examine or change the serverinfo string
821: ===========
822: */
823: void SV_Serverinfo_f (void)
824: {
825: Com_Printf ("Server info settings:\n");
826: Info_Print (Cvar_Serverinfo());
827: }
828:
829:
830: /*
831: ===========
832: SV_DumpUser_f
833:
834: Examine all a users info strings
835: ===========
836: */
837: void SV_DumpUser_f (void)
838: {
839: if (Cmd_Argc() != 2)
840: {
841: Com_Printf ("Usage: info <userid>\n");
842: return;
843: }
844:
845: if (!SV_SetPlayer ())
846: return;
847:
848: Com_Printf ("userinfo\n");
849: Com_Printf ("--------\n");
850: Info_Print (sv_client->userinfo);
851:
852: }
853:
854:
855: /*
856: ==============
857: SV_ServerRecord_f
858:
859: Begins server demo recording. Every entity and every message will be
860: recorded, but no playerinfo will be stored. Primarily for demo merging.
861: ==============
862: */
863: void SV_ServerRecord_f (void)
864: {
865: char name[MAX_OSPATH];
1.1.1.2 ! root 866: char buf_data[32768];
1.1 root 867: sizebuf_t buf;
868: int len;
869: int i;
870:
871: if (Cmd_Argc() != 2)
872: {
873: Com_Printf ("serverrecord <demoname>\n");
874: return;
875: }
876:
877: if (svs.demofile)
878: {
879: Com_Printf ("Already recording.\n");
880: return;
881: }
882:
883: if (sv.state != ss_game)
884: {
885: Com_Printf ("You must be in a level to record.\n");
886: return;
887: }
888:
889: //
890: // open the demo file
891: //
892: Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
893:
894: Com_Printf ("recording to %s.\n", name);
895: FS_CreatePath (name);
896: svs.demofile = fopen (name, "wb");
897: if (!svs.demofile)
898: {
899: Com_Printf ("ERROR: couldn't open.\n");
900: return;
901: }
902:
903: // setup a buffer to catch all multicasts
904: SZ_Init (&svs.demo_multicast, svs.demo_multicast_buf, sizeof(svs.demo_multicast_buf));
905:
906: //
907: // write a single giant fake message with all the startup info
908: //
909: SZ_Init (&buf, buf_data, sizeof(buf_data));
910:
1.1.1.2 ! root 911: //
! 912: // serverdata needs to go over for all types of servers
! 913: // to make sure the protocol is right, and to set the gamedir
! 914: //
! 915: // send the serverdata
! 916: MSG_WriteByte (&buf, svc_serverdata);
! 917: MSG_WriteLong (&buf, PROTOCOL_VERSION);
! 918: MSG_WriteLong (&buf, svs.spawncount);
! 919: // 2 means server demo
! 920: MSG_WriteByte (&buf, 2); // demos are always attract loops
! 921: MSG_WriteString (&buf, Cvar_VariableString ("gamedir"));
! 922: MSG_WriteShort (&buf, -1);
! 923: // send full levelname
! 924: MSG_WriteString (&buf, sv.configstrings[CS_NAME]);
! 925:
1.1 root 926: for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
927: if (sv.configstrings[i][0])
928: {
929: MSG_WriteByte (&buf, svc_configstring);
930: MSG_WriteShort (&buf, i);
931: MSG_WriteString (&buf, sv.configstrings[i]);
932: }
933:
934: // write it to the demo file
935: Com_DPrintf ("signon message length: %i\n", buf.cursize);
936: len = LittleLong (buf.cursize);
937: fwrite (&len, 4, 1, svs.demofile);
938: fwrite (buf.data, buf.cursize, 1, svs.demofile);
939:
940: // the rest of the demo file will be individual frames
941: }
942:
943:
944: /*
945: ==============
946: SV_ServerStop_f
947:
948: Ends server demo recording
949: ==============
950: */
951: void SV_ServerStop_f (void)
952: {
953: if (!svs.demofile)
954: {
955: Com_Printf ("Not doing a serverrecord.\n");
956: return;
957: }
958: fclose (svs.demofile);
959: svs.demofile = NULL;
960: Com_Printf ("Recording completed.\n");
961: }
962:
963:
964: /*
965: ===============
966: SV_KillServer_f
967:
968: Kick everyone off, possibly in preparation for a new game
969:
970: ===============
971: */
972: void SV_KillServer_f (void)
973: {
974: if (!svs.initialized)
975: return;
976: SV_Shutdown ("Server was killed.\n", false);
977: NET_Config ( false ); // close network sockets
978: }
979:
980: /*
981: ===============
982: SV_ServerCommand_f
983:
984: Let the game dll handle a command
985: ===============
986: */
987: void SV_ServerCommand_f (void)
988: {
989: if (!ge)
990: {
991: Com_Printf ("No game loaded.\n");
992: return;
993: }
994:
995: ge->ServerCommand();
996: }
997:
998: //===========================================================
999:
1000: /*
1001: ==================
1002: SV_InitOperatorCommands
1003: ==================
1004: */
1005: void SV_InitOperatorCommands (void)
1006: {
1007: Cmd_AddCommand ("heartbeat", SV_Heartbeat_f);
1008: Cmd_AddCommand ("kick", SV_Kick_f);
1009: Cmd_AddCommand ("status", SV_Status_f);
1010: Cmd_AddCommand ("serverinfo", SV_Serverinfo_f);
1011: Cmd_AddCommand ("dumpuser", SV_DumpUser_f);
1012:
1013: Cmd_AddCommand ("map", SV_Map_f);
1014: Cmd_AddCommand ("demomap", SV_DemoMap_f);
1015: Cmd_AddCommand ("gamemap", SV_GameMap_f);
1016: Cmd_AddCommand ("setmaster", SV_SetMaster_f);
1017:
1018: if ( dedicated->value )
1019: Cmd_AddCommand ("say", SV_ConSay_f);
1020:
1021: Cmd_AddCommand ("serverrecord", SV_ServerRecord_f);
1022: Cmd_AddCommand ("serverstop", SV_ServerStop_f);
1023:
1024: Cmd_AddCommand ("save", SV_Savegame_f);
1025: Cmd_AddCommand ("load", SV_Loadgame_f);
1026:
1027: Cmd_AddCommand ("killserver", SV_KillServer_f);
1028:
1029: Cmd_AddCommand ("sv", SV_ServerCommand_f);
1030: }
1031:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.