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