📄 sv_ccmds.c
字号:
/*
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "server.h"
/*
===============================================================================
OPERATOR CONSOLE ONLY COMMANDS
These commands can only be entered from stdin or by a remote operator datagram
===============================================================================
*/
/*
====================
SV_SetMaster_f
Specify a list of master servers
====================
*/
void SV_SetMaster_f (void)
{
int i, slot;
// only dedicated servers send heartbeats
if (!dedicated->value)
{
Com_Printf ("Only dedicated servers use masters.\n");
return;
}
// make sure the server is listed public
Cvar_Set ("public", "1");
for (i=1 ; i<MAX_MASTERS ; i++)
memset (&master_adr[i], 0, sizeof(master_adr[i]));
slot = 1; // slot 0 will always contain the id master
for (i=1 ; i<Cmd_Argc() ; i++)
{
if (slot == MAX_MASTERS)
break;
if (!NET_StringToAdr (Cmd_Argv(i), &master_adr[i]))
{
Com_Printf ("Bad address: %s\n", Cmd_Argv(i));
continue;
}
if (master_adr[slot].port == 0)
master_adr[slot].port = BigShort (PORT_MASTER);
Com_Printf ("Master server at %s\n", NET_AdrToString (master_adr[slot]));
Com_Printf ("Sending a ping.\n");
Netchan_OutOfBandPrint (NS_SERVER, master_adr[slot], "ping");
slot++;
}
svs.last_heartbeat = -9999999;
}
/*
==================
SV_SetPlayer
Sets sv_client and sv_player to the player with idnum Cmd_Argv(1)
==================
*/
qboolean SV_SetPlayer (void)
{
client_t *cl;
int i;
int idnum;
char *s;
if (Cmd_Argc() < 2)
return false;
s = Cmd_Argv(1);
// numeric values are just slot numbers
if (s[0] >= '0' && s[0] <= '9')
{
idnum = atoi(Cmd_Argv(1));
if (idnum < 0 || idnum >= maxclients->value)
{
Com_Printf ("Bad client slot: %i\n", idnum);
return false;
}
sv_client = &svs.clients[idnum];
sv_player = sv_client->edict;
if (!sv_client->state)
{
Com_Printf ("Client %i is not active\n", idnum);
return false;
}
return true;
}
// check for a name match
for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
{
if (!cl->state)
continue;
if (!strcmp(cl->name, s))
{
sv_client = cl;
sv_player = sv_client->edict;
return true;
}
}
Com_Printf ("Userid %s is not on the server\n", s);
return false;
}
/*
===============================================================================
SAVEGAME FILES
===============================================================================
*/
/*
=====================
SV_WipeSavegame
Delete save/<XXX>/
=====================
*/
void SV_WipeSavegame (char *savename)
{
char name[MAX_OSPATH];
char *s;
Com_DPrintf("SV_WipeSaveGame(%s)\n", savename);
Com_sprintf (name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir (), savename);
remove (name);
Com_sprintf (name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir (), savename);
remove (name);
Com_sprintf (name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir (), savename);
s = Sys_FindFirst( name, 0, 0 );
while (s)
{
remove (s);
s = Sys_FindNext( 0, 0 );
}
Sys_FindClose ();
Com_sprintf (name, sizeof(name), "%s/save/%s/*.sv2", FS_Gamedir (), savename);
s = Sys_FindFirst(name, 0, 0 );
while (s)
{
remove (s);
s = Sys_FindNext( 0, 0 );
}
Sys_FindClose ();
}
/*
================
CopyFile
================
*/
void CopyFile (char *src, char *dst)
{
FILE *f1, *f2;
int l;
byte buffer[65536];
Com_DPrintf ("CopyFile (%s, %s)\n", src, dst);
f1 = fopen (src, "rb");
if (!f1)
return;
f2 = fopen (dst, "wb");
if (!f2)
{
fclose (f1);
return;
}
while (1)
{
l = fread (buffer, 1, sizeof(buffer), f1);
if (!l)
break;
fwrite (buffer, 1, l, f2);
}
fclose (f1);
fclose (f2);
}
/*
================
SV_CopySaveGame
================
*/
void SV_CopySaveGame (char *src, char *dst)
{
char name[MAX_OSPATH], name2[MAX_OSPATH];
int l, len;
char *found;
Com_DPrintf("SV_CopySaveGame(%s, %s)\n", src, dst);
SV_WipeSavegame (dst);
// copy the savegame over
Com_sprintf (name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir(), src);
Com_sprintf (name2, sizeof(name2), "%s/save/%s/server.ssv", FS_Gamedir(), dst);
FS_CreatePath (name2);
CopyFile (name, name2);
Com_sprintf (name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir(), src);
Com_sprintf (name2, sizeof(name2), "%s/save/%s/game.ssv", FS_Gamedir(), dst);
CopyFile (name, name2);
Com_sprintf (name, sizeof(name), "%s/save/%s/", FS_Gamedir(), src);
len = strlen(name);
Com_sprintf (name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir(), src);
found = Sys_FindFirst(name, 0, 0 );
while (found)
{
strcpy (name+len, found+len);
Com_sprintf (name2, sizeof(name2), "%s/save/%s/%s", FS_Gamedir(), dst, found+len);
CopyFile (name, name2);
// change sav to sv2
l = strlen(name);
strcpy (name+l-3, "sv2");
l = strlen(name2);
strcpy (name2+l-3, "sv2");
CopyFile (name, name2);
found = Sys_FindNext( 0, 0 );
}
Sys_FindClose ();
}
/*
==============
SV_WriteLevelFile
==============
*/
void SV_WriteLevelFile (void)
{
char name[MAX_OSPATH];
FILE *f;
Com_DPrintf("SV_WriteLevelFile()\n");
Com_sprintf (name, sizeof(name), "%s/save/current/%s.sv2", FS_Gamedir(), sv.name);
f = fopen(name, "wb");
if (!f)
{
Com_Printf ("Failed to open %s\n", name);
return;
}
fwrite (sv.configstrings, sizeof(sv.configstrings), 1, f);
CM_WritePortalState (f);
fclose (f);
Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name);
ge->WriteLevel (name);
}
/*
==============
SV_ReadLevelFile
==============
*/
void SV_ReadLevelFile (void)
{
char name[MAX_OSPATH];
FILE *f;
Com_DPrintf("SV_ReadLevelFile()\n");
Com_sprintf (name, sizeof(name), "%s/save/current/%s.sv2", FS_Gamedir(), sv.name);
f = fopen(name, "rb");
if (!f)
{
Com_Printf ("Failed to open %s\n", name);
return;
}
FS_Read (sv.configstrings, sizeof(sv.configstrings), f);
CM_ReadPortalState (f);
fclose (f);
Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name);
ge->ReadLevel (name);
}
/*
==============
SV_WriteServerFile
==============
*/
void SV_WriteServerFile (qboolean autosave)
{
FILE *f;
cvar_t *var;
char name[MAX_OSPATH], string[128];
char comment[32];
time_t aclock;
struct tm *newtime;
Com_DPrintf("SV_WriteServerFile(%s)\n", autosave ? "true" : "false");
Com_sprintf (name, sizeof(name), "%s/save/current/server.ssv", FS_Gamedir());
f = fopen (name, "wb");
if (!f)
{
Com_Printf ("Couldn't write %s\n", name);
return;
}
// write the comment field
memset (comment, 0, sizeof(comment));
if (!autosave)
{
time (&aclock);
newtime = localtime (&aclock);
Com_sprintf (comment,sizeof(comment), "%2i:%i%i %2i/%2i ", newtime->tm_hour
, newtime->tm_min/10, newtime->tm_min%10,
newtime->tm_mon+1, newtime->tm_mday);
strncat (comment, sv.configstrings[CS_NAME], sizeof(comment)-1-strlen(comment) );
}
else
{ // autosaved
Com_sprintf (comment, sizeof(comment), "ENTERING %s", sv.configstrings[CS_NAME]);
}
fwrite (comment, 1, sizeof(comment), f);
// write the mapcmd
fwrite (svs.mapcmd, 1, sizeof(svs.mapcmd), f);
// write all CVAR_LATCH cvars
// these will be things like coop, skill, deathmatch, etc
for (var = cvar_vars ; var ; var=var->next)
{
if (!(var->flags & CVAR_LATCH))
continue;
if (strlen(var->name) >= sizeof(name)-1
|| strlen(var->string) >= sizeof(string)-1)
{
Com_Printf ("Cvar too long: %s = %s\n", var->name, var->string);
continue;
}
memset (name, 0, sizeof(name));
memset (string, 0, sizeof(string));
strcpy (name, var->name);
strcpy (string, var->string);
fwrite (name, 1, sizeof(name), f);
fwrite (string, 1, sizeof(string), f);
}
fclose (f);
// write game state
Com_sprintf (name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir());
ge->WriteGame (name, autosave);
}
/*
==============
SV_ReadServerFile
==============
*/
void SV_ReadServerFile (void)
{
FILE *f;
char name[MAX_OSPATH], string[128];
char comment[32];
char mapcmd[MAX_TOKEN_CHARS];
Com_DPrintf("SV_ReadServerFile()\n");
Com_sprintf (name, sizeof(name), "%s/save/current/server.ssv", FS_Gamedir());
f = fopen (name, "rb");
if (!f)
{
Com_Printf ("Couldn't read %s\n", name);
return;
}
// read the comment field
FS_Read (comment, sizeof(comment), f);
// read the mapcmd
FS_Read (mapcmd, sizeof(mapcmd), f);
// read all CVAR_LATCH cvars
// these will be things like coop, skill, deathmatch, etc
while (1)
{
if (!fread (name, 1, sizeof(name), f))
break;
FS_Read (string, sizeof(string), f);
Com_DPrintf ("Set %s = %s\n", name, string);
Cvar_ForceSet (name, string);
}
fclose (f);
// start a new game fresh with new cvars
SV_InitGame ();
strcpy (svs.mapcmd, mapcmd);
// read game state
Com_sprintf (name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir());
ge->ReadGame (name);
}
//=========================================================
/*
==================
SV_DemoMap_f
Puts the server in demo mode on a specific map/cinematic
==================
*/
void SV_DemoMap_f (void)
{
SV_Map (true, Cmd_Argv(1), false );
}
/*
==================
SV_GameMap_f
Saves the state of the map just being exited and goes to a new map.
If the initial character of the map string is '*', the next map is
in a new unit, so the current savegame directory is cleared of
map files.
Example:
*inter.cin+jail
Clears the archived maps, plays the inter.cin cinematic, then
goes to map jail.bsp.
==================
*/
void SV_GameMap_f (void)
{
char *map;
int i;
client_t *cl;
qboolean *savedInuse;
if (Cmd_Argc() != 2)
{
Com_Printf ("USAGE: gamemap <map>\n");
return;
}
Com_DPrintf("SV_GameMap(%s)\n", Cmd_Argv(1));
FS_CreatePath (va("%s/save/current/", FS_Gamedir()));
// check for clearing the current savegame
map = Cmd_Argv(1);
if (map[0] == '*')
{
// wipe all the *.sav files
SV_WipeSavegame ("current");
}
else
{ // save the map just exited
if (sv.state == ss_game)
{
// clear all the client inuse flags before saving so that
// when the level is re-entered, the clients will spawn
// at spawn points instead of occupying body shells
savedInuse = malloc(maxclients->value * sizeof(qboolean));
for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
{
savedInuse[i] = cl->edict->inuse;
cl->edict->inuse = false;
}
SV_WriteLevelFile ();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -