📄 cmd.pas
字号:
{----------------------------------------------------------------------------}
{ }
{ File(s): qcommon.h (part), cmd.c }
{ Content: Quake2\QCommon\ }
{ }
{ Initial conversion by : D-12 (Thomas.lavergne) - d-12@laposte.net }
{ Initial conversion on : -Jan-2002 }
{ }
{ This File contains part of convertion of Quake2 source to ObjectPascal. }
{ More information about this project can be found at: }
{ http://www.sulaco.co.za/quake2/ }
{ }
{ 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. }
{ }
{----------------------------------------------------------------------------}
{ Updated on : 03-jun-2002 }
{ Updated by : Juha Hartikainen (juha@linearteam.org) }
{ - Removed NODEPEND hack. }
{ - Fixed couple places to let this compile right. }
{----------------------------------------------------------------------------}
{ * Still dependent (to compile correctly) on: }
{ - cl_main.pas }
{----------------------------------------------------------------------------}
{ * TODO: }
{ }
{----------------------------------------------------------------------------}
unit cmd;
interface
uses
q_shared, common, cl_main, SysUtils,
CPas;
// From Quake2\QCommon\qcommon.h
(*
==============================================================
CMD
Command text buffering and command execution
==============================================================
*)
(*
Any number of commands can be added in a frame, from several different sources.
Most commands come from either keybindings or console line input, but remote
servers can also send across commands and entire text files can be execed.
The + command line options are also added to the command buffer.
The game starts with a Cbuf_AddText ('exec quake.rc'#10); Cbuf_Execute ();
*)
const
EXEC_NOW = 0; // don't return until completed
EXEC_INSERT = 1; // insert at current position, but don't run yet
EXEC_APPEND = 2; // add to end of the command buffer
procedure Cbuf_Init; cdecl;
// allocates an initial text buffer that will grow as needed
procedure Cbuf_AddText(text_: PChar); cdecl;
// as new commands are generated from the console or keybindings,
// the text is added to the end of the command buffer.
procedure Cbuf_InsertText(text_: PChar); cdecl;
// when a command wants to issue other commands immediately, the text is
// inserted at the beginning of the buffer, before any remaining unexecuted
// commands.
procedure Cbuf_ExecuteText(exec_when: Integer; text_: PChar); cdecl;
// this can be used in place of either Cbuf_AddText or Cbuf_InsertText
procedure Cbuf_AddEarlyCommands(clear: qboolean); cdecl;
// adds all the +set commands from the command line
function Cbuf_AddLateCommands: qboolean; cdecl;
// adds all the remaining + commands from the command line
// Returns true if any late commands were added, which
// will keep the demoloop from immediately starting
procedure Cbuf_Execute; cdecl;
// Pulls off \n terminated lines of text from the command buffer and sends
// them through Cmd_ExecuteString. Stops when the buffer is empty.
// Normally called once per frame, but may be explicitly invoked.
// Do not call inside a command function!
procedure Cbuf_CopyToDefer; cdecl;
procedure Cbuf_InsertFromDefer; cdecl;
// These two functions are used to defer any pending commands while a map
// is being loaded
//===========================================================================
(*
Command execution takes a null terminated string, breaks it into tokens,
then searches for a command or variable that matches the first token.
*)
procedure Cmd_Init; cdecl;
procedure Cmd_AddCommand(cmd_name: PChar; function_: tcdeclproc); cdecl;
// called by the init functions of other parts of the program to
// register commands and functions to call for them.
// The cmd_name is referenced later, so it should not be in temp memory
// if function is NULL, the command will be forwarded to the server
// as a clc_stringcmd instead of executed locally
procedure Cmd_RemoveCommand(cmd_name: PChar); cdecl;
function Cmd_Exists(cmd_name: PChar): qboolean; cdecl;
// used by the cvar code to check for cvar / command name overlap
function Cmd_CompleteCommand(partial: PChar): PChar; cdecl;
// attempts to match a partial command for automatic command line completion
// returns NULL if nothing fits
function Cmd_Argc: Integer; cdecl;
function Cmd_Argv(arg: Integer): PChar; cdecl;
function Cmd_Args: PChar; cdecl;
// The functions that execute commands get their parameters with these
// functions. Cmd_Argv () will return an empty string, not a NULL
// if arg > argc, so string operations are always safe.
procedure Cmd_TokenizeString(text_: PChar; macroExpand: qboolean); cdecl;
// Takes a null terminated string. Does not need to be /n terminated.
// breaks the string up into arg tokens.
procedure Cmd_ExecuteString(text_: PChar);cdecl;
// Parses a single line of text into arguments and tries to execute it
// as if it was typed at the console
//procedure Cmd_ForwardToServer;
// adds the current command line as a clc_stringcmd to the client message.
// things like godmode, noclip, etc, are commands directed to the server,
// so when they are typed in at the console, they will need to be forwarded.
implementation
uses
cvar, files;
// cmd.c -- Quake script command processing module
const
MAX_ALIAS_NAME = 32;
type
cmdalias_p = ^cmdalias_s;
cmdalias_s = packed record
next: cmdalias_p;
name: array[0..MAX_ALIAS_NAME - 1] of Char;
value: PChar;
end;
cmdalias_t = cmdalias_s;
var
cmd_alias: cmdalias_p;
cmd_wait: qboolean;
const
ALIAS_LOOP_COUNT = 16;
var
alias_count: Integer;
//=============================================================================
(*
============
Cmd_Wait_f
Causes execution of the remainder of the command buffer to be delayed until
next frame. This allows commands like:
bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
============
*)
procedure Cmd_Wait_f; cdecl;
begin
cmd_wait := True;
end;
(*
=============================================================================
COMMAND BUFFER
=============================================================================
*)
var
cmd_text: sizebuf_t;
cmd_text_buf: array[0..8191] of Char;
defer_text_buf: array[0..8191] of Char;
(*
============
Cbuf_Init
============
*)
procedure Cbuf_Init;
begin
SZ_Init(cmd_text, PByte(@cmd_text_buf[0]), SizeOf(cmd_text_buf));
end;
(*
============
Cbuf_AddText
Adds command text at the end of the buffer
============
*)
procedure Cbuf_AddText(text_: PChar);
var
l: Integer;
begin
l := StrLen(text_);
if (cmd_text.cursize + l) >= cmd_text.maxsize then
begin
Com_Printf('Cbuf_AddText: overflow'#10, []);
Exit;
end;
SZ_Write(cmd_text, text_, strlen(text_));
end;
(*
============
Cbuf_InsertText
Adds command text immediately after the current command
Adds a \n(#10) to the text
FIXME: actually change the command buffer to do less copying
============
*)
procedure Cbuf_InsertText(text_: PChar);
var
temp: PChar;
templen: Integer;
begin
// copy off any commands still remaining in the exec buffer
templen := cmd_text.cursize;
if templen <> 0 then
begin
temp := Z_Malloc(templen);
memcpy(temp, cmd_text.data, templen);
SZ_Clear(cmd_text);
end
else
temp := nil; // shut up compiler
// add the entire text of the file
Cbuf_AddText(text_);
// add the copied off data
if templen <> 0 then
begin
SZ_Write(cmd_text, temp, templen);
Z_Free(temp);
end;
end;
(*
============
Cbuf_CopyToDefer
============
*)
procedure Cbuf_CopyToDefer;
begin
memcpy(@defer_text_buf[0], @cmd_text_buf[0], cmd_text.cursize);
defer_text_buf[cmd_text.cursize] := #0;
cmd_text.cursize := 0;
end;
(*
============
Cbuf_InsertFromDefer
============
*)
procedure Cbuf_InsertFromDefer;
begin
Cbuf_InsertText(@defer_text_buf);
defer_text_buf[0] := #0;
end;
(*
============
Cbuf_ExecuteText
============
*)
procedure Cbuf_ExecuteText(exec_when: Integer; text_: PChar);
begin
case exec_when of
EXEC_NOW: Cmd_ExecuteString(text_);
EXEC_INSERT: Cbuf_InsertText(text_);
EXEC_APPEND: Cbuf_AddText(text_);
else Com_Error(ERR_FATAL, 'Cbuf_ExecuteText: bad exec_when', []);
end;
end;
(*
============
Cbuf_Execute
============
*)
procedure Cbuf_Execute;
var
i: Integer;
text_: PChar;
line: array[0..1023] of Char;
quotes: Integer;
begin
alias_count := 0; // don't allow infinite alias loops
while cmd_text.cursize <> 0 do
begin
// find a \n(#10) or ; line break
text_ := PChar(cmd_text.data);
quotes := 0;
for i := 0 to cmd_text.cursize - 1 do
begin
if (text_[i] = '"') then
Inc(quotes);
if ((not ((quotes and 1) <> 0)) and (text_[i] = ';')) then
Break;
if (text_[i] = #10) then
Break;
end;
memcpy(@line, text_, i);
line[i] := #0;
// delete the text from the command buffer and move remaining commands down
// this is necessary because commands (exec, alias) can insert data at the
// beginning of the text buffer
if (i = cmd_text.cursize) then
begin
cmd_text.cursize := 0;
end
else
begin
Inc(i);
Dec(cmd_text.cursize, i);
memmove(text_, text_ + i, cmd_text.cursize);
end;
// execute the command line
Cmd_ExecuteString(line);
if cmd_wait then
begin
//skip out while text still remains in buffer, leaving it
// for next frame
cmd_wait := false;
Break;
end;
end;
end;
(*
===============
Cbuf_AddEarlyCommands
Adds command line parameters as script statements
Commands lead with a +, and continue until another +
Set commands are added early, so they are guaranteed to be set before
the client and server initialize for the first time.
Other commands are added late, after all initialization is complete.
===============
*)
procedure Cbuf_AddEarlyCommands(clear: qboolean);
var
i: Integer;
s: PChar;
begin
i := 0;
while i < COM_Argc do
begin
s := COM_Argv(i);
if strcmp(s, '+set') <> 0 then
begin
Inc(i, 1);
Continue;
end;
Cbuf_AddText(va('set %s %s'#10, [COM_Argv(i + 1), COM_Argv(i + 2)]));
if clear then
begin
COM_ClearArgv(i);
COM_ClearArgv(i + 1);
COM_ClearArgv(i + 2);
end;
Inc(i, 3);
end;
end;
(*
=================
Cbuf_AddLateCommands
Adds command line parameters as script statements
Commands lead with a + and continue until another + or -
quake +vid_ref gl +map amlev1
Returns true if any late commands were added, which
will keep the demoloop from immediately starting
=================
*)
function Cbuf_AddLateCommands: qboolean;
var
i, j: Integer;
s: Integer;
c: Char;
text_, build: PChar;
argc: Integer;
begin
// build the combined string to parse from
s := 0;
argc := COM_Argc;
for i := 1 to argc - 1 do
Inc(s, strlen(COM_Argv(i)) + 1);
if s = 0 then
begin
Result := False;
Exit;
end;
text_ := Z_Malloc(s + 1);
text_[0] := #0;
for i := 1 to argc - 1 do
begin
StrCat(text_, COM_Argv(i));
if i <> (argc - 1) then
strcat(text_, ' ');
end;
// pull out the commands
build := Z_Malloc(s + 1);
build[0] := #0;
i := 0;
while i < s - 1 do
begin
if text_[i] = '+' then
begin
Inc(i);
j := i;
while (text_[j] <> '+') and (text_[j] <> '-') and (text_[j] <> #0) do
Inc(j);
c := text_[j];
text_[j] := #0;
strcat(build, text_ + i);
strcat(build, #10);
text_[j] := c;
i := j - 1;
end;
Inc(i);
end;
Result := build[0] <> #0;
if Result then
Cbuf_AddText(build);
Z_Free(text_);
Z_Free(build);
end;
(*
==============================================================================
SCRIPT COMMANDS
==============================================================================
*)
(*
===============
Cmd_Exec_f
===============
*)
procedure Cmd_Exec_f; cdecl;
var
f, f2: PChar;
len: Integer;
begin
if Cmd_Argc <> 2 then
begin
Com_Printf('exec <filename> : execute a script file'#10, []);
Exit;
end;
len := FS_LoadFile(Cmd_Argv(1), @f);
if f = nil then
begin
Com_Printf('couldn''t exec %s'#10, [Cmd_Argv(1)]);
Exit;
end;
Com_Printf('execing %s'#10, [Cmd_Argv(1)]);
// the file doesn't have a trailing 0, so we need to copy it off
f2 := Z_Malloc(len + 1);
memcpy(f2, f, len);
f2[len] := #0;
Cbuf_InsertText(f2);
Z_Free(f2);
FS_FreeFile(f);
end;
(*
===============
Cmd_Echo_f
Just prints the rest of the line to the console
===============
*)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -