📄 sv_ccmds.pas
字号:
// 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(Round(maxclients.value) * sizeof(qboolean));
cl := client_p(svs.clients);
i := 0;
while (i < maxclients.value) do
begin
savedInuse^[i] := cl^.edict^.inuse;
cl^.edict^.inuse := false;
Inc(i);
Inc(cl);
end;
SV_WriteLevelFile();
// we must restore these for clients to transfer over correctly
cl := client_p(svs.clients);
i := 0;
while (i < maxclients.value) do
begin
cl^.edict^.inuse := savedInuse^[i];
inc(i);
inc(cl);
end;
free(savedInuse);
end;
end;
// start up the next map
SV_Map(false, Cmd_Argv(1), false);
// archive server state
strncpy(@svs.mapcmd, Cmd_Argv(1), sizeof(svs.mapcmd) - 1);
// copy off the level to the autosave slot
if not (dedicated.value <> 0) then
begin
SV_WriteServerFile(true);
SV_CopySaveGame('current', 'save0');
end;
end;
(*
==================================================
SV_Map_f
Goes directly to a given map without any savegame archiving.
For development work
==================================================
*)
procedure SV_Map_f(); cdecl;
var
map: pchar;
expanded: array[0..MAX_QPATH - 1] of char;
begin
// if not a pcx, demo, or cinematic, check to make sure the level exists
map := Cmd_Argv(1);
if (not (strstr(map, '.') <> nil)) then
begin
Com_sprintf(expanded, sizeof(expanded), 'maps/%s.bsp', [map]);
if FS_LoadFile(expanded, nil) = -1 then
begin
Com_Printf('Can''t find %s'#10, [expanded]);
exit;
end;
end;
sv.state := ss_dead; // don't save current level when changing
SV_WipeSavegame('current');
SV_GameMap_f();
end;
(*
==================================================
SAVEGAMES
==================================================
*)
(*
==================================================
SV_Loadgame_f
==================================================
*)
procedure SV_Loadgame_f(); cdecl;
var
name: array[0..MAX_OSPATH - 1] of Char;
f: Integer;
dir: PChar;
begin
if (Cmd_Argc() <> 2) then
begin
Com_Printf('USAGE: loadgame <directory>'#10, []);
exit;
end;
Com_Printf('Loading game...'#10, []);
dir := Cmd_Argv(1);
if (strstr(dir, '..') <> nil) or (strstr(dir, '/') <> nil) or (strstr(dir, '\') <> nil) then
begin
Com_Printf('Bad savedir.'#10, []);
end;
// make sure the server.ssv file exists
Com_sprintf(name, sizeof(name), '%s/save/%s/server.ssv', [FS_Gamedir(), Cmd_Argv(1)]);
f := FileOpen(name, fmOpenRead);
if (f = -1) then
begin
Com_Printf('No such savegame: %s'#10, [name]);
exit;
end;
FileClose(f);
SV_CopySaveGame(Cmd_Argv(1), 'current');
SV_ReadServerFile();
// go to the map
sv.state := ss_dead; // don't save current level when changing
SV_Map(false, @svs.mapcmd, true);
end;
(*
==================================================
SV_Savegame_f
==================================================
*)
procedure SV_Savegame_f(); cdecl;
var
dir: pchar;
begin
if (sv.state <> ss_game) then
begin
Com_Printf('You must be in a game to save.'#10, []);
exit;
end;
if (Cmd_Argc() <> 2) then
begin
Com_Printf('USAGE: savegame <directory>'#10, []);
exit;
end;
if (Cvar_VariableValue('deathmatch') <> 0) then
begin
Com_Printf('Can''t savegame in a deathmatch'#10, []);
exit;
end;
if not (strcmp(Cmd_Argv(1), 'current') <> 0) then
begin
Com_Printf('Can''t save to ''current'''#10, []);
exit;
end;
if (maxclients.value = 1) and (svs.clients^[0].edict^.client^.ps.stats[STAT_HEALTH] <= 0) then
begin
Com_Printf(#10'Can''t savegame while dead!'#10, []);
exit;
end;
dir := Cmd_Argv(1);
if (strstr(dir, '..') <> nil) or (strstr(dir, '/') <> nil) or (strstr(dir, '\') <> nil) then
begin
Com_Printf('Bad savedir.'#10, []);
end;
Com_Printf('Saving game...'#10, []);
// archive current level, including all client edicts.
// when the level is reloaded, they will be shells awaiting
// a connecting client
SV_WriteLevelFile();
// save server state
SV_WriteServerFile(false);
// copy it off
SV_CopySaveGame('current', dir);
Com_Printf('Done.'#10, []);
end;
(*
==================================================
SV_Kick_f
Kick a user off of the server
==================================================
*)
procedure SV_Kick_f(); cdecl;
begin
if (not svs.initialized) then
begin
Com_Printf('No server running.'#10, []);
exit;
end;
if (Cmd_Argc() <> 2) then
begin
Com_Printf('Usage: kick <userid>'#10, []);
exit;
end;
if (not SV_SetPlayer()) then
exit;
SV_BroadcastPrintf(PRINT_HIGH, '%s was kicked'#10, [sv_client^.name]);
// print directly, because the dropped client won't get the
// SV_BroadcastPrintf message
SV_ClientPrintf(sv_client, PRINT_HIGH, 'You were kicked from the game'#10, []);
SV_DropClient(sv_client);
sv_client^.lastmessage := svs.realtime; // min case there is a funny zombie
end;
(*
==================================================
SV_Status_f
==================================================
*)
procedure SV_Status_f; cdecl;
var
i, j, l: integer;
cl: client_p;
s: pchar;
ping: integer;
label
continue_;
begin
if (svs.clients = nil) then
begin
Com_Printf('No server running.'#10, []);
exit;
end;
Com_Printf('map : %s'#10, [sv.name]);
Com_Printf('num score ping name lastmsg address qport '#10, []);
Com_Printf('--- ----- ---- --------------- ------- --------------------- ------'#10, []);
i := 0;
cl := client_p(svs.clients);
while (i < maxclients.value) do
begin
if (Integer(cl.state) = 0) then
goto continue_;
Com_Printf('%3i ', [i]);
Com_Printf('%5i ', [cl^.edict^.client^.ps.stats[STAT_FRAGS]]);
if (cl^.state = cs_connected) then
Com_Printf('CNCT ', [])
else if (cl^.state = cs_zombie) then
Com_Printf('ZMBI ', [])
else
begin
if cl^.ping < 9999 then
ping := cl^.ping
else
ping := 9999;
Com_Printf('%4i ', [ping]);
end;
Com_Printf('%s', [cl^.name]);
l := 16 - strlen(cl.name);
for j := 0 to l - 1 do
Com_Printf(' ');
Com_Printf('%7i ', [svs.realtime - cl.lastmessage]);
s := NET_AdrToString(cl.netchan.remote_address);
Com_Printf('%s', [s]);
l := 22 - strlen(s);
for j := 0 to l - 1 do
Com_Printf(' ');
Com_Printf('%5i', [cl.netchan.qport]);
Com_Printf(#10);
continue_:
Inc(i);
Inc(cl);
end;
Com_Printf(#10);
end;
(*
==================================================
SV_ConSay_f
==================================================
*)
procedure SV_ConSay_f; cdecl;
var
client: client_p;
j: integer;
p: pchar;
text: array[0..1024 - 1] of char;
label
continue_;
begin
if (Cmd_Argc() < 2) then
exit;
strcpy(text, 'console: ');
p := Cmd_Args();
if (p^ = '"') then
begin
Inc(p);
p[strlen(p) - 1] := #0;
end;
strcat(text, p);
j := 0;
client := client_p(svs.clients);
while (j < maxclients.value) do
begin
if (client^.state <> cs_spawned) then
goto continue_;
SV_ClientPrintf(client, PRINT_CHAT, '%s'#10, [text]);
continue_:
Inc(j);
Inc(client);
end;
end;
(*
==================================================
SV_Heartbeat_f
==================================================
*)
procedure SV_Heartbeat_f; cdecl;
begin
svs.last_heartbeat := -9999999;
end;
(*
==================================================
SV_Serverinfo_f
Examine or change the serverinfo string
==================================================
*)
procedure SV_Serverinfo_f; cdecl;
begin
Com_Printf('Server info settings:'#10);
Info_Print(Cvar_Serverinfo_());
end;
(*
==================================================
SV_DumpUser_f
Examine all a users info strings
==================================================
*)
procedure SV_DumpUser_f; cdecl;
begin
if (Cmd_Argc() <> 2) then
begin
Com_Printf('Usage: info <userid>'#10);
exit;
end;
if (not SV_SetPlayer()) then
exit;
Com_Printf('userinfo'#10);
Com_Printf('--------'#10);
Info_Print(sv_client.userinfo);
end;
(*
==================================================
SV_ServerRecord_f
Begins server demo recording. Every entity and every message will be
recorded, but no playerinfo will be stored. Primarily for demo merging.
==================================================
*)
procedure SV_ServerRecord_f; cdecl;
var
name: array[0..MAX_OSPATH - 1] of char;
buf_data: array[0..32768 - 1] of char;
buf: sizebuf_t;
len: integer;
i: integer;
begin
if (Cmd_Argc() <> 2) then
begin
Com_Printf('serverrecord <demoname>'#10);
exit;
end;
if (svs.demofile > 0) then
begin
Com_Printf('Already recording.'#10);
exit;
end;
if (sv.state <> ss_game) then
begin
Com_Printf('You must be in a level to record.'#10);
exit;
end;
//
// open the demo file
//
Com_sprintf(name, sizeof(name), '%s/demos/%s.dm2', [FS_Gamedir(), Cmd_Argv(1)]);
Com_Printf('recording to %s.'#10, [name]);
FS_CreatePath(name);
svs.demofile := FileOpen(name, fmOpenReadWrite);
if (svs.demofile = -1) then
begin
Com_Printf('ERROR: couldn''t open.'#10);
exit;
end;
// setup a buffer to catch all multicasts
SZ_Init(svs.demo_multicast, @svs.demo_multicast_buf, sizeof(svs.demo_multicast_buf));
//
// write a single giant fake message with all the startup info
//
SZ_Init(buf, @buf_data, sizeof(buf_data));
//
// serverdata needs to go over for all types of servers
// to make sure the protocol is right, and to set the gamedir
//
// send the serverdata
MSG_WriteByte(buf, Integer(svc_serverdata));
MSG_WriteLong(buf, PROTOCOL_VERSION);
MSG_WriteLong(buf, svs.spawncount);
// 2 means server demo
MSG_WriteByte(buf, 2); // demos are always attract loops
MSG_WriteString(buf, Cvar_VariableString('gamedir'));
MSG_WriteShort(buf, -1);
// send full levelname
MSG_WriteString(buf, sv.configstrings[CS_NAME]);
for i := 0 to MAX_CONFIGSTRINGS - 1 do
begin
if (sv.configstrings[i, 0] <> #0) then
begin
MSG_WriteByte(buf, Integer(svc_configstring));
MSG_WriteShort(buf, i);
MSG_WriteString(buf, sv.configstrings[i]);
end;
end;
// write it to the demo file
Com_DPrintf('signon message length: %i'#10, [buf.cursize]);
len := LittleLong(buf.cursize);
FileWrite(svs.demofile, len, 4);
FileWrite(svs.demofile, buf.data^, buf.cursize);
// the rest of the demo file will be individual frames
end;
(*
==================================================
SV_ServerStop_f
Ends server demo recording
==================================================
*)
procedure SV_ServerStop_f; cdecl;
begin
if (svs.demofile <= 0) then
begin
Com_Printf('Not doing a serverrecord.'#10);
exit;
end;
FileClose(svs.demofile);
svs.demofile := 0;
Com_Printf('Recording completed.'#10);
end;
(*
==================================================
SV_KillServer_f
Kick everyone off, possibly in preparation for a new game
==================================================*)
procedure SV_KillServer_f; cdecl;
begin
if (not svs.initialized) then
exit;
SV_Shutdown('Server was killed.'#10, false);
NET_Config(false); // close network sockets
end;
(*
==================================================
SV_ServerCommand_f
Let the game dll handle a command
==================================================
*)
procedure SV_ServerCommand_f(); cdecl;
begin
if (ge = nil) then
begin
Com_Printf('No game loaded.'#10);
exit;
end;
ge.ServerCommand();
end;
(*
==================================================
SV_InitOperatorCommands
==================================================
*)
procedure SV_InitOperatorCommands();
begin
Cmd_AddCommand('heartbeat', SV_Heartbeat_f);
Cmd_AddCommand('kick', SV_Kick_f);
Cmd_AddCommand('status', SV_Status_f);
Cmd_AddCommand('serverinfo', SV_Serverinfo_f);
Cmd_AddCommand('dumpuser', SV_DumpUser_f);
Cmd_AddCommand('map', SV_Map_f);
Cmd_AddCommand('demomap', SV_DemoMap_f);
Cmd_AddCommand('gamemap', SV_GameMap_f);
Cmd_AddCommand('setmaster', SV_SetMaster_f);
if (dedicated.value <> 0) then
Cmd_AddCommand('say', SV_ConSay_f);
Cmd_AddCommand('serverrecord', SV_ServerRecord_f);
Cmd_AddCommand('serverstop', SV_ServerStop_f);
Cmd_AddCommand('save', SV_Savegame_f);
Cmd_AddCommand('load', SV_Loadgame_f);
Cmd_AddCommand('killserver', SV_KillServer_f);
Cmd_AddCommand('sv', SV_ServerCommand_f);
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -