The thread will be kept for archival purposes, but all future additions and edits will be made to the wiki article. Please contribute to the wiki page for the benefit of all! Thanks, guys
Most of the time, we volunteer support specialists are in
gs#sourcemod to answer all your questions. Unfortunately, some of us have lives (except for sawce) and we can't be on every waking moment. Fortunately, there is a solution. It is...
Yak's FAQs on Scripting!
A guide to common and not-so-common tasks in SourcePawn
If you have a question and/or answer to add, reply to the thread and I will tack it onto the list. Suggestions (and corrections) are welcome!
Table of Contents
1. How do I learn SourcePawn?
2. Where can I find all the SourcePawn functions and other information?
3. How do I find the classname of an entity? (e.g., "weapon_knife" or "prop_physics")
4. How do I block regular commands, such as
kill and
say?
5. How do I hook +commands, such as
+zoom and
+attack?
6. How do I get rid of loose indentation warnings?
7. How do I get rid of tag mismatch warnings?
8. How do I add color to my messages?
9. Why do I get an "unknown symbol" error when using an SDKTools native?
10. Why is Source telling me my command is an "Unknown command"?
1. How do I learn SourcePawn?
Look at the AlliedModders wiki:
http://wiki.alliedmods.net/Introduction_to_SourcePawn
2. Where can I find all the SourcePawn functions and other information?
When you
#include <sourcemod>, the compiler retrieves the file
sourcemod.inc from the
sourcemod/scripting/include/ folder. Every function, variable, and definition can be found in the .inc files inside
scripting/include.
Searching through all those files can be quite tedious. Thankfully, the honorable
Nican created an API reference on the web, complete with searching and commenting:
http://docs.sourcemod.net/api/
If you are in
gs#sourcemod, you can use the
!api command to search Nican's API reference:
Code:
<theY4Kman> !api Netclass
<yakbot> theY4Kman: GetEntityNetClass(edict, String:clsname[], maxlength): Retrieves an entity's networkable serverclass name. This is not the same as the classname and is used for networkable state changes. (http://docs.sourcemod.net/api/index....oad=show&id=66)
3. How do I find the classname of an entity? (e.g., "weapon_knife" or "prop_physics")
The classname of an entity (not to be confused with a netclass) is a unique identifier. It's the most well known of entity names. To find it, use the function
GetEdictClassname(edict, String:clsname[], maxlength):
Code:
decl String:classname[128];
GetEdictClassname(myentity, classname, sizeof(classname));
4. How do I block regular commands, such as kill and say?
Use
RegConsoleCmd(const String:cmd[], ConCmd:callback, const String:description[]="", flags=0) (
API reference) to hook the command you wish to block. Then, in the callback for the command, return
Plugin_Handled:
Code:
public OnPluginStart()
{
RegConsoleCmd("say", SayCallback);
}
public Action:SayCallback(client, args)
{
return Plugin_Handled;
}
EDIT: As of version 1.3, there is a better way to hook and block commands that are already registered. Unlike
RegConsoleCmd, the new
AddCommandListener(CommandListener:callback, const String:command[]="") creates only a lightweight hook for when the command is run, instead of creating a whole new command and slowing down the command dispatch. Thus, you should
always use AddCommandListener when possible. Here's how to use it:
Code:
public OnPluginStart()
{
AddCommandListener(SayCallback, "say");
}
public Action:SayCallback(client, const String:command[], argc)
{
Return Plugin_Handled;
}
5. How do I hook +commands, such as +zoom and +attack?
Unlike regular commands, the +commands are handled on the client, then sent to the server by other means. That means you cannot use
RegConsoleCmd to hook +commands. It also means that unless you're using SourceMod 1.3,
you can only hook +commands, you CANNOT block them. You must use loop through all the clients
OnGameFrame() and call
GetClientButtons(client) with the proper
IN_ definition (see
entity_prop_stocks.inc) to check if a client is using a +command:
Code:
public OnGameFrame()
{
for (new i=1; i<=MaxClients; i++)
{
if (IsClientInGame(i) && GetClientButtons(i) & IN_DUCK)
{
// Client i is pressing their button bound to +duck
}
}
}
A better method for hooking +commands was added in SourceMod 1.3,
rev 2711: the forward
OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon). This forward is fired every time a player uses a movement button. The great thing about this forward is that changes to the
buttons are saved. Thus, we can actually block +commands:
Code:
public Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon)
{
// Block +duck
buttons &= ~IN_DUCK;
// We must return Plugin_Continue to let the changes be processed.
// Otherwise, we can return Plugin_Handled to block the commands
return Plugin_Continue;
}
A lot of the changes to the buttons won't look too great to the players, because of clientside prediction. Before the server can reply to the user's +command, the client acts out the +command to make the game as smooth as possible. Unfortunately, it makes the game very much not smooth if we edit
buttons.
6. How do I get rid of loose indentation warnings?
Code:
myplugin.sp(#) : warning 217: loose indentation
Loose indentation warnings arise when indentation in your code is inconsistent. This usually means using both tabs and spaces as indentation. However, it can also mean a different amount of spaces or tabs are being used. Therefore, to correct it just use one type of indentation. Because different editors have different settings for the size of tab stops, it is recommended that you use 4 spaces to indent.
7. How do I get rid of tag mismatch warnings?
Though every variable in SourcePawn is one cell (4 bytes), with the exception of strings, there are many different ways to interpret what's contained in the cell. To signify what is in a cell, tags are used. The most common are
_ (the default tag: a vanilla cell. This tag is implied when no other tag is specified.),
Float,
bool, and
String. See
http://wiki.alliedmods.net/Introduct...Pawn#Variables for more information.
Functions wear these tags on their parameters so you can tell what needs to be passed to the function:
Code:
native SetEntPropFloat(entity, PropType:type, const String:prop[], Float:value);
This function calls for
entity, a cell with the implied tag
_,
type, with the developer-defined tag
PropType,
prop, with the built-in tag
String, and
value, with the built-in tag
Float.
To call this function, then, you must pass values with the specified tags. For example:
Code:
SetEntPropFloat(1234, Prop_Send, "m_fNumber", 1.0);
This calls the function correctly:
1234 is a regular cell,
Prop_Send is a variable defined in the
enum PropType in
entity.inc,
"m_fNumber" is a
String, and
1.0 is a
Float. For a nonexample:
Code:
SetEntPropFloat(1234.0, 1, "m_fNumber", 1337);
This is incorrect!
1234.0 is a
Float that should be a regular cell (
_),
1 is a regular cell that should be a
PropType, and
1337 is a regular cell that should be a
Float. This call will generate a tag mismatch warning. To correct it, simply use a value with the correct tag. Most of the time, tags that are not built-in (such as
PropType) can be found in the same file where a function uses them (you can find
PropType in
entity.inc).
8. How do I add color to my messages?
Thanks to antihacker for the question/answer and psychonic for some corrections.
Though the actual colors will vary depending on the mod, you can add color to any chat message using the characters
0x01 to
0x08. For example:
Code:
PrintToChatAll ("\x01 1 .. \x02 2 .. \x03 3 .. \x04 4 .. \x05 5 .. \x06 6 .. \x07 7 .. \x08 8");
Example output from Left 4 Dead (image courtesy of
antihacker):
With a little experimenting, you can learn the colors for a mod. However, the meaning behind the colors is generally as follows:
0x01 = Normal color
0x02 = Use team color to the end of a player's name. When used, it can be the only color used, and it must be at the start of the message.
0x03 = Team color
0x04 = Location color
This data comes from Counter-Strike: Source.
Unfortunately, to use players' team colors, you must use UserMessages, because there is no way for the current SourceMod functions to know what team color to use. Here's an example:
Code:
new Handle:hBf;
hBf = StartMessageOne("SayText2", player_to); // To send the message to all players, use StartMessageAll("SayText2");
if (hBf != INVALID_HANDLE)
{
BfWriteByte(hBf, player_from);
BfWriteByte(hBf, 0);
BfWriteString(hBf, "<\x03player_from team color\x01> My message");
EndMessage();
}
player_to is the client index to send the message to (Or use
StartMessageAll() instead of
StartMessageOne() to send the message to all players).
player_from is the client index of the player whose team color will be utilized in the message. The message will now look like this (assuming player_from is on the RED team):
Code:
<player_from team color> My message
There's another catch, however: this is only known to work in CS:S and TF2. For other mods, your mileage may vary.
EDIT: exvel has released an excellent
library of functions that will do all this work for you. Instead of using color codes, you merely write "{green}" or "{blue}".
9. Why do I get an "unknown symbol" error when using an SDKTools native?
Thanks to Tsunami
None of SourceMod's or SDKTools's functions are built into SourcePawn. Therefore, every time you use one of their functions, SourcePawn needs to know how to call the function. Normally, this is done using includes. Just like you would
#include <sourcemod> to use SourceMod's functions, you need to:
Code:
#include <sdktools>
to use an SDKTools native.
10. Why is Source telling me my command is an "Unknown command"?
This is because you're not returning Plugin_Handled in your callback. If you don't, SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so.
__________________