adding_new_cmds.txt
来自「quake1 dos源代码最新版本」· 文本 代码 · 共 223 行
TXT
223 行
================================================================
Title : Tutorial: Infos about implementing new commands into the Quake engine
(including an enhanced "version" command)
Date : 2000-09-18
Filename : ADDING_NEW_CMDS.TXT
Author : Matthias "Maddes" Buecher
Email Address : maddes@go.to
Homepage : Quake Info Pool
http://www.quake-info-pool.net/
Quake Standards Group (short QSG)
http://www.quakesource.org/
Complexity : Low
================================================================
I. How to add a command
=======================
First you need to define the function you want to add and then add it to Quake's
command list. The function must be "void <functionname> (void)", you can check
for parameters with Quake's Cmd_Argc() function and access these through
Cmd_Argv(n).
Let's add a really useless function, open HOST_CMD.C and add the following
function at the top of it:
void Host_Useless_f (void)
{
int i;
Con_Printf ("You called USELESS with %i parameters\n", Cmd_Argc());
for (i=0 ; i<Cmd_Argc() ; i++)
{
Con_Printf (" Parameter %i: %s\n", i, Cmd_Argv(i));
}
}
You still can not use it as we haven't told Quake that there's a "useless"
command which calls this function.
Go to the Host_InitCommands() function and add the following line at the end:
Cmd_AddCommand ("useless", Host_Useless_f);
Compile and try it.
II. Creating new commands the right way
=======================================
You know how to add a command, but not how to create a GOOD working command.
Here are some guidelines to develop commands the right way, and we will create
an enhanced "version" command.
There are different command types:
1) client only commands
Example: playdemo
It's only important for the client side, where a human sits and wants to
watch the demo. No dedicated or active listen server is needed.
To decide between client and server side commands you have to check the
"cmd_source" variable. If it is set to "src_command" then the command was at the
console command of the executing machine, otherwise it was requested by a client
(happens on servers = singleplayer and listen/dedicated server).
Just check the command source, and if it wasn't entered at the console reject
it. This way the command can never be executed on the server through a client
request ("cmd useless").
Note this is just a safety meassure to avoid unwanted client requests, and
normally not necessary for client only commands.
void Host_Useless_f (void)
{
int i;
if (cmd_source != src_command)
return; // this a client only command, sorry
Con_Printf ("You called USELESS with %i parameters\n", Cmd_Argc());
...
}
This also works on dedicated servers, remember that SV_PLAYER is not valid.
2) server only commands (includes client "cmd" requests)
2a) ...which access the client's structure/entity
Example: god
It's only important for the server, where the settings for the client are
stored and processed.
Just check the command source the other way round :)
void Host_Useless_f (void)
{
int i;
if (cmd_source == src_command) // server only command
return;
Con_Printf ("You called USELESS with %i parameters\n", Cmd_Argc());
...
}
Have a closer look at the code. Do you see the problems?
One problem is that the client doesn't see the result of his request to the
server. The other problem is the inconvenience that clients have to type "cmd
useless" instead of just "useless".
The solution looks like this:
void Host_Useless_f (void)
{
int i;
if (cmd_source == src_command)
{
Cmd_ForwardToServer (); // client forgot to add "cmd", so forward to server
return; // this a server only command, sorry
}
SV_ClientPrintf ("You called USELESS with %i parameters\n", Cmd_Argc());
...
}
If the client only typed "useless", then it will be automatically forwarded to
the server, just like "cmd version". The result are always printed to the
calling client.
The function doesn't work on dedicated servers, as it can only be called by
clients through "cmd", so you can be sure that SV_PLAYER always points to the
calling client.
Right now client requests for "useless" are rejected by the server.
Open "sv_user.c", go to the SV_ReadClientMessage() function and directly behind...
else if (Q_strncasecmp(s, "ban", 3) == 0)
ret = 1;
add...
else if (Q_strncasecmp(s, "useless", 7) == 0)
ret = 1;
Now the server will process client requests for commands beginning with
"useless" (e.g. "useless", "useless1", etc.)
2b) ...which do not need a valid SV_PLAYER to access the client's structure/entity
AND/OR shall be available through the dedicated server console
Example: ???
As there's no connected client available on a dedicated server, we have to allow
direct calls from the console. This can be reduced to dedicated servers, by
checking the executing machine for an dedicated flag.
Also we have to decide where to print depending on the command source.
As we do not want to maintain redudant code we have to use a variable for the
function call. We just assign the needed print function to the print variable
depending on the command source.
void Host_Useless_f (void)
{
void (*print) (char *fmt, ...);
int i;
if (cmd_source == src_command)
{
if (cls.state != ca_dedicated) // not a dedicated server
{
Cmd_ForwardToServer (); // client forgot to add "cmd", so forward to server
return; // this a server only command, sorry
}
print = Con_Printf; // directly print to dedicated server console
}
else
{
print = SV_ClientPrintf; // print to client's console
}
print ("You called USELESS with %i parameters\n", Cmd_Argc());
...
}
Now on a dedicated server "useless" will work , but not "cmd useless".
Remember that SV_PLAYER is not valid when called from the dedicated server
console, and you have to allow the command for client requests in "sv_user.c".
You could also check for !sv.active, but then "useless" on a listen server would
act like on a dedicated server (no valid SV_PLAYER).
3) client and server commands
Example: version (enhanced version, how it should be)
If a client enters just "version" in the console, then he wants to see the
version of this particular machine (could also be a dedicated server!). But
when typing "cmd version", he requests the server's version.
Note that the original implementation unfortunately doesn't behave this way.
Now we will enhance the existing "version" command to be able to display the
version of the client/current and the server engine.
void Host_Version_f (void)
{
void (*print) (char *fmt, ...);
if (cmd_source == src_command)
{
print = Con_Printf;
}
else
{
print = SV_ClientPrintf;
print ("Server ");
}
print ("Version %1.2f\n", VERSION);
print ("Exe: "__TIME__" "__DATE__"\n");
}
Rememeber that you have allow the command for client requests in "sv_user.c".
Now you are prepared to develop perfectly working console commands.
Keep classic
Maddes
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?