Difference between revisions of "Scripting FAQ (SourceMod)"
(Added tag mismatch warning question) |
m (→How do I learn SourcePawn?: Remove underscores from cross-page link) |
||
(22 intermediate revisions by 7 users not shown) | |||
Line 1: | Line 1: | ||
+ | [[Image:Yak.gif]] | ||
+ | yak wuz heer | ||
=How do I learn SourcePawn?= | =How do I learn SourcePawn?= | ||
− | A good start is on the [[Introduction to SourcePawn]] page, which will teach you the SourcePawn language. Then go through the [[Introduction to SourceMod Plugins]], which will teach you how to write your first plug-in. | + | A good start is on the [[Introduction to SourcePawn 1.7]] page, which will teach you the SourcePawn language. Then go through the [[Introduction to SourceMod Plugins]], which will teach you how to write your first plug-in. |
=Where can I find all the SourcePawn functions and other information?= | =Where can I find all the SourcePawn functions and other information?= | ||
Line 10: | Line 12: | ||
This line instructs the compiler to retrieve the file <tt>sourcemod.inc</tt> from the <tt>sourcemod/scripting/include</tt> folder. Every function, variable, and definition can be found in the <tt>.inc</tt> files inside the <tt>sourcemod/scripting/include</tt> folder. | This line instructs the compiler to retrieve the file <tt>sourcemod.inc</tt> from the <tt>sourcemod/scripting/include</tt> folder. Every function, variable, and definition can be found in the <tt>.inc</tt> files inside the <tt>sourcemod/scripting/include</tt> folder. | ||
− | But searching through all those files can be quite tedious. Thankfully, the honourable [https://forums.alliedmods.net/member.php?u=9443 Nican] created an API reference viewable on the web, complete with searching and commenting: [ | + | But searching through all those files can be quite tedious. Thankfully, the honourable [https://forums.alliedmods.net/member.php?u=9443 Nican] created an API reference viewable on the web, complete with searching and commenting: [https://sm.alliedmods.net/new-api/ https://sm.alliedmods.net/new-api/] |
− | Also, if you are in the | + | Also, if you are in the AlliedMods Discord server (https://discord.gg/HUc67zN), you can use the <tt>/docs</tt> command to search the API. |
− | |||
− | |||
=How do I find the classname of an entity? (e.g., "<tt>weapon_knife</tt>" or "<tt>prop_physics</tt>")= | =How do I find the classname of an entity? (e.g., "<tt>weapon_knife</tt>" or "<tt>prop_physics</tt>")= | ||
− | 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 [ | + | The classname of an entity (not to be confused with a netclass such as <tt>CCSPlayer</tt>) is a unique identifier. It's the most well known of entity names. To find it, use the function [https://sm.alliedmods.net/new-api/entity/GetEdictClassname GetEdictClassname()]: |
− | <pawn> | + | <pawn>char classname[128]; |
GetEdictClassname(myentity, classname, sizeof(classname)); | GetEdictClassname(myentity, classname, sizeof(classname)); | ||
Line 26: | Line 26: | ||
=How do I block regular commands, such as <tt>kill</tt> and <tt>say</tt>?= | =How do I block regular commands, such as <tt>kill</tt> and <tt>say</tt>?= | ||
− | As of version 1.3, the recommended way to hook and block commands is with [ | + | As of version 1.3, the recommended way to hook and block commands is with [https://sm.alliedmods.net/new-api/console/AddCommandListener AddCommandListener()]. Previously, using [https://sm.alliedmods.net/new-api/console/RegConsoleCmd RegConsoleCmd()] with the command name was the only way to hook commands, but this creates a whole new command for the command dispatch to check every time any command is executed. [https://sm.alliedmods.net/new-api/console/AddCommandListener AddCommandListener()] creates only a lightweight hook, processed only when the specific command is executed. |
Here's how to use it to block the <tt>say</tt> command: | Here's how to use it to block the <tt>say</tt> command: | ||
− | <pawn>public OnPluginStart() | + | <pawn>public void OnPluginStart() |
{ | { | ||
AddCommandListener(SayCallback, "say"); | AddCommandListener(SayCallback, "say"); | ||
} | } | ||
− | public Action | + | public Action SayCallback(int client, const char[] command, int argc) |
{ | { | ||
− | + | return Plugin_Handled; | |
}</pawn> | }</pawn> | ||
=How do I hook +commands, such as <tt>+zoom</tt> and <tt>+attack</tt>?= | =How do I hook +commands, such as <tt>+zoom</tt> and <tt>+attack</tt>?= | ||
− | Unlike regular commands, <tt>+commands</tt> are handled on the client's computer, then sent to the server in a more compressed fashion. This means [ | + | Unlike regular commands, <tt>+commands</tt> are handled on the client's computer, then sent to the server in a more compressed fashion. This means [https://sm.alliedmods.net/new-api/console/AddCommandListener AddCommandListener()] cannot be used to hook <tt>+commands</tt>. As of version 1.3, the recommended solution is to use the global forward [https://sm.alliedmods.net/new-api/sdktools_hooks/OnPlayerRunCmd OnPlayerRunCmd()]. This forward is fired every time a player uses a movement button. To detect or block a <tt>+command</tt>, you'll first have to find out its proper <tt>IN_</tt> constant (see <tt>[https://sm.alliedmods.net/new-api/entity_prop_stocks/__raw entity_prop_stocks.inc]</tt>). |
Here's how to use it to block crouching when attacking: | Here's how to use it to block crouching when attacking: | ||
− | <pawn>public Action | + | <pawn>public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon) |
{ | { | ||
// Check if the player is attacking (+attack) | // Check if the player is attacking (+attack) | ||
Line 66: | Line 66: | ||
Good: | Good: | ||
− | <pawn>public OnPluginStart() | + | <pawn>public void OnPluginStart() |
{ | { | ||
− | + | int myvar = 5; | |
if (myvar == (2 + 3)) | if (myvar == (2 + 3)) | ||
PrintToServer("myvar is %d", myvar); | PrintToServer("myvar is %d", myvar); | ||
Line 75: | Line 75: | ||
Bad: | Bad: | ||
− | <pawn>public | + | <pawn>public voidOnPluginStart() |
{ | { | ||
− | + | int myvar = 5; | |
if (myvar == (2 + 3)) | if (myvar == (2 + 3)) | ||
PrintToServer("myvar is %d", myvar); | PrintToServer("myvar is %d", myvar); | ||
Line 83: | Line 83: | ||
=How do I get rid of tag mismatch warnings?= | =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 inside a cell. To signify a cell's contents, tags are used. The most common are <tt>_</tt> (the default tag: a vanilla cell. This tag is implied when no other tag is specified.), <tt> | + | Though every variable in SourcePawn is one cell (4 bytes), with the exception of strings, there are many different ways to interpret what's inside a cell. To signify a cell's contents, tags are used. The most common are <tt>_</tt> (the default tag: a vanilla cell. This tag is implied when no other tag is specified.), <tt>float</tt>, <tt>bool</tt>, and <tt>char</tt>. See [[Introduction_to_SourcePawn_1.7#Variables_2]] for more information. |
Functions wear these tags on their parameters so you can tell what needs to be passed to the function: | Functions wear these tags on their parameters so you can tell what needs to be passed to the function: | ||
− | <pawn>native SetEntPropFloat(entity, PropType | + | <pawn>native int SetEntPropFloat(int entity, PropType type, const char[] prop, float value, int element)</pawn> |
− | This function calls for '''<tt>entity</tt>''', a cell with the implied tag '''''<tt> | + | This function calls for '''<tt>entity</tt>''', a cell with the implied tag '''''<tt>int</tt>'''''; '''<tt>type</tt>''', with the developer-defined tag '''''<tt>PropType</tt>'''''; '''<tt>prop</tt>''', with the built-in tag '''''<tt>char</tt>'''''; and '''<tt>value</tt>''', with the built-in tag '''''<tt>float</tt>'''''. |
To call this function, then, you must pass values with the specified tags. For example: | To call this function, then, you must pass values with the specified tags. For example: | ||
<pawn>SetEntPropFloat(1234, Prop_Send, "m_fNumber", 1.0);</pawn> | <pawn>SetEntPropFloat(1234, Prop_Send, "m_fNumber", 1.0);</pawn> | ||
− | This calls the function correctly: <tt>1234</tt> is a regular cell ('''''<tt>_</tt>'''''), <tt>Prop_Send</tt> is a variable defined in the enum '''''<tt>PropType</tt>''''' in [ | + | This calls the function correctly: <tt>1234</tt> is a regular cell ('''''<tt>_</tt>'''''), <tt>Prop_Send</tt> is a variable defined in the enum '''''<tt>PropType</tt>''''' in [https://sm.alliedmods.net/new-api/entity/__raw entity.inc], <tt>"m_fNumber"</tt> is a '''''<tt>char</tt>''''', and <tt>1.0</tt> is a '''''<tt>float</tt>'''''. For a nonexample: |
<pawn>SetEntPropFloat(1234.0, 1, "m_fNumber", 1337);</pawn> | <pawn>SetEntPropFloat(1234.0, 1, "m_fNumber", 1337);</pawn> | ||
− | This is incorrect! <tt>1234.0</tt> is a '''''<tt> | + | This is incorrect! <tt>1234.0</tt> is a '''''<tt>float</tt>''''' that should be a regular cell ('''''<tt>int</tt>'''''); <tt>1</tt> is a regular cell ('''''<tt>int</tt>''''') that should be a '''''<tt>PropType</tt>'''''; and <tt>1337</tt> is a regular cell ('''''<tt>int</tt>''''') that should be a '''''<tt>float</tt>'''''. 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 '''''<tt>PropType</tt>''''') can be found in the same file where a function uses them (you can find '''''<tt>PropType</tt>''''' in [https://sm.alliedmods.net/new-api/entity/__raw entity.inc]). |
+ | |||
+ | =How do I add color to my messages?= | ||
+ | Though the actual colors will vary depending on the mod, you can add color to any chat message using the characters <tt>0x01</tt> to <tt>0x08</tt>. For example: | ||
+ | <pawn>PrintToChatAll ("\x01 1 .. \x02 2 .. \x03 3 .. \x04 4 .. \x05 5 .. \x06 6 .. \x07 7 .. \x08 8");</pawn> | ||
+ | |||
+ | Example output from Left 4 Dead: | ||
+ | |||
+ | [[Image:Left_4_Dead_Colors.png]] | ||
+ | |||
+ | With a little experimenting, you can learn the colors for a mod. However, the meaning behind the colors is generally as follows: | ||
+ | |||
+ | * <tt style="color: blue;">0x01</tt> = Normal color | ||
+ | * <tt style="color: blue;">0x02</tt> = 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. | ||
+ | * <tt style="color: blue;">0x03</tt> = Team color | ||
+ | * <tt style="color: blue;">0x04</tt> = Location color | ||
+ | |||
+ | This data comes from Counter-Strike: Source. | ||
+ | |||
+ | For CS:GO, here are some colors: | ||
+ | <pawn>PrintToChat("\x011\x022\x033\x044\x055\x066\x077\x088\x099\x0AA\x0BB\x0CC\x0DD\x0EE\x0FF");</pawn> | ||
+ | |||
+ | Unfortunately, to use players' team colors, you must use UserMessages, because there is no way for the current SourceMod functions to know which team color to use. Here's an example: | ||
+ | |||
+ | <pawn>Handle hBf; | ||
+ | hBf = StartMessageOne("SayText2", player_to); // To send the message to all players, use StartMessageAll("SayText2"); | ||
+ | if (hBf != null) | ||
+ | { | ||
+ | BfWriteByte(hBf, player_from); | ||
+ | BfWriteByte(hBf, 0); | ||
+ | BfWriteString(hBf, "<\x03player_from team color\x01> My message"); | ||
+ | EndMessage(); | ||
+ | }</pawn> | ||
+ | |||
+ | <tt>player_to</tt> is the client index to send the message to (or use [https://sm.alliedmods.net/new-api/usermessages/StartMessageAll StartMessageAll()] instead of [https://sm.alliedmods.net/new-api/usermessages/StartMessageOne StartMessageOne()] to send the message to all players). <tt>player_from</tt> is the client index of the player whose team color will be utilized in the message. The message will now look like this (assuming <tt>player_from</tt> is on the RED team): | ||
+ | |||
+ | <span style="color:#AA0"><</span><span style="color:#F00">player_from team color</span><span style="color:#AA0">> My message</span> | ||
+ | |||
+ | There's another catch, however: this is only known to work in CS:S and TF2. For other mods, your mileage may vary. | ||
+ | |||
+ | Additionally, CS:S, TF2, HL2:DM, and DoD:S all support RGB(A) hex values in chat messages with <tt style="color: blue;">0x07</tt> and <tt style="color: blue;">0x08</tt>, followed by a 6- or 8- char hex value (e.g., <tt>\x07ABCDEF</tt>, <tt>\x0801234567</tt>). | ||
+ | |||
+ | ==Color Libraries== | ||
+ | There are a few libraries that can handle colors for you, so you don't have to muck with UserMessages: | ||
+ | |||
+ | ===exvel's Colors=== | ||
+ | This is a simple include file, allowing easy control over chat coloring. You can grab it [http://forums.alliedmods.net/showthread.php?t=96831 on the forums]. Instead of hex codes, colors are denoted with tags, such as <span style="color: #AA0;"><tt>{default}</tt></span>, <span style="color: #0F0;"><tt>{green}</tt></span>, or <span style="color: #00F;"><tt>{blue}</tt></span>. | ||
+ | |||
+ | Here's some example usage. Note that this code assumes client 1 is in game and on the <span style="color: #00F;">BLU</span> team: | ||
+ | |||
+ | <pawn>#include <colors> | ||
+ | |||
+ | public void OnPluginStart() | ||
+ | { | ||
+ | CPrintToChatAll("{green}Hello {red}World! {default}Test 1 concluded."); | ||
+ | |||
+ | int client_on_blu_team = 1; | ||
+ | CPrintToChatAllEx(client_on_blu_team, "{teamcolor}BLU team {default}says {green}hi!"); | ||
+ | }</pawn> | ||
+ | |||
+ | <span style="color: #0F0;">Hello </span><span style="color: #F00;">World! </span><span style="color: #AA0;">Test 1 concluded.</span> | ||
+ | <span style="color: #00F;">BLU team </span><span style="color: #AA0;">says </span><span style="color: #0F0;">hi!</span> | ||
+ | |||
+ | Note that to use a team color, there needs to be at least one player on that team. Otherwise, it will default to <span style="color: #0F0;"><tt>{green}</tt></span>. | ||
+ | |||
+ | ===SMLib=== | ||
+ | [http://forums.alliedmods.net/showthread.php?p=1398699 SMLib] is a huge collection of stock functions to keep plug-in developers from reinventing the wheel. It includes a [http://forums.alliedmods.net/showthread.php?p=1398702#post1398702 colors API] very similar to [[#exvel's Colors|exvel's]]. The benefits of SMLib are it supports shorthand color names, such as <span style="color: #AA0;"><tt>{N}</tt></span> and <span style="color: #0F0;"><tt>{G}</tt></span>, and all colors are supported in its <tt>Client_PrintToChat()</tt> function. | ||
+ | |||
+ | Here's how to print the same thing as the above example with SMLib. Note that this code also assumes client 1 is in game and on the <span style="color: #00F;">BLU</span> team: | ||
+ | |||
+ | <pawn>#include <smlib> | ||
+ | |||
+ | public void OnPluginStart() | ||
+ | { | ||
+ | Client_PrintToChatAll("{G}Hello {R}World! {N}Test 1 concluded."); | ||
+ | |||
+ | int client_on_blu_team = 1; | ||
+ | Client_PrintToChatAll(client_on_blu_team, "{T}BLU team {N}says {G}hi!"); | ||
+ | }</pawn> | ||
+ | |||
+ | <span style="color: #0F0;">Hello </span><span style="color: #F00;">World! </span><span style="color: #AA0;">Test 1 concluded.</span> | ||
+ | <span style="color: #00F;">BLU team </span><span style="color: #AA0;">says </span><span style="color: #0F0;">hi!</span> | ||
+ | |||
+ | =Why do I get an "unknown symbol" error when using an SDKTools native?= | ||
+ | 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: | ||
+ | <pawn>#include <sdktools></pawn> | ||
+ | to use an SDKTools native. | ||
+ | |||
+ | =Why is Source telling me my command is an "Unknown command"?= | ||
+ | This is because you're not returning <tt>Plugin_Handled</tt> 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. | ||
+ | |||
+ | <pawn>public Action MyCommand(int client, int args) | ||
+ | { | ||
+ | // Do something... | ||
+ | |||
+ | return Plugin_Handled; | ||
+ | }</pawn> |
Latest revision as of 09:45, 23 March 2023
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"?
How do I learn SourcePawn?
A good start is on the Introduction to SourcePawn 1.7 page, which will teach you the SourcePawn language. Then go through the Introduction to SourceMod Plugins, which will teach you how to write your first plug-in.
Where can I find all the SourcePawn functions and other information?
All SourceMod plug-ins have this line:
#include <sourcemod>
This line instructs the compiler to retrieve the file sourcemod.inc from the sourcemod/scripting/include folder. Every function, variable, and definition can be found in the .inc files inside the sourcemod/scripting/include folder.
But searching through all those files can be quite tedious. Thankfully, the honourable Nican created an API reference viewable on the web, complete with searching and commenting: https://sm.alliedmods.net/new-api/
Also, if you are in the AlliedMods Discord server (https://discord.gg/HUc67zN), you can use the /docs command to search the API.
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 such as CCSPlayer) is a unique identifier. It's the most well known of entity names. To find it, use the function GetEdictClassname():
char classname[128]; GetEdictClassname(myentity, classname, sizeof(classname)); PrintToServer("myentity classname: %s", classname); // myentity classname: weapon_knife
How do I block regular commands, such as kill and say?
As of version 1.3, the recommended way to hook and block commands is with AddCommandListener(). Previously, using RegConsoleCmd() with the command name was the only way to hook commands, but this creates a whole new command for the command dispatch to check every time any command is executed. AddCommandListener() creates only a lightweight hook, processed only when the specific command is executed.
Here's how to use it to block the say command:
public void OnPluginStart() { AddCommandListener(SayCallback, "say"); } public Action SayCallback(int client, const char[] command, int argc) { return Plugin_Handled; }
How do I hook +commands, such as +zoom and +attack?
Unlike regular commands, +commands are handled on the client's computer, then sent to the server in a more compressed fashion. This means AddCommandListener() cannot be used to hook +commands. As of version 1.3, the recommended solution is to use the global forward OnPlayerRunCmd(). This forward is fired every time a player uses a movement button. To detect or block a +command, you'll first have to find out its proper IN_ constant (see entity_prop_stocks.inc).
Here's how to use it to block crouching when attacking:
public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon) { // Check if the player is attacking (+attack) if ((buttons & IN_ATTACK) == IN_ATTACK) { // If so, block their crouching (+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; }
How do I get rid of loose indentation warnings?
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, use just one type of indentation. Because different editors have different settings for the size of tab stops, it is recommended you use 4 spaces to indent.
Good:
public void OnPluginStart() { int myvar = 5; if (myvar == (2 + 3)) PrintToServer("myvar is %d", myvar); }
Bad:
public voidOnPluginStart() { int myvar = 5; if (myvar == (2 + 3)) PrintToServer("myvar is %d", myvar); }
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 inside a cell. To signify a cell's contents, 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 char. See Introduction_to_SourcePawn_1.7#Variables_2 for more information.
Functions wear these tags on their parameters so you can tell what needs to be passed to the function:
native int SetEntPropFloat(int entity, PropType type, const char[] prop, float value, int element)
This function calls for entity, a cell with the implied tag int; type, with the developer-defined tag PropType; prop, with the built-in tag char; and value, with the built-in tag float. To call this function, then, you must pass values with the specified tags. For example:
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 char, and 1.0 is a float. For a nonexample:
SetEntPropFloat(1234.0, 1, "m_fNumber", 1337);
This is incorrect! 1234.0 is a float that should be a regular cell (int); 1 is a regular cell (int) that should be a PropType; and 1337 is a regular cell (int) 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).
How do I add color to my messages?
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:
PrintToChatAll ("\x01 1 .. \x02 2 .. \x03 3 .. \x04 4 .. \x05 5 .. \x06 6 .. \x07 7 .. \x08 8");
Example output from Left 4 Dead:
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.
For CS:GO, here are some colors:
PrintToChat("\x011\x022\x033\x044\x055\x066\x077\x088\x099\x0AA\x0BB\x0CC\x0DD\x0EE\x0FF");
Unfortunately, to use players' team colors, you must use UserMessages, because there is no way for the current SourceMod functions to know which team color to use. Here's an example:
Handle hBf; hBf = StartMessageOne("SayText2", player_to); // To send the message to all players, use StartMessageAll("SayText2"); if (hBf != null) { 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):
<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.
Additionally, CS:S, TF2, HL2:DM, and DoD:S all support RGB(A) hex values in chat messages with 0x07 and 0x08, followed by a 6- or 8- char hex value (e.g., \x07ABCDEF, \x0801234567).
Color Libraries
There are a few libraries that can handle colors for you, so you don't have to muck with UserMessages:
exvel's Colors
This is a simple include file, allowing easy control over chat coloring. You can grab it on the forums. Instead of hex codes, colors are denoted with tags, such as {default}, {green}, or {blue}.
Here's some example usage. Note that this code assumes client 1 is in game and on the BLU team:
#include <colors> public void OnPluginStart() { CPrintToChatAll("{green}Hello {red}World! {default}Test 1 concluded."); int client_on_blu_team = 1; CPrintToChatAllEx(client_on_blu_team, "{teamcolor}BLU team {default}says {green}hi!"); }
Hello World! Test 1 concluded. BLU team says hi!
Note that to use a team color, there needs to be at least one player on that team. Otherwise, it will default to {green}.
SMLib
SMLib is a huge collection of stock functions to keep plug-in developers from reinventing the wheel. It includes a colors API very similar to exvel's. The benefits of SMLib are it supports shorthand color names, such as {N} and {G}, and all colors are supported in its Client_PrintToChat() function.
Here's how to print the same thing as the above example with SMLib. Note that this code also assumes client 1 is in game and on the BLU team:
#include <smlib> public void OnPluginStart() { Client_PrintToChatAll("{G}Hello {R}World! {N}Test 1 concluded."); int client_on_blu_team = 1; Client_PrintToChatAll(client_on_blu_team, "{T}BLU team {N}says {G}hi!"); }
Hello World! Test 1 concluded. BLU team says hi!
Why do I get an "unknown symbol" error when using an SDKTools native?
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:
#include <sdktools>
to use an SDKTools native.
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.
public Action MyCommand(int client, int args) { // Do something... return Plugin_Handled; }