<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.alliedmods.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dr.+Greg+House</id>
	<title>AlliedModders Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.alliedmods.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dr.+Greg+House"/>
	<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/Special:Contributions/Dr._Greg_House"/>
	<updated>2026-05-27T02:27:20Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.6</generator>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=KeyValues_(SourceMod_Scripting)&amp;diff=10088</id>
		<title>KeyValues (SourceMod Scripting)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=KeyValues_(SourceMod_Scripting)&amp;diff=10088"/>
		<updated>2015-12-05T23:51:52Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Added a small annotation in order to try to prevent wrong usage scenarios of keyvalues (i.e. for player stats), as threads have been appearing continuously over the years. Stop the madness! ToDo: Maybe a pro-con section or a better example?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;KeyValues are simple, tree-based structures used for storing nested sections containing key/value pairs.  Detailed information on KeyValues can be seen at the [http://developer.valvesoftware.com/wiki/KeyValues_class Valve Developer Wiki].  &lt;br /&gt;
&lt;br /&gt;
All KeyValues specific functions in this document are from &amp;lt;tt&amp;gt;public/include/keyvalues.inc&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''Note: While the following examples are correct code-wise, over the years they have occasionally led people to use KeyValues in cases where they really should be using a [https://wiki.alliedmods.net/SQL_%28SourceMod_Scripting%29 database] instead.''&lt;br /&gt;
&lt;br /&gt;
=Introduction=&lt;br /&gt;
KeyValues consist of a ''nodes'', or ''sections'', which contain pairs of keys and values.  A section looks like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;quot;section&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;key&amp;quot;	&amp;quot;value&amp;quot;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;&amp;quot;section&amp;quot;&amp;lt;/tt&amp;gt; string denotes the section's name.  The &amp;lt;tt&amp;gt;&amp;quot;key&amp;quot;&amp;lt;/tt&amp;gt; string is the key name, and the &amp;lt;tt&amp;gt;&amp;quot;value&amp;quot;&amp;lt;/tt&amp;gt;&amp;quot; string is the value.&lt;br /&gt;
&lt;br /&gt;
KeyValues structures are created with &amp;lt;tt&amp;gt;CreateKeyValues()&amp;lt;/tt&amp;gt;.  The Handle must be freed when finished in order to avoid a memory leak.&lt;br /&gt;
&lt;br /&gt;
=Files=&lt;br /&gt;
KeyValues can be exported and imported via KeyValues files.  These files always consist of a root node and any number of sub-keys or sub-sections.  For example, a file might look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;quot;MyFile&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;STEAM_0:0:7&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;name&amp;quot;		&amp;quot;crab&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example, &amp;lt;tt&amp;gt;STEAM_0:0:7&amp;lt;/tt&amp;gt; is a section under &amp;lt;tt&amp;gt;MyFile&amp;lt;/tt&amp;gt;, the root node.  &lt;br /&gt;
&lt;br /&gt;
To load KeyValues from a file, use &amp;lt;tt&amp;gt;FileToKeyValues&amp;lt;/tt&amp;gt;  To save KeyValues to a file, use &amp;lt;tt&amp;gt;KeyValuesToFile&amp;lt;/tt&amp;gt;.  For both cases, you must already have a Handle to a KeyValues structure.&lt;br /&gt;
&lt;br /&gt;
=Traversal=&lt;br /&gt;
SourceMod provides natives for traversing over a KeyValues structure.  However, it is important to understand how this traversal works.  Internally, SourceMod keeps track of two pieces of information:&lt;br /&gt;
*The root node&lt;br /&gt;
*A traversal stack&lt;br /&gt;
&lt;br /&gt;
Since a KeyValues structure is inherently recursive (it's a tree), the ''traversal stack'' is used to save a history of each ''traversal'' made (a traversal being a recursive dive into the tree, where the current nesting level deepens by one section).  The ''top'' of this stack is where all operations take place.  &lt;br /&gt;
&lt;br /&gt;
For example, &amp;lt;tt&amp;gt;kv.JumpToKey&amp;lt;/tt&amp;gt; will attempt to find a sub-key under the current section.  If it succeeds, the position in the tree has changed by moving down one level, and thus this position is pushed onto the traversal stack.  Now, operations such as &amp;lt;tt&amp;gt;kv.GetString&amp;lt;/tt&amp;gt; will use this new position.&lt;br /&gt;
&lt;br /&gt;
There are natives which change the position but do not change the traversal stack.  For example, &amp;lt;tt&amp;gt;kv.GotoNextKey&amp;lt;/tt&amp;gt; will change the current section being viewed, but there are no push/pop operations to the traversal stack.  This is because the nesting level did not change; it advances to the next section at the same level, rather than finding a section a level deeper.&lt;br /&gt;
&lt;br /&gt;
More traversal natives:&lt;br /&gt;
*&amp;lt;tt&amp;gt;kv.GotoFirstSubKey&amp;lt;/tt&amp;gt; - Finds the first sub-section under the current section.  This pushes the section onto the traversal stack.&lt;br /&gt;
*&amp;lt;tt&amp;gt;kv.GoBack&amp;lt;/tt&amp;gt; - Pops the traversal stack (moves up one level).&lt;br /&gt;
*&amp;lt;tt&amp;gt;kv.Rewind&amp;lt;/tt&amp;gt; - Clears the traversal stack so the current position is the root node.&lt;br /&gt;
&lt;br /&gt;
==Basic Lookup==&lt;br /&gt;
Let's take our &amp;lt;tt&amp;gt;MyFile&amp;lt;/tt&amp;gt; example from above.  How could we retrieve the name &amp;quot;crab&amp;quot; given the Steam ID?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;bool GetNameFromSteamID(const char[] steamid, char[] name, int maxlength)&lt;br /&gt;
{&lt;br /&gt;
	KeyValues kv = new KeyValues(&amp;quot;MyFile&amp;quot;);&lt;br /&gt;
	kv.ImportFromFile(&amp;quot;myfile.txt&amp;quot;);&lt;br /&gt;
	if (!kv.JumpToKey(steamid))&lt;br /&gt;
	{&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
	kv.GetString(&amp;quot;name&amp;quot;, name, maxlength);&lt;br /&gt;
	delete kv;&lt;br /&gt;
	return true;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Note:''' &amp;lt;tt&amp;gt;kv.JumpToKey&amp;lt;/tt&amp;gt; is a traversal native that changes the nesting level of the traversal stack.  However, &amp;lt;tt&amp;gt;delete&amp;lt;/tt&amp;gt; will not accidentally close only the current level of the KeyValues structure.  It will close the entire thing.&lt;br /&gt;
&lt;br /&gt;
==Iterative Lookup==&lt;br /&gt;
Let's modify our previous example to use iteration.  This has the same functionality, but demonstrates how to browse over many sections.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;bool GetNameFromSteamID(const char[] steamid, char[] name,int maxlength)&lt;br /&gt;
{&lt;br /&gt;
	KeyValues kv = new KeyValues(&amp;quot;MyFile&amp;quot;);&lt;br /&gt;
	kv.ImportFromFile(&amp;quot;myfile.txt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	if (!kv.GotoFirstSubKey())&lt;br /&gt;
	{&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	char buffer[255];&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		kv.GetSectionName(buffer, sizeof(buffer));&lt;br /&gt;
		if (StrEqual(buffer, steamid))&lt;br /&gt;
		{&lt;br /&gt;
			kv.GetString(&amp;quot;name&amp;quot;, name, maxlength);&lt;br /&gt;
			delete kv;&lt;br /&gt;
			return true;&lt;br /&gt;
		}&lt;br /&gt;
	} while (kv.GotoNextKey());&lt;br /&gt;
&lt;br /&gt;
	delete kv;&lt;br /&gt;
	return false;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Note:''' In this example, note that &amp;lt;tt&amp;gt;kv.GotoNextKey&amp;lt;/tt&amp;gt; is an iterative function, not a traversal one.  Since it does not change the nesting level, we don't need to call &amp;lt;tt&amp;gt;kv.GoBack&amp;lt;/tt&amp;gt; to return and continue iterating.&lt;br /&gt;
&lt;br /&gt;
==Full Traversal==&lt;br /&gt;
Let's say we wanted to browse every section of a KeyValues file.  An example of this might look like:&lt;br /&gt;
&amp;lt;pawn&amp;gt;BrowseKeyValues(KeyValues kv)&lt;br /&gt;
{&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		// You can read the section/key name by using KvGetSectionName here.&lt;br /&gt;
&lt;br /&gt;
		if (kv.GotoFirstSubKey(false))&lt;br /&gt;
		{&lt;br /&gt;
			// Current key is a section. Browse it recursively.&lt;br /&gt;
			BrowseKeyValues(kv);&lt;br /&gt;
			kv.GoBack(kv);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			// Current key is a regular key, or an empty section.&lt;br /&gt;
			if (kv.GetDataType(NULL_STRING) != KvData_None)&lt;br /&gt;
			{&lt;br /&gt;
				// Read value of key here (use NULL_STRING as key name). You can&lt;br /&gt;
				// also get the key name by using kv.GetSectionName here.&lt;br /&gt;
			}&lt;br /&gt;
			else&lt;br /&gt;
			{&lt;br /&gt;
				// Found an empty sub section. It can be handled here if necessary.&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	} while (kv.GotoNextKey(false));&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function will browse an entire KeyValues structure.  Note that every successful call to &amp;lt;tt&amp;gt;kv.GotoFirstSubKey&amp;lt;/tt&amp;gt; is paired with a call to &amp;lt;tt&amp;gt;kv.GoBack&amp;lt;/tt&amp;gt;.  This is because the former pushes onto the traversal stack.  We must pop the position off so we can continue browsing from our old position.&lt;br /&gt;
&lt;br /&gt;
Also note that &amp;lt;tt&amp;gt;keyOnly&amp;lt;/tt&amp;gt; is set to false in both &amp;lt;tt&amp;gt;kv.GotoFirstSubKey&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;kv.GotoNextKey&amp;lt;/tt&amp;gt; so that it will jump to regular keys and not just between sections.  Because of this we also need to check if &amp;lt;tt&amp;gt;kv.GotoFirstSubKey&amp;lt;/tt&amp;gt; succeeded moving to a regular key before we read the value. This is simply done by getting the data type of the current key.  If &amp;lt;tt&amp;gt;kv.GotoFirstSubKey&amp;lt;/tt&amp;gt; failed to move to any key (the section is empty), the cursor is still on the section, which doesn't have a data type.  If there is a data type, we've confirmed that it's a regular key.&lt;br /&gt;
&lt;br /&gt;
=Deletion=&lt;br /&gt;
There are two ways to delete sections from a KeyValues structure:&lt;br /&gt;
*&amp;lt;tt&amp;gt;kv.DeleteKey&amp;lt;/tt&amp;gt; - Safely removes a named sub-section and any of its child sections/keys.&lt;br /&gt;
*&amp;lt;tt&amp;gt;kv.DeleteThis&amp;lt;/tt&amp;gt; - Safely removes the current position if possible.&lt;br /&gt;
&lt;br /&gt;
==Simple Deletion==&lt;br /&gt;
First, let's use our &amp;quot;basic lookup&amp;quot; example to delete a Steam ID section:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
RemoveSteamID(const char[] steamid)&lt;br /&gt;
{&lt;br /&gt;
	KeyValues kv = new KeyValues(&amp;quot;MyFile&amp;quot;);&lt;br /&gt;
	kv.ImportFromFile(&amp;quot;myfile.txt&amp;quot;);&lt;br /&gt;
	if (!kv.JumpToKey(steamid))&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
	kv.DeleteThis();&lt;br /&gt;
	kv.Rewind();&lt;br /&gt;
	kv.ExportToFile(&amp;quot;myfile.txt&amp;quot;);&lt;br /&gt;
	delete kv;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Note:''' We called &amp;lt;tt&amp;gt;kv.Rewind&amp;lt;/tt&amp;gt; so the file would be dumped from the root position, instead of the current one.&lt;br /&gt;
&lt;br /&gt;
==Iterative Deletion==&lt;br /&gt;
Likewise, let's show how our earlier iterative example could be adapted for deleting a section.  For this we can use &amp;lt;tt&amp;gt;kv.DeleteThis&amp;lt;/tt&amp;gt;, which deletes the current position from the previous position in the traversal stack.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;RemoveSteamID(const char[] steamid)&lt;br /&gt;
{&lt;br /&gt;
	KeyValues kv = new KeyValues(&amp;quot;MyFile&amp;quot;);&lt;br /&gt;
	kv.ImportFromFile(&amp;quot;myfile.txt&amp;quot;);&lt;br /&gt;
	if (!kv.JumpToKey(steamid))&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	char buffer[255];&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		kv.GetSectionName(buffer, sizeof(buffer))&lt;br /&gt;
		if (StrEqual(buffer, steamid))&lt;br /&gt;
		{&lt;br /&gt;
			kv.DeleteThis();&lt;br /&gt;
			delete kv;&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
	} while (kv.GotoNextKey());&lt;br /&gt;
&lt;br /&gt;
	delete kv;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Full Deletion==&lt;br /&gt;
Now, let's take a look at how we would delete all (or some) keys.  &amp;lt;tt&amp;gt;kv.DeleteThis&amp;lt;/tt&amp;gt; has the special property that it can act as an automatic iterator.  When it deletes a key, it automatically attempts to advance to the next one, as &amp;lt;tt&amp;gt;kv.GotoNextKey&amp;lt;/tt&amp;gt; would.  If it can't find any more keys, it simply pops the traversal stack.  &lt;br /&gt;
&lt;br /&gt;
An example of deleting all SteamIDs that have blank names:&lt;br /&gt;
&amp;lt;pawn&amp;gt;DeleteAll(KeyValues kv)&lt;br /&gt;
{&lt;br /&gt;
	if (!kv.GotoFirstSubKey())&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (;;)&lt;br /&gt;
	{&lt;br /&gt;
		char name[4];&lt;br /&gt;
		kv.GetString(name, sizeof(name));&lt;br /&gt;
		if (name[0] == '\0')&lt;br /&gt;
		{&lt;br /&gt;
			if (kv.DeleteThis() &amp;lt; 1)&lt;br /&gt;
			{&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		} else if (!kv.GotoNextKey()) {&lt;br /&gt;
			break;&lt;br /&gt;
		}	&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While at first this loop looks infinite, it is not.  If &amp;lt;tt&amp;gt;kv.DeleteThis&amp;lt;/tt&amp;gt; fails to find a subsequent key, it will break out of the loop.  Similarly, if &amp;lt;tt&amp;gt;kv.GotoNextKey&amp;lt;/tt&amp;gt; fails, the loop will end.&lt;br /&gt;
&lt;br /&gt;
==KeyValue Creation==&lt;br /&gt;
This is how you would create the &amp;lt;tt&amp;gt;MyFile&amp;lt;/tt&amp;gt; example from above with the KeyValue API&lt;br /&gt;
&amp;lt;pawn&amp;gt;KeyValues kv = new KeyValues(&amp;quot;MyFile&amp;quot;);&lt;br /&gt;
kv.JumpToKey(&amp;quot;STEAM_0:0:7&amp;quot;, true);&lt;br /&gt;
kv.SetString(&amp;quot;name&amp;quot;, &amp;quot;crab&amp;quot;);&lt;br /&gt;
kv.Rewind();&lt;br /&gt;
kv.ExportToFile(&amp;quot;C:\javalia.txt&amp;quot;);&lt;br /&gt;
delete kv;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=CSGO_Quirks&amp;diff=9905</id>
		<title>CSGO Quirks</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=CSGO_Quirks&amp;diff=9905"/>
		<updated>2015-05-09T14:49:16Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Elaborating on what will happen if you dare to use fancy wavs.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''Others are encouraged to expand on this article with their findings or to clarify any information''&lt;br /&gt;
&lt;br /&gt;
This article explains some of the quirks when coding for CS:GO and offers some workarounds when available and other information.  &lt;br /&gt;
&lt;br /&gt;
=Playing Custom Sounds=&lt;br /&gt;
Since Left 4 Dead, all normally played sounds must exist in the sound cache on the client. This is an issue for custom sounds as adding to the sound cache requires the client to run snd_rebuildaudiocache (not executable by the server), which also takes a sizable amount of time to run while otherwise locking up the game.&lt;br /&gt;
&lt;br /&gt;
==Workarounds==&lt;br /&gt;
Note that with any of these methods, it is still required that you still add the sound to the download table if you need the client to download it from the server or &amp;quot;fastdl&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
All of the listed workarounds involve explicitly telling the client to stream the sound directly from the disk rather than starting it from the cache. This is less than ideal, but seems to work well enough. These have only been made to work with mp3 files. A way to support wav files has not yet been found.&lt;br /&gt;
&lt;br /&gt;
===The &amp;quot;play&amp;quot; client command===&lt;br /&gt;
If you don't need all of the flexibility that the EmitSound* natives expose and just need one or more clients to hear a sound, you can use the ClientCommand native with the 'play' command. The only difference from normal operation is the prefixing of an asterisk (*) to the path which denotes the sound to be streamed. For this method, you do not need to use PrecacheSound on the server.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
for csgo/sound/custom/ur.mp3&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
ClientCommand( client, &amp;quot;play */custom/ur.mp3&amp;quot; );&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Fake precaching and EmitSound===&lt;br /&gt;
For this method, you also need to make use of the asterisk trick. Unfortunately, the EmitSound natives will fail if the file is not listed in the soundprecache table, and PrecacheSound will fail if the file does not exist, (which is won't, as it treats the asterisk as part of the path when doing the lookup).&lt;br /&gt;
&lt;br /&gt;
Since the sound will be streamed, not needing to actually be precached other than to satisfy the check in EmitSound, we can work around this by manually adding the path, with the asterisk prefixed, directly to the soundprecache table. Then you use the EmitSound native of choice as usual with the exception of the asterisk.&lt;br /&gt;
&lt;br /&gt;
Full plugin example:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
#include &amp;lt;sdktools&amp;gt;&lt;br /&gt;
&lt;br /&gt;
new const String:FULL_SOUND_PATH[] = &amp;quot;sound/custom/ur.mp3&amp;quot;;&lt;br /&gt;
new const String:RELATIVE_SOUND_PATH[] = &amp;quot;*/custom/ur.mp3&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd( &amp;quot;sm_testsound&amp;quot;, sm_testsound );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
	AddFileToDownloadsTable( FULL_SOUND_PATH );&lt;br /&gt;
	FakePrecacheSound( RELATIVE_SOUND_PATH );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:sm_testsound( client, argc )&lt;br /&gt;
{&lt;br /&gt;
	EmitSoundToClient( client, RELATIVE_SOUND_PATH );&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
stock FakePrecacheSound( const String:szPath[] )&lt;br /&gt;
{&lt;br /&gt;
	AddToStringTable( FindStringTable( &amp;quot;soundprecache&amp;quot; ), szPath );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternately, if you are going to have sounds in a plugin that supports multiple games, you should use the [https://forums.alliedmods.net/showthread.php?t=237045 EmitSoundAny snippets].&lt;br /&gt;
&lt;br /&gt;
''Recent reports indicate that this only works for '''mp3'''s and not '''wav'''s. In the latter case nothing will happen apart from you experiencing puzzled facial expressions and headaches on yourself.''&lt;br /&gt;
&lt;br /&gt;
===Using the music directory===&lt;br /&gt;
By adding your custom sounds under sound/music/, they will automatically be streamed from disk. They can also be used just like sounds in any other game with regard to PrecacheSound (albeit not necessary to be beyond adding to soundprecache table) and EmitSound.&lt;br /&gt;
&lt;br /&gt;
This will have the side effect of your sounds volume being tied to the game's music volume, which many players turn down or off. For this reason, it's not recommended.&lt;br /&gt;
&lt;br /&gt;
=Max Players, Clients=&lt;br /&gt;
CS:GO has four different values that all affect the maximum number of players that can join a game. The lowest one of the four determines the value used.&lt;br /&gt;
&lt;br /&gt;
==The absolute maximum==&lt;br /&gt;
The current absolute maximum number of players for CS:GO, including GOTV is '''64'''. This is a compile-time maximum in the engine on both client and server and cannot be changed.&lt;br /&gt;
&lt;br /&gt;
(This is the maximum value as returned by IServerGameClients::GetPlayerLimits).&lt;br /&gt;
&lt;br /&gt;
==The engine's Maxclients==&lt;br /&gt;
This is the number known as gpGlobals-&amp;gt;maxClients in SM extensions or MM:S plugins and MaxClients in SourcePawn.&lt;br /&gt;
&lt;br /&gt;
It is able to be changed in other games, up to the maximum, by setting the -maxplayers command line parameter. As this doesn't exist in CS:GO, it acts like other games when not set and uses a hardcoded default, '''64''' for CS:GO.&lt;br /&gt;
&lt;br /&gt;
(This is the default value as returned by IServerGameClients::GetPlayerLimits).&lt;br /&gt;
&lt;br /&gt;
A gamemode's maxplayers can be overridden with the maxplayers parameter in the appropriate section of gamemodes_server.txt&lt;br /&gt;
&lt;br /&gt;
To override maxplayers for all gamemodes, use the -maxplayers_override command line parameter.  However, be aware that the game itself appears to enforce a 44 player maximum regardless of what you set this to.&lt;br /&gt;
&lt;br /&gt;
==The GameTypes maxplayers==&lt;br /&gt;
CS:GO has a new &amp;quot;GameTypes&amp;quot; system with it's own set of game mode and type -specifec params. There is more on this below, but it also includes a maxplayers value, also referred to as numSlots or MaxHumanPlayers in some areas.&lt;br /&gt;
&lt;br /&gt;
This is the value set in gamemodes.txt or overridden in gamemodes_server.txt or overridden by the new -maxplayer_override command line parameter.&lt;br /&gt;
&lt;br /&gt;
It is used for showing the maxplayers listed in the output of the status command as well as the max count used for the server browser (unless overridden by sv_visiblemaxplayers).&lt;br /&gt;
&lt;br /&gt;
This does not include the count set by the &amp;quot;extraspectators&amp;quot; value in the gamemodes.txt, but both maxplayers and extraspectators must be equal or less than the engine's Maxclients.&lt;br /&gt;
&lt;br /&gt;
The GameTypes max can be retrieved in SourcePawn with the new GetMaxHumanPlayers native or in C++ with IServerGameClients::GetMaxHumanPlayers.&lt;br /&gt;
&lt;br /&gt;
==Spawnpoint count==&lt;br /&gt;
Regardless of the above values, you're limited by the running map's number of spawnpoints, evenly split per team (30 for stock maps).&lt;br /&gt;
&lt;br /&gt;
You can use a mod like [http://www.bailopan.net/stripper/ Stripper:Source] or [https://forums.alliedmods.net/showthread.php?p=1054201 Spawn Tools 7] to add more spawnpoint entities.&lt;br /&gt;
&lt;br /&gt;
=Menus=&lt;br /&gt;
With SourceMod's &amp;quot;Radio-style&amp;quot; menus (ShowMenu / CHudMenu), the 0 key will cannot be detected due to the client never sending &amp;quot;menuselect 0&amp;quot;. SourceMod works around this by limiting menus to 9.&lt;br /&gt;
&lt;br /&gt;
The older &amp;quot;Valve-style&amp;quot; menus created by IServerPluginHelpers::CreateMessage with the DIALOG_MENU type aren't supported at all due at least to missing res file info on the client.&lt;br /&gt;
&lt;br /&gt;
=GameTypes / GameModes=&lt;br /&gt;
CS:GO uses a new &amp;quot;GameTypes&amp;quot; system for coordinating server mode and type info between the server, client, and matchmaking, as well as for handling some server rules.&lt;br /&gt;
&lt;br /&gt;
Some things handled by the GameTypes system:&lt;br /&gt;
*Game types&lt;br /&gt;
*Game modes&lt;br /&gt;
*Config to execute for each mode&lt;br /&gt;
*Weapon progression for applicable modes&lt;br /&gt;
*Maps and map order for each mode&lt;br /&gt;
*Maxplayers for each type and mode&lt;br /&gt;
*Player and view models for each map&lt;br /&gt;
*Bot difficulty&lt;br /&gt;
*ELO ranking data&lt;br /&gt;
&lt;br /&gt;
gamemodes.txt is the main data file (in KeyValues format) holding the backing info, with an optional gamemodes_server.txt being merged into it.&lt;br /&gt;
&lt;br /&gt;
The gamemodes data files should not be accessed directly, but rather through the IGameTypes interface. None of it is currently exposed in SourceMod but there are plans to add a new extension giving access to much of the data.&lt;br /&gt;
&lt;br /&gt;
In C++, you can easily get a pointer to the gametypes interface with CreateInterfaceFn from the matchmaking_ds binary, looking up VENGINE_GAMETYPES_VERSION.&lt;br /&gt;
&lt;br /&gt;
See: http://hg.alliedmods.net/hl2sdks/hl2sdk-csgo/file/tip/public/matchmaking/igametypes.h&lt;br /&gt;
&lt;br /&gt;
Example for getting the gametypes ptr in an SM extension, courtesy of Drifter's work-in-progress gametypes extension:&lt;br /&gt;
&amp;lt;cpp&amp;gt;&lt;br /&gt;
IGameTypes *g_pGameTypes;&lt;br /&gt;
&lt;br /&gt;
#define GET_V_IFACE_CURRENT_CUSTOM(v_factory, v_var, v_type, v_name) \&lt;br /&gt;
        v_var = (v_type *)g_SMAPI-&amp;gt;VInterfaceMatch(v_factory, v_name); \&lt;br /&gt;
        if (!v_var) \&lt;br /&gt;
        { \&lt;br /&gt;
                if (error &amp;amp;&amp;amp; maxlen) \&lt;br /&gt;
                { \&lt;br /&gt;
                        g_SMAPI-&amp;gt;Format(error, maxlen, &amp;quot;Could not find interface: %s&amp;quot;, v_name); \&lt;br /&gt;
                } \&lt;br /&gt;
                return false; \&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
	CreateInterfaceFn matchmakingDSFactory = NULL;&lt;br /&gt;
	ILibrary *mmlib;&lt;br /&gt;
	char path[PLATFORM_MAX_PATH];&lt;br /&gt;
&lt;br /&gt;
	libsys-&amp;gt;PathFormat(path, sizeof(path), &amp;quot;%s/bin/matchmaking_ds%s.%s&amp;quot;, g_SMAPI-&amp;gt;GetBaseDir(), MATCHMAKINGDS_SUFFIX, MATCHMAKINGDS_EXT);&lt;br /&gt;
&lt;br /&gt;
	if ((mmlib = libsys-&amp;gt;OpenLibrary(path, NULL, 0)))&lt;br /&gt;
	{&lt;br /&gt;
		matchmakingDSFactory = (CreateInterfaceFn)mmlib-&amp;gt;GetSymbolAddress(&amp;quot;CreateInterface&amp;quot;);&lt;br /&gt;
		mmlib-&amp;gt;CloseLibrary();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (!matchmakingDSFactory)&lt;br /&gt;
	{&lt;br /&gt;
		g_pSM-&amp;gt;Format(error, maxlen, &amp;quot;Failed to find matchmakingDS factory&amp;quot;);&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	GET_V_IFACE_CURRENT_CUSTOM(matchmakingDSFactory, g_pGameTypes, IGameTypes, VENGINE_GAMETYPES_VERSION);&lt;br /&gt;
&lt;br /&gt;
	if (!g_pGameTypes)&lt;br /&gt;
	{&lt;br /&gt;
		g_pSM-&amp;gt;Format(error, maxlen, &amp;quot;Failed to find IGameTypes ptr&amp;quot;);&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/cpp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Coding=&lt;br /&gt;
There are exceptionalities for some of the SourcePawn coding conventions in CS:GO.&lt;br /&gt;
&lt;br /&gt;
==Slaying players during player_hurt event==&lt;br /&gt;
CS:GO will crash if you attempt to call ForcePlayerSuicide() during a player_hurt callback. A timer can be used as a workaround.&lt;br /&gt;
&lt;br /&gt;
Example for slaying a team attacker:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	HookEvent(&amp;quot;player_hurt&amp;quot;, OnPlayerHurt);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnPlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
	new victim   = GetClientOfUserId(GetEventInt(event,&amp;quot;userid&amp;quot;));&lt;br /&gt;
	new attacker = GetClientOfUserId(GetEventInt(event,&amp;quot;attacker&amp;quot;));&lt;br /&gt;
	if(attacker &amp;gt; 0 &amp;amp;&amp;amp; attacker &amp;lt;= MaxClients &amp;amp;&amp;amp; IsPlayerAlive(attacker) &amp;amp;&amp;amp; GetClientTeam(attacker) == GetClientTeam(victim))&lt;br /&gt;
		CreateTimer(0.0, SlayTimer, attacker, TIMER_FLAG_NO_MAPCHANGE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:SlayTimer(Handle:timer, any:client)&lt;br /&gt;
{&lt;br /&gt;
	ForcePlayerSuicide(client);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Assists / Score==&lt;br /&gt;
Assists and score are not named entity properties in CS:GO. To view or alter assists or displayed score, you can use the following natives new in SM 1.5.0-hg3706:&lt;br /&gt;
&lt;br /&gt;
CS_GetClientAssists&lt;br /&gt;
CS_SetClientAssists&lt;br /&gt;
CS_GetContributionScore&lt;br /&gt;
CS_SetContributionScore&lt;br /&gt;
&lt;br /&gt;
==Denoting Text-Colors==&lt;br /&gt;
CSGO has a few odd requirements for properly coloring text in chat.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
// \x01 is white.&lt;br /&gt;
// \x04 is green.&lt;br /&gt;
&lt;br /&gt;
//These examples set the first half of the string green, and the second half white.&lt;br /&gt;
&lt;br /&gt;
//Normal example:&lt;br /&gt;
new String:normalColoring = &amp;quot;\x04This half is green,\x01 This half is white.&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
//CSGO example:&lt;br /&gt;
new String:csgoColoring = &amp;quot; \x01\x0B\x04This half is green,\x01 This half is white.&amp;quot;;&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Specific Quirks:&lt;br /&gt;
* The string must start with a space.&lt;br /&gt;
* Following the space there must be a white color-indicator (\x01).&lt;br /&gt;
* Following the \x01, there must be a printable character. \x0B works great because it does not appear in the message.&lt;br /&gt;
* After all of these steps you can treat the rest of the string like normal.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=CSGO_Quirks&amp;diff=9904</id>
		<title>CSGO Quirks</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=CSGO_Quirks&amp;diff=9904"/>
		<updated>2015-05-09T14:44:55Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Return of the evil slashes.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;''Others are encouraged to expand on this article with their findings or to clarify any information''&lt;br /&gt;
&lt;br /&gt;
This article explains some of the quirks when coding for CS:GO and offers some workarounds when available and other information.  &lt;br /&gt;
&lt;br /&gt;
=Playing Custom Sounds=&lt;br /&gt;
Since Left 4 Dead, all normally played sounds must exist in the sound cache on the client. This is an issue for custom sounds as adding to the sound cache requires the client to run snd_rebuildaudiocache (not executable by the server), which also takes a sizable amount of time to run while otherwise locking up the game.&lt;br /&gt;
&lt;br /&gt;
==Workarounds==&lt;br /&gt;
Note that with any of these methods, it is still required that you still add the sound to the download table if you need the client to download it from the server or &amp;quot;fastdl&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
All of the listed workarounds involve explicitly telling the client to stream the sound directly from the disk rather than starting it from the cache. This is less than ideal, but seems to work well enough. These have only been made to work with mp3 files. A way to support wav files has not yet been found.&lt;br /&gt;
&lt;br /&gt;
===The &amp;quot;play&amp;quot; client command===&lt;br /&gt;
If you don't need all of the flexibility that the EmitSound* natives expose and just need one or more clients to hear a sound, you can use the ClientCommand native with the 'play' command. The only difference from normal operation is the prefixing of an asterisk (*) to the path which denotes the sound to be streamed. For this method, you do not need to use PrecacheSound on the server.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
for csgo/sound/custom/ur.mp3&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
ClientCommand( client, &amp;quot;play */custom/ur.mp3&amp;quot; );&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Fake precaching and EmitSound===&lt;br /&gt;
For this method, you also need to make use of the asterisk trick. Unfortunately, the EmitSound natives will fail if the file is not listed in the soundprecache table, and PrecacheSound will fail if the file does not exist, (which is won't, as it treats the asterisk as part of the path when doing the lookup).&lt;br /&gt;
&lt;br /&gt;
Since the sound will be streamed, not needing to actually be precached other than to satisfy the check in EmitSound, we can work around this by manually adding the path, with the asterisk prefixed, directly to the soundprecache table. Then you use the EmitSound native of choice as usual with the exception of the asterisk.&lt;br /&gt;
&lt;br /&gt;
Full plugin example:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
#include &amp;lt;sdktools&amp;gt;&lt;br /&gt;
&lt;br /&gt;
new const String:FULL_SOUND_PATH[] = &amp;quot;sound/custom/ur.mp3&amp;quot;;&lt;br /&gt;
new const String:RELATIVE_SOUND_PATH[] = &amp;quot;*/custom/ur.mp3&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd( &amp;quot;sm_testsound&amp;quot;, sm_testsound );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnMapStart()&lt;br /&gt;
{&lt;br /&gt;
	AddFileToDownloadsTable( FULL_SOUND_PATH );&lt;br /&gt;
	FakePrecacheSound( RELATIVE_SOUND_PATH );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:sm_testsound( client, argc )&lt;br /&gt;
{&lt;br /&gt;
	EmitSoundToClient( client, RELATIVE_SOUND_PATH );&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
stock FakePrecacheSound( const String:szPath[] )&lt;br /&gt;
{&lt;br /&gt;
	AddToStringTable( FindStringTable( &amp;quot;soundprecache&amp;quot; ), szPath );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternately, if you are going to have sounds in a plugin that supports multiple games, you should use the [https://forums.alliedmods.net/showthread.php?t=237045 EmitSoundAny snippets].&lt;br /&gt;
&lt;br /&gt;
''Recent reports indicate that this only works for '''mp3'''s and not '''wav'''s.''&lt;br /&gt;
&lt;br /&gt;
===Using the music directory===&lt;br /&gt;
By adding your custom sounds under sound/music/, they will automatically be streamed from disk. They can also be used just like sounds in any other game with regard to PrecacheSound (albeit not necessary to be beyond adding to soundprecache table) and EmitSound.&lt;br /&gt;
&lt;br /&gt;
This will have the side effect of your sounds volume being tied to the game's music volume, which many players turn down or off. For this reason, it's not recommended.&lt;br /&gt;
&lt;br /&gt;
=Max Players, Clients=&lt;br /&gt;
CS:GO has four different values that all affect the maximum number of players that can join a game. The lowest one of the four determines the value used.&lt;br /&gt;
&lt;br /&gt;
==The absolute maximum==&lt;br /&gt;
The current absolute maximum number of players for CS:GO, including GOTV is '''64'''. This is a compile-time maximum in the engine on both client and server and cannot be changed.&lt;br /&gt;
&lt;br /&gt;
(This is the maximum value as returned by IServerGameClients::GetPlayerLimits).&lt;br /&gt;
&lt;br /&gt;
==The engine's Maxclients==&lt;br /&gt;
This is the number known as gpGlobals-&amp;gt;maxClients in SM extensions or MM:S plugins and MaxClients in SourcePawn.&lt;br /&gt;
&lt;br /&gt;
It is able to be changed in other games, up to the maximum, by setting the -maxplayers command line parameter. As this doesn't exist in CS:GO, it acts like other games when not set and uses a hardcoded default, '''64''' for CS:GO.&lt;br /&gt;
&lt;br /&gt;
(This is the default value as returned by IServerGameClients::GetPlayerLimits).&lt;br /&gt;
&lt;br /&gt;
A gamemode's maxplayers can be overridden with the maxplayers parameter in the appropriate section of gamemodes_server.txt&lt;br /&gt;
&lt;br /&gt;
To override maxplayers for all gamemodes, use the -maxplayers_override command line parameter.  However, be aware that the game itself appears to enforce a 44 player maximum regardless of what you set this to.&lt;br /&gt;
&lt;br /&gt;
==The GameTypes maxplayers==&lt;br /&gt;
CS:GO has a new &amp;quot;GameTypes&amp;quot; system with it's own set of game mode and type -specifec params. There is more on this below, but it also includes a maxplayers value, also referred to as numSlots or MaxHumanPlayers in some areas.&lt;br /&gt;
&lt;br /&gt;
This is the value set in gamemodes.txt or overridden in gamemodes_server.txt or overridden by the new -maxplayer_override command line parameter.&lt;br /&gt;
&lt;br /&gt;
It is used for showing the maxplayers listed in the output of the status command as well as the max count used for the server browser (unless overridden by sv_visiblemaxplayers).&lt;br /&gt;
&lt;br /&gt;
This does not include the count set by the &amp;quot;extraspectators&amp;quot; value in the gamemodes.txt, but both maxplayers and extraspectators must be equal or less than the engine's Maxclients.&lt;br /&gt;
&lt;br /&gt;
The GameTypes max can be retrieved in SourcePawn with the new GetMaxHumanPlayers native or in C++ with IServerGameClients::GetMaxHumanPlayers.&lt;br /&gt;
&lt;br /&gt;
==Spawnpoint count==&lt;br /&gt;
Regardless of the above values, you're limited by the running map's number of spawnpoints, evenly split per team (30 for stock maps).&lt;br /&gt;
&lt;br /&gt;
You can use a mod like [http://www.bailopan.net/stripper/ Stripper:Source] or [https://forums.alliedmods.net/showthread.php?p=1054201 Spawn Tools 7] to add more spawnpoint entities.&lt;br /&gt;
&lt;br /&gt;
=Menus=&lt;br /&gt;
With SourceMod's &amp;quot;Radio-style&amp;quot; menus (ShowMenu / CHudMenu), the 0 key will cannot be detected due to the client never sending &amp;quot;menuselect 0&amp;quot;. SourceMod works around this by limiting menus to 9.&lt;br /&gt;
&lt;br /&gt;
The older &amp;quot;Valve-style&amp;quot; menus created by IServerPluginHelpers::CreateMessage with the DIALOG_MENU type aren't supported at all due at least to missing res file info on the client.&lt;br /&gt;
&lt;br /&gt;
=GameTypes / GameModes=&lt;br /&gt;
CS:GO uses a new &amp;quot;GameTypes&amp;quot; system for coordinating server mode and type info between the server, client, and matchmaking, as well as for handling some server rules.&lt;br /&gt;
&lt;br /&gt;
Some things handled by the GameTypes system:&lt;br /&gt;
*Game types&lt;br /&gt;
*Game modes&lt;br /&gt;
*Config to execute for each mode&lt;br /&gt;
*Weapon progression for applicable modes&lt;br /&gt;
*Maps and map order for each mode&lt;br /&gt;
*Maxplayers for each type and mode&lt;br /&gt;
*Player and view models for each map&lt;br /&gt;
*Bot difficulty&lt;br /&gt;
*ELO ranking data&lt;br /&gt;
&lt;br /&gt;
gamemodes.txt is the main data file (in KeyValues format) holding the backing info, with an optional gamemodes_server.txt being merged into it.&lt;br /&gt;
&lt;br /&gt;
The gamemodes data files should not be accessed directly, but rather through the IGameTypes interface. None of it is currently exposed in SourceMod but there are plans to add a new extension giving access to much of the data.&lt;br /&gt;
&lt;br /&gt;
In C++, you can easily get a pointer to the gametypes interface with CreateInterfaceFn from the matchmaking_ds binary, looking up VENGINE_GAMETYPES_VERSION.&lt;br /&gt;
&lt;br /&gt;
See: http://hg.alliedmods.net/hl2sdks/hl2sdk-csgo/file/tip/public/matchmaking/igametypes.h&lt;br /&gt;
&lt;br /&gt;
Example for getting the gametypes ptr in an SM extension, courtesy of Drifter's work-in-progress gametypes extension:&lt;br /&gt;
&amp;lt;cpp&amp;gt;&lt;br /&gt;
IGameTypes *g_pGameTypes;&lt;br /&gt;
&lt;br /&gt;
#define GET_V_IFACE_CURRENT_CUSTOM(v_factory, v_var, v_type, v_name) \&lt;br /&gt;
        v_var = (v_type *)g_SMAPI-&amp;gt;VInterfaceMatch(v_factory, v_name); \&lt;br /&gt;
        if (!v_var) \&lt;br /&gt;
        { \&lt;br /&gt;
                if (error &amp;amp;&amp;amp; maxlen) \&lt;br /&gt;
                { \&lt;br /&gt;
                        g_SMAPI-&amp;gt;Format(error, maxlen, &amp;quot;Could not find interface: %s&amp;quot;, v_name); \&lt;br /&gt;
                } \&lt;br /&gt;
                return false; \&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
	CreateInterfaceFn matchmakingDSFactory = NULL;&lt;br /&gt;
	ILibrary *mmlib;&lt;br /&gt;
	char path[PLATFORM_MAX_PATH];&lt;br /&gt;
&lt;br /&gt;
	libsys-&amp;gt;PathFormat(path, sizeof(path), &amp;quot;%s/bin/matchmaking_ds%s.%s&amp;quot;, g_SMAPI-&amp;gt;GetBaseDir(), MATCHMAKINGDS_SUFFIX, MATCHMAKINGDS_EXT);&lt;br /&gt;
&lt;br /&gt;
	if ((mmlib = libsys-&amp;gt;OpenLibrary(path, NULL, 0)))&lt;br /&gt;
	{&lt;br /&gt;
		matchmakingDSFactory = (CreateInterfaceFn)mmlib-&amp;gt;GetSymbolAddress(&amp;quot;CreateInterface&amp;quot;);&lt;br /&gt;
		mmlib-&amp;gt;CloseLibrary();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (!matchmakingDSFactory)&lt;br /&gt;
	{&lt;br /&gt;
		g_pSM-&amp;gt;Format(error, maxlen, &amp;quot;Failed to find matchmakingDS factory&amp;quot;);&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	GET_V_IFACE_CURRENT_CUSTOM(matchmakingDSFactory, g_pGameTypes, IGameTypes, VENGINE_GAMETYPES_VERSION);&lt;br /&gt;
&lt;br /&gt;
	if (!g_pGameTypes)&lt;br /&gt;
	{&lt;br /&gt;
		g_pSM-&amp;gt;Format(error, maxlen, &amp;quot;Failed to find IGameTypes ptr&amp;quot;);&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/cpp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Coding=&lt;br /&gt;
There are exceptionalities for some of the SourcePawn coding conventions in CS:GO.&lt;br /&gt;
&lt;br /&gt;
==Slaying players during player_hurt event==&lt;br /&gt;
CS:GO will crash if you attempt to call ForcePlayerSuicide() during a player_hurt callback. A timer can be used as a workaround.&lt;br /&gt;
&lt;br /&gt;
Example for slaying a team attacker:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	HookEvent(&amp;quot;player_hurt&amp;quot;, OnPlayerHurt);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public OnPlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
	new victim   = GetClientOfUserId(GetEventInt(event,&amp;quot;userid&amp;quot;));&lt;br /&gt;
	new attacker = GetClientOfUserId(GetEventInt(event,&amp;quot;attacker&amp;quot;));&lt;br /&gt;
	if(attacker &amp;gt; 0 &amp;amp;&amp;amp; attacker &amp;lt;= MaxClients &amp;amp;&amp;amp; IsPlayerAlive(attacker) &amp;amp;&amp;amp; GetClientTeam(attacker) == GetClientTeam(victim))&lt;br /&gt;
		CreateTimer(0.0, SlayTimer, attacker, TIMER_FLAG_NO_MAPCHANGE);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:SlayTimer(Handle:timer, any:client)&lt;br /&gt;
{&lt;br /&gt;
	ForcePlayerSuicide(client);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Assists / Score==&lt;br /&gt;
Assists and score are not named entity properties in CS:GO. To view or alter assists or displayed score, you can use the following natives new in SM 1.5.0-hg3706:&lt;br /&gt;
&lt;br /&gt;
CS_GetClientAssists&lt;br /&gt;
CS_SetClientAssists&lt;br /&gt;
CS_GetContributionScore&lt;br /&gt;
CS_SetContributionScore&lt;br /&gt;
&lt;br /&gt;
==Denoting Text-Colors==&lt;br /&gt;
CSGO has a few odd requirements for properly coloring text in chat.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
// \x01 is white.&lt;br /&gt;
// \x04 is green.&lt;br /&gt;
&lt;br /&gt;
//These examples set the first half of the string green, and the second half white.&lt;br /&gt;
&lt;br /&gt;
//Normal example:&lt;br /&gt;
new String:normalColoring = &amp;quot;\x04This half is green,\x01 This half is white.&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
//CSGO example:&lt;br /&gt;
new String:csgoColoring = &amp;quot; \x01\x0B\x04This half is green,\x01 This half is white.&amp;quot;;&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Specific Quirks:&lt;br /&gt;
* The string must start with a space.&lt;br /&gt;
* Following the space there must be a white color-indicator (\x01).&lt;br /&gt;
* Following the \x01, there must be a printable character. \x0B works great because it does not appear in the message.&lt;br /&gt;
* After all of these steps you can treat the rest of the string like normal.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=User_Messages&amp;diff=9055</id>
		<title>User Messages</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=User_Messages&amp;diff=9055"/>
		<updated>2013-09-02T17:30:07Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Added L4D2 user messages with parameter types of some of them&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is just a dump of some stuff for now, needs a complete revamp later.&lt;br /&gt;
Here's a topic on the subject as well: https://forums.alliedmods.net/showthread.php?t=80256 and here http://forums.alliedmods.net/showthread.php?t=52777&lt;br /&gt;
&lt;br /&gt;
=== Counter-Strike: Source User Messages ===&lt;br /&gt;
List obtained by using 'meta game' in the console&lt;br /&gt;
&lt;br /&gt;
  User Messages:  Name                              Index  Size &lt;br /&gt;
                  Geiger                            0      1    &lt;br /&gt;
                  Train                             1      1    &lt;br /&gt;
                  HudText                           2      -1   &lt;br /&gt;
                  SayText                           3      -1   &lt;br /&gt;
                  SayText2                          4      -1   &lt;br /&gt;
                  TextMsg                           5      -1   &lt;br /&gt;
                  HudMsg                            6      -1   &lt;br /&gt;
                  ResetHUD                          7      1    &lt;br /&gt;
                  GameTitle                         8      0    &lt;br /&gt;
                  ItemPickup                        9      -1   &lt;br /&gt;
                  ShowMenu                          10     -1   &lt;br /&gt;
                  Shake                             11     13   &lt;br /&gt;
                  Fade                              12     10   &lt;br /&gt;
                  VGUIMenu                          13     -1   &lt;br /&gt;
                  CloseCaption                      14     7    &lt;br /&gt;
                  SendAudio                         15     -1   &lt;br /&gt;
                  RawAudio                          16     -1   &lt;br /&gt;
                  VoiceMask                         17     17   &lt;br /&gt;
                  RequestState                      18     0    &lt;br /&gt;
                  BarTime                           19     -1   &lt;br /&gt;
                  Damage                            20     -1   &lt;br /&gt;
                  RadioText                         21     -1   &lt;br /&gt;
                  HintText                          22     -1   &lt;br /&gt;
                  ReloadEffect                      23     2    &lt;br /&gt;
                  PlayerAnimEvent                   24     -1   &lt;br /&gt;
                  AmmoDenied                        25     2    &lt;br /&gt;
                  UpdateRadar                       26     -1   &lt;br /&gt;
                  KillCam                           27     -1   &lt;br /&gt;
  28 user messages in total&lt;br /&gt;
&lt;br /&gt;
=== Left 4 Dead 2 User Messages ===&lt;br /&gt;
&lt;br /&gt;
For vote specific user messages see http://wiki.alliedmods.net/Left_4_Voting_2&lt;br /&gt;
&lt;br /&gt;
  User Messages:  Name                              Index  Size Parameter types&lt;br /&gt;
                  SayText2                          4      -1&lt;br /&gt;
                  TextMsg                           5      -1&lt;br /&gt;
                  HudMsg                            6      -1&lt;br /&gt;
                  ResetHUD                          7      1&lt;br /&gt;
                  GameTitle                         8      0&lt;br /&gt;
                  ItemPickup                        9      -1&lt;br /&gt;
                  ShowMenu                          10     -1&lt;br /&gt;
                  Shake                             11     13&lt;br /&gt;
                  Fade                              12     10&lt;br /&gt;
                  VGUIMenu                          13     -1	string, bool, string (keyvalues)&lt;br /&gt;
                  Rumble                            14     3&lt;br /&gt;
                  CloseCaption                      15     -1&lt;br /&gt;
                  CloseCaptionDirect                16     -1&lt;br /&gt;
                  SendAudio                         17     -1&lt;br /&gt;
                  RawAudio                          18     -1&lt;br /&gt;
                  VoiceMask                         19     9&lt;br /&gt;
                  RequestState                      20     0&lt;br /&gt;
                  BarTime                           21     -1&lt;br /&gt;
                  Damage                            22     -1&lt;br /&gt;
                  RadioText                         23     -1&lt;br /&gt;
                  HintText                          24     -1&lt;br /&gt;
                  KeyHintText                       25     -1&lt;br /&gt;
                  ReloadEffect                      26     4&lt;br /&gt;
                  PlayerAnimEvent                   27     -1&lt;br /&gt;
                  AmmoDenied                        28     2&lt;br /&gt;
                  UpdateRadar                       29     -1&lt;br /&gt;
                  KillCam                           30     -1&lt;br /&gt;
                  MarkAchievement                   31     -1&lt;br /&gt;
                  Splatter                          32     1&lt;br /&gt;
                  MeleeSlashSplatter                33     1&lt;br /&gt;
                  MeleeClubSplatter                 34     1&lt;br /&gt;
                  MudSplatter                       35     1&lt;br /&gt;
                  SplatterClear                     36     0&lt;br /&gt;
                  MessageText                       37     -1&lt;br /&gt;
                  TransitionRestore                 38     0&lt;br /&gt;
                  Spawn                             39     1&lt;br /&gt;
                  CreditsLine                       40     -1&lt;br /&gt;
                  CreditsMsg                        41     0&lt;br /&gt;
                  JoinLateMsg                       42     0&lt;br /&gt;
                  StatsCrawlMsg                     43     0&lt;br /&gt;
                  StatsSkipState                    44     2&lt;br /&gt;
                  ShowStats                         45     -1&lt;br /&gt;
                  BlurFade                          46     0&lt;br /&gt;
                  MusicCmd                          47     -1&lt;br /&gt;
                  WitchBloodSplatter                48     -1	vec[3]&lt;br /&gt;
                  AchievementEvent                  49     -1&lt;br /&gt;
                  PZDmgMsg                          50     -1&lt;br /&gt;
                  AllPlayersConnectedGameStarting   51     0&lt;br /&gt;
                  VoteRegistered                    52     1&lt;br /&gt;
                  DisconnectToLobby                 53     0	empty&lt;br /&gt;
                  CallVoteFailed                    54     1&lt;br /&gt;
                  SteamWeaponStatData               55     -1&lt;br /&gt;
                  CurrentTimescale                  56     4&lt;br /&gt;
                  DesiredTimescale                  57     16&lt;br /&gt;
                  PZEndGamePanelMsg                 58     1&lt;br /&gt;
                  PZEndGamePanelVoteRegisteredMsg   59     1&lt;br /&gt;
                  PZEndGameVoteStatsMsg             60     8&lt;br /&gt;
                  VoteStart                         61     -1&lt;br /&gt;
                  VotePass                          62     -1&lt;br /&gt;
                  VoteFail                          63     1&lt;br /&gt;
&lt;br /&gt;
=== Fade Flags ===&lt;br /&gt;
These may not be correct...&lt;br /&gt;
&lt;br /&gt;
 FFADE_IN            0x0001        // Just here so we don't pass 0 into the function&lt;br /&gt;
 FFADE_OUT           0x0002        // Fade out (not in)&lt;br /&gt;
 FFADE_MODULATE      0x0004        // Modulate (don't blend)&lt;br /&gt;
 FFADE_STAYOUT       0x0008        // ignores the duration, stays faded out until new ScreenFade message received&lt;br /&gt;
 FFADE_PURGE         0x0010        // Purges all other fades, replacing them with this one&lt;br /&gt;
&lt;br /&gt;
=== Fade Function ===&lt;br /&gt;
Example Fade function (be sure to define the Fade Flags!)&lt;br /&gt;
&lt;br /&gt;
This Fades the clients screen to a specified color, and stays until you reset the color to {0,0,0,0}&lt;br /&gt;
&lt;br /&gt;
To modify it to Fade the screen for a certain amount of time, remove the STAYOUT flag, and pass a value to &amp;quot;fade &amp;amp; hold&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 PerformFade(target, 500, {0, 128, 255, 51})&lt;br /&gt;
&lt;br /&gt;
 PerformFade(client, duration, const color[4]) {&lt;br /&gt;
 	new Handle:hFadeClient=StartMessageOne(&amp;quot;Fade&amp;quot;,client)&lt;br /&gt;
 	BfWriteShort(hFadeClient,duration)	// FIXED 16 bit, with SCREENFADE_FRACBITS fractional, milliseconds duration&lt;br /&gt;
 	BfWriteShort(hFadeClient,0)		// FIXED 16 bit, with SCREENFADE_FRACBITS fractional, milliseconds duration until reset (fade &amp;amp; hold)&lt;br /&gt;
 	BfWriteShort(hFadeClient,(FFADE_PURGE|FFADE_OUT|FFADE_STAYOUT)) // fade type (in / out)&lt;br /&gt;
 	BfWriteByte(hFadeClient,color[0])	// fade red&lt;br /&gt;
 	BfWriteByte(hFadeClient,color[1])	// fade green&lt;br /&gt;
 	BfWriteByte(hFadeClient,color[2])	// fade blue&lt;br /&gt;
 	BfWriteByte(hFadeClient,color[3])	// fade alpha&lt;br /&gt;
 	EndMessage()&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== HudMsg Function ===&lt;br /&gt;
This does not work in CS:S.&lt;br /&gt;
&lt;br /&gt;
This Draws a text Message to a specified players screen. This is just for educational purposes and there is a much easier way of doing this with native functions here: http://docs.sourcemod.net/api/index.php?fastload=show&amp;amp;id=846&amp;amp; &amp;amp; http://docs.sourcemod.net/api/index.php?fastload=show&amp;amp;id=842&amp;amp;&lt;br /&gt;
&lt;br /&gt;
 PerformHudMsg(client, &amp;quot;This is a Test&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
 PerformHudMsg(client, const String:szMsg[]) {&lt;br /&gt;
 	new Handle:hBf = StartMessageOne(&amp;quot;HudMsg&amp;quot;, client)&lt;br /&gt;
 	BfWriteByte(hBf, 3) //channel&lt;br /&gt;
 	BfWriteFloat(hBf, 0.0); // x ( -1 = center )&lt;br /&gt;
 	BfWriteFloat(hBf, -1); // y ( -1 = center )&lt;br /&gt;
 	// second color&lt;br /&gt;
 	BfWriteByte(hBf, 0); //r1&lt;br /&gt;
 	BfWriteByte(hBf, 0); //g1&lt;br /&gt;
 	BfWriteByte(hBf, 255); //b1&lt;br /&gt;
 	BfWriteByte(hBf, 255); //a1 // transparent?&lt;br /&gt;
 	// init color&lt;br /&gt;
 	BfWriteByte(hBf, 255); //r2&lt;br /&gt;
 	BfWriteByte(hBf, 0); //g2&lt;br /&gt;
 	BfWriteByte(hBf, 0); //b2&lt;br /&gt;
 	BfWriteByte(hBf, 255); //a2&lt;br /&gt;
 	BfWriteByte(hBf, 0); //effect (0 is fade in/fade out; 1 is flickery credits; 2 is write out)&lt;br /&gt;
 	BfWriteFloat(hBf, 1.0); //fadeinTime (message fade in time - per character in effect 2)&lt;br /&gt;
 	BfWriteFloat(hBf, 1.0); //fadeoutTime&lt;br /&gt;
 	BfWriteFloat(hBf, 15.0); //holdtime&lt;br /&gt;
 	BfWriteFloat(hBf, 5.0); //fxtime (effect type(2) used)&lt;br /&gt;
 	BfWriteString(hBf, szMsg); //Message&lt;br /&gt;
 	EndMessage();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== HookUserMessage Fade ===&lt;br /&gt;
Some sample code to hook a UserMessage, In this case Fade.&lt;br /&gt;
You cannot send other UserMessages inside of a UserMessage Hook. Many simple functions such as PrintToChat call UserMessages.&lt;br /&gt;
&lt;br /&gt;
 public OnPluginStart() {&lt;br /&gt;
     HookUserMessage(GetUserMessageId(&amp;quot;Fade&amp;quot;),HookFade,true)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 public Action:HookFade(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init) {&lt;br /&gt;
 	new duration = BfReadShort(bf)&lt;br /&gt;
 	new holdtime = BfReadShort(bf)&lt;br /&gt;
 	BfReadShort(bf) 	//You must read all of the bf values, even If you only want the last value such as this one.&lt;br /&gt;
 	new r = BfReadByte(bf) 	//You do NOT need to store their value though.&lt;br /&gt;
 	new g = BfReadByte(bf)&lt;br /&gt;
 	new b = BfReadByte(bf)&lt;br /&gt;
 	new a = BfReadByte(bf)&lt;br /&gt;
 	&lt;br /&gt;
 	PrintToServer(&amp;quot;Duration: %i, HoldTime: %i, rgba: %i %i %i %i&amp;quot;,duration,holdtime,r,g,b,a)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Generic_Source_Server_Events&amp;diff=8972</id>
		<title>Generic Source Server Events</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Generic_Source_Server_Events&amp;diff=8972"/>
		<updated>2013-08-01T17:17:43Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Pretty sure it's not &amp;quot;server_meSSSage&amp;quot; either :p&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;:''Refer back to [[Game Events (Source)]] for more events.''&lt;br /&gt;
&lt;br /&gt;
These '''should''' apply to all Source Engine Servers&lt;br /&gt;
=== server_spawn ===&lt;br /&gt;
{{qnotice|Send once a server starts (spawns)}}&amp;lt;br&amp;gt;&lt;br /&gt;
{{begin-hl2msg|server_spawn|string}}&lt;br /&gt;
{{hl2msg|string|hostname|public host name}}&lt;br /&gt;
{{hl2msg|string|address|hostame, IP or DNS name}}&lt;br /&gt;
{{hl2msg|string|port|server port}}&lt;br /&gt;
{{hl2msg|string|game|game dir}}&lt;br /&gt;
{{hl2msg|string|mapname|map name}}&lt;br /&gt;
{{hl2msg|long|maxplayers|max players}}&lt;br /&gt;
{{hl2msg|string|os|WIN32, LINUX}}&lt;br /&gt;
{{hl2msg|bool|dedicated|true if dedicated server}}&lt;br /&gt;
{{hl2msg|bool|password|true if password protected}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== server_shutdown ===&lt;br /&gt;
{{qnotice|Server shut down}}&lt;br /&gt;
{{begin-hl2msg|server_shutdown|string}}&lt;br /&gt;
{{hl2msg|string|reason|reason why server was shut down}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== server_cvar ===&lt;br /&gt;
{{qnotice|A server console var has changed}}&lt;br /&gt;
{{begin-hl2msg|server_cvar|string}}&lt;br /&gt;
{{hl2msg|string|cvarname|cvar name, eg &amp;quot;mp_roundtime&amp;quot;}}&lt;br /&gt;
{{hl2msg|string|cvarvalue|new cvar value}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== server_message ===&lt;br /&gt;
{{qnotice|A generic server message}}&lt;br /&gt;
{{begin-hl2msg|server_message|string}}&lt;br /&gt;
{{hl2msg|string|text|the message text}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== server_addban ===&lt;br /&gt;
{{qnotice|When the server has a ban added}}&lt;br /&gt;
{{begin-hl2msg|server_addban|string}}&lt;br /&gt;
{{hl2msg|string|name|player name}}&lt;br /&gt;
{{hl2msg|string|userid|user ID on server}}&lt;br /&gt;
{{hl2msg|string|networkid|player network (i.e steam) id}}&lt;br /&gt;
{{hl2msg|string|ip|IP address}}&lt;br /&gt;
{{hl2msg|string|duration|length of the ban}}&lt;br /&gt;
{{hl2msg|string|by|banned by...}}&lt;br /&gt;
{{hl2msg|bool|kicked|whether the player was also kicked}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== server_removeban ===&lt;br /&gt;
{{qnotice|When the server has a ban removed}}&lt;br /&gt;
{{begin-hl2msg|server_removeban|string}}&lt;br /&gt;
{{hl2msg|string|networkid|player network (i.e steam) id}}&lt;br /&gt;
{{hl2msg|string|ip|IP address}}&lt;br /&gt;
{{hl2msg|string|by|removed by...}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== player_connect ===&lt;br /&gt;
{{qnotice|A new client connected}}&lt;br /&gt;
{{begin-hl2msg|player_connect|string}}&lt;br /&gt;
{{hl2msg|string|name|player name}}&lt;br /&gt;
{{hl2msg|byte|index|player slot (entity index-1)}}&lt;br /&gt;
{{hl2msg|short|userid|user ID on server (unique on server)}}&lt;br /&gt;
{{hl2msg|string|networkid|player network (i.e steam) id}}&lt;br /&gt;
{{hl2msg|string|address|ip:port}}&lt;br /&gt;
{{hl2msg|short|bot|is a bot}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== player_info ===&lt;br /&gt;
{{qnotice|A player changed his name}}&lt;br /&gt;
{{begin-hl2msg|player_info|string}}&lt;br /&gt;
{{hl2msg|string|name|player name}}&lt;br /&gt;
{{hl2msg|byte|index|player slot (entity index-1)}}&lt;br /&gt;
{{hl2msg|short|userid|user ID on server (unique on server)}}&lt;br /&gt;
{{hl2msg|string|networkid|player network (i.e steam) id}}&lt;br /&gt;
{{hl2msg|bool|bot|true if player is a AI bot}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== player_disconnect ===&lt;br /&gt;
{{qnotice|A client was disconnected}}&lt;br /&gt;
{{begin-hl2msg|player_disconnect|string}}&lt;br /&gt;
{{hl2msg|short|userid|user ID on server}}&lt;br /&gt;
{{hl2msg|string|reason|&amp;quot;self&amp;quot;, &amp;quot;kick&amp;quot;, &amp;quot;ban&amp;quot;, &amp;quot;cheat&amp;quot;, &amp;quot;error&amp;quot;}}&lt;br /&gt;
{{hl2msg|string|name|player name}}&lt;br /&gt;
{{hl2msg|string|networkid|player network (i.e steam) id}}&lt;br /&gt;
{{hl2msg|short|bot|is a bot}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== player_activate ===&lt;br /&gt;
{{qnotice|A client has entered the game (connected and loaded)}}&lt;br /&gt;
{{begin-hl2msg|player_activate|string}}&lt;br /&gt;
{{hl2msg|short|userid|user ID on server}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
=== player_say ===&lt;br /&gt;
{{qnotice|When a client sends a message in chat}}&lt;br /&gt;
{{begin-hl2msg|player_say|string}}&lt;br /&gt;
{{hl2msg|short|userid|user ID on server}}&lt;br /&gt;
{{hl2msg|string|text|the say text}}&lt;br /&gt;
{{end-hl2msg}}&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)&amp;diff=8966</id>
		<title>SQL (SourceMod Scripting)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)&amp;diff=8966"/>
		<updated>2013-07-22T16:32:23Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: API says &amp;quot;SQL_ConnectEx&amp;quot; is deprecated and that you should use &amp;quot;SQL_ConnectCustom&amp;quot; instead.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This article is an introduction to using SourceMod's SQL features.  It is not an introduction or tutorial about SQL or any specific SQL implementation.&lt;br /&gt;
&lt;br /&gt;
SourceMod's SQL layer is formally called ''DBI'', or the '''D'''ata'''b'''ase '''I'''nterface.  The interface is a generic abstraction of common SQL functions.  To connect to a specific database implementation (such as MySQL, or sqLite), a SourceMod DBI &amp;quot;driver&amp;quot; must be loaded.  Currently, there are drivers for MySQL and SQLite&lt;br /&gt;
&lt;br /&gt;
SourceMod automatically detects and loads drivers on demand (if they exist, of course).  All database natives are in &amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;.  The C++ API is in &amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Connecting=&lt;br /&gt;
There are two ways to connect to a database.  The first is through named configurations.  Named configurations are preset configurations listed in &amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;.  SourceMod specifies that if SQL is being used, there should always be one configuration named &amp;quot;default.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
An example of such a configuration looks like:&lt;br /&gt;
&amp;lt;pre&amp;gt;	&amp;quot;default&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;host&amp;quot;				&amp;quot;localhost&amp;quot;&lt;br /&gt;
		&amp;quot;database&amp;quot;			&amp;quot;sourcemod&amp;quot;&lt;br /&gt;
		&amp;quot;user&amp;quot;				&amp;quot;root&amp;quot;&lt;br /&gt;
		&amp;quot;pass&amp;quot;				&amp;quot;&amp;quot;&lt;br /&gt;
		//&amp;quot;timeout&amp;quot;			&amp;quot;0&amp;quot;&lt;br /&gt;
		//&amp;quot;port&amp;quot;			&amp;quot;0&amp;quot;&lt;br /&gt;
	}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Connections based on named configurations can be instantiated with either &amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
The other option is to use &amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt; and manually specify all connection parameters by passing a keyvalue handle containing them.&lt;br /&gt;
&lt;br /&gt;
Example of a typical connection:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new String:error[255]&lt;br /&gt;
new Handle:db = SQL_DefConnect(error, sizeof(error))&lt;br /&gt;
&lt;br /&gt;
if (db == INVALID_HANDLE)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error)&lt;br /&gt;
} else {&lt;br /&gt;
	CloseHandle(db)&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Queries=&lt;br /&gt;
&lt;br /&gt;
==No Results==&lt;br /&gt;
The simplest queries are ones which do not return results -- for example, CREATE, DROP, UPDATE, INSERT, and DELETE.  For such queries it is recommended to use &amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;.  The name does not imply that the query will be faster, but rather, it is faster to write code using this function.  For example, given that &amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; is a valid database Handle:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;if (!SQL_FastQuery(db, &amp;quot;UPDATE stats SET players = players + 1&amp;quot;))&lt;br /&gt;
{&lt;br /&gt;
	new String:error[255]&lt;br /&gt;
	SQL_GetError(db, error, sizeof(error))&lt;br /&gt;
	PrintToServer(&amp;quot;Failed to query (error: %s)&amp;quot;, error)&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
If a query returns a result set and the results must be processed, you must use &amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;.  Unlike &amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;, this function returns a Handle which must be closed.&lt;br /&gt;
&lt;br /&gt;
An example of a query which will produce results is:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;)&lt;br /&gt;
if (query == INVALID_HANDLE)&lt;br /&gt;
{&lt;br /&gt;
	new String:error[255]&lt;br /&gt;
	SQL_GetError(db, error, sizeof(error))&lt;br /&gt;
	PrintToServer(&amp;quot;Failed to query (error: %s)&amp;quot;, error)&lt;br /&gt;
} else {&lt;br /&gt;
	/* Process results here!&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* Free the Handle */&lt;br /&gt;
	CloseHandle(query)&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Prepared Statements=&lt;br /&gt;
Prepared statements are another method of querying.  The idea behind a prepared statement is that you construct a query &amp;quot;template&amp;quot; once, and re-use it as many times as needed.  Prepared statements have the following benefits:&lt;br /&gt;
*A good SQL implementation will be able to cache the query better if it is a prepared statement.&lt;br /&gt;
*You don't have to rebuild the entire query string every execution.&lt;br /&gt;
*You don't have to allocate a new query structure on every execution.&lt;br /&gt;
*Input is &amp;quot;always&amp;quot; secure (more on this later).&lt;br /&gt;
&lt;br /&gt;
A prepared statement has &amp;quot;markers&amp;quot; for inputs.  For example, let's consider a function that takes in a database Handle and a name, and retrieves some info from a table:&lt;br /&gt;
&amp;lt;pawn&amp;gt;GetSomeInfo(Handle:db, const String:name[])&lt;br /&gt;
{&lt;br /&gt;
	new Handle:hQuery&lt;br /&gt;
	new String:query[100]&lt;br /&gt;
&lt;br /&gt;
	/* Create enough space to make sure our string is quoted properly  */&lt;br /&gt;
	new buffer_len = strlen(name) * 2 + 1&lt;br /&gt;
	new String:new_name[buffer_len]&lt;br /&gt;
&lt;br /&gt;
	/* Ask the SQL driver to make sure our string is safely quoted */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len)&lt;br /&gt;
&lt;br /&gt;
	/* Build the query */&lt;br /&gt;
	Format(query, sizeof(query), &amp;quot;SELECT userid FROM vb_user WHERE username = '%s'&amp;quot;, new_name)&lt;br /&gt;
	&lt;br /&gt;
	/* Execute the query */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		return 0&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Get some info here&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(hQuery)&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Observe a version with prepared statements:&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:hUserStmt = INVALID_HANDLE;&lt;br /&gt;
GetSomeInfo(Handle:db, const String:name[])&lt;br /&gt;
{&lt;br /&gt;
	/* Check if we haven't already created the statement */&lt;br /&gt;
	if (hUserStmt == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		new String:error[255]&lt;br /&gt;
		hUserStmt = SQL_PrepareQuery(db, &amp;quot;SELECT userid FROM vb_user WHERE username = ?&amp;quot;, error, sizeof(error));&lt;br /&gt;
		if (hUserStmt == INVALID_HANDLE)&lt;br /&gt;
		{&lt;br /&gt;
			return 0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SQL_BindParamString(hUserStmt, 0, name, false);&lt;br /&gt;
	if (!SQL_Execute(hUserStmt))&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * Get some info here&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The important differences:&lt;br /&gt;
*The input string (&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;) did not need to be backticked (quoted).  The SQL engine automatically performs all type safety and insertion checks.&lt;br /&gt;
*There was no need for quotation marks around the parameter marker, &amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;, even though it accepted a string.&lt;br /&gt;
*We only needed to create the statement Handle once; after that it can live for the lifetime of the database connection.&lt;br /&gt;
&lt;br /&gt;
=Processing Results=&lt;br /&gt;
Processing results is done in the same manner for both normal queries and prepared statements.  The important functions are:&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - Returns the number of rows.&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - Fetches the next row if one is available.&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - Fetches data from a field in the current row.&lt;br /&gt;
&lt;br /&gt;
Let's consider a sample table that looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;CREATE TABLE users (&lt;br /&gt;
	name VARCHAR(64) NOT NULL PRIMARY KEY,&lt;br /&gt;
	age INT UNSIGNED NOT NULL&lt;br /&gt;
	);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following example has code that will print out all users matching a certain age.  There is an example for both prepared statements and normal queries.&lt;br /&gt;
&amp;lt;pawn&amp;gt;PrintResults(Handle:query)&lt;br /&gt;
{&lt;br /&gt;
	/* Even if we have just one row, you must call SQL_FetchRow() first */&lt;br /&gt;
	new String:name[65]&lt;br /&gt;
	while (SQL_FetchRow(query))&lt;br /&gt;
	{&lt;br /&gt;
		SQL_FetchString(query, 0, name, sizeof(name))&lt;br /&gt;
		PrintToServer(&amp;quot;Name \&amp;quot;%s\&amp;quot; was found.&amp;quot;, name)&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
bool:GetByAge_Query(Handle:db, age)&lt;br /&gt;
{&lt;br /&gt;
	new String:query[100]&lt;br /&gt;
	Format(query, sizeof(query), &amp;quot;SELECT name FROM users WHERE age = %d&amp;quot;, age)&lt;br /&gt;
&lt;br /&gt;
	new Handle:hQuery = SQL_Query(db, query)&lt;br /&gt;
	if (hQuery == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		return false&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	PrintResults(hQuery)&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(hQuery)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
new Handle:hAgeStmt = INVALID_HANDLE&lt;br /&gt;
bool:GetByAge_Statement(Handle:db, age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		new String:error[255]&lt;br /&gt;
		if ((hAgeStmt = SQL_PrepareQuery(db, &lt;br /&gt;
			&amp;quot;SELECT name FROM users WHERE age = ?&amp;quot;, &lt;br /&gt;
			error, &lt;br /&gt;
			sizeof(error))) &lt;br /&gt;
		     == INVALID_HANDLE)&lt;br /&gt;
		{&lt;br /&gt;
			return false&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SQL_BindParamInt(hAgeStmt, 0, age)&lt;br /&gt;
	if (!SQL_Execute(hAgeStmt))&lt;br /&gt;
	{&lt;br /&gt;
		return false&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	PrintResults(hAgeStmt)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that these examples did not close the statement Handles.  These examples assume a global database instance that is only closed when the plugin is unloaded.  For plugins which maintain temporary database connections, prepared statement Handles must be freed or else the database connection will never be closed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Threading=&lt;br /&gt;
SourceMod supports threaded SQL querying.  That is, SQL operations can be completed in a separate thread from main gameplay.  If your database server is remote or requires a network connection, queries can cause noticeable gameplay lag, and supporting threading is often a good idea if your queries occur in the middle of gameplay.&lt;br /&gt;
&lt;br /&gt;
Threaded queries are ''asynchronous''.  That is, they are dispatched and you can only find the results through a callback.  Although the callback is guaranteed to fire eventually, it may not fire in any specific given timeframe.  Certain drivers may not support threading; if this is the case, an RTE will be thrown.  If the threader cannot start or the threader is currently disabled, SourceMod will transparently execute the query in the main thread as a fallback.&lt;br /&gt;
&lt;br /&gt;
==Operations==&lt;br /&gt;
All threaded operations use the same callback for result notification: &amp;lt;tt&amp;gt;SQLTCallback&amp;lt;/tt&amp;gt;.  The parameters are:&lt;br /&gt;
*&amp;lt;tt&amp;gt;owner&amp;lt;/tt&amp;gt; - The &amp;quot;owner&amp;quot; of the operation.  For a query, this is a database.  For a database, this is a driver.  If the owner was not found or was invalidated, &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt; is passed.&lt;br /&gt;
*&amp;lt;tt&amp;gt;hndl&amp;lt;/tt&amp;gt; - The object being requested.  The value is defined by the SQL operation.&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - An error string.&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - Custom data that can be passed via some SQL operations.&lt;br /&gt;
&lt;br /&gt;
The following operations can be done via threads:&lt;br /&gt;
*&amp;lt;b&amp;gt;Connection&amp;lt;/b&amp;gt;, via &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;.&lt;br /&gt;
*&amp;lt;b&amp;gt;Querying&amp;lt;/b&amp;gt;, via &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
*''Note: prepared statements are not yet available for the threader.''&lt;br /&gt;
&lt;br /&gt;
It is always safe to chain one operation from another.&lt;br /&gt;
&lt;br /&gt;
===Connecting===&lt;br /&gt;
It is not necessary to make a threaded connection in order to make threaded queries.  However, creating a threaded connection will not pause the game server if a connection cannot be immediately established.  &lt;br /&gt;
&lt;br /&gt;
The following parameters are used for the threaded connection callback:&lt;br /&gt;
*&amp;lt;tt&amp;gt;owner&amp;lt;/tt&amp;gt;: A Handle to the driver, or &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt; if it could not be found.&lt;br /&gt;
*&amp;lt;tt&amp;gt;hndl&amp;lt;/tt&amp;gt;: A Handle to the database, or &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt; if the connection failed.&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: The error string, if any.&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: Unused (0)&lt;br /&gt;
&lt;br /&gt;
Example: &lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:hDatabase = INVALID_HANDLE;&lt;br /&gt;
&lt;br /&gt;
StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	SQL_TConnect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public GotDatabase(Handle:owner, Handle:hndl, const String:error[], any:data)&lt;br /&gt;
{&lt;br /&gt;
	if (hndl == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} else {&lt;br /&gt;
		hDatabase = hndl;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Querying===&lt;br /&gt;
Threaded queries can be performed on any database Handle as long as the driver supports threading.  All query results for the first result set are retrieved while in the thread.  If your query returns more than one set of results (for example, &amp;lt;tt&amp;gt;CALL&amp;lt;/tt&amp;gt; on MySQL with certain functions), the behaviour of the threader is undefined at this time.  &amp;lt;b&amp;gt;Note that if you want to perform both threaded and non-threaded queries on the same connection, you MUST read the &amp;quot;Locking&amp;quot; section below.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Query operations use the following callback parameters:&lt;br /&gt;
*&amp;lt;tt&amp;gt;owner&amp;lt;/tt&amp;gt;: A Handle to the database used to query.  The Handle is not the same as the Handle originally passed; however, it will test positively with &amp;lt;tt&amp;gt;SQL_IsSameConnection&amp;lt;/tt&amp;gt;.  The Handle can be cloned but it cannot be closed (it is closed automatically).  It may be &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt; in the case of a serious error (for example, the driver being unloaded).&lt;br /&gt;
*&amp;lt;tt&amp;gt;hndl&amp;lt;/tt&amp;gt;: A Handle to the query.  It can be cloned, but not closed (it is closed automatically).  It may be &amp;lt;tt&amp;gt;INVALID_HANDLE&amp;lt;/tt&amp;gt; if there was an error.&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: Error string, if any.&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: Optional user-defined data passed in through &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Example, continuing from above:&lt;br /&gt;
&amp;lt;pawn&amp;gt;CheckSteamID(userid, const String:auth[])&lt;br /&gt;
{&lt;br /&gt;
	decl String:query[255];&lt;br /&gt;
	Format(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	SQL_TQuery(hdatabase, T_CheckSteamID, query, userid)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public T_CheckSteamID(Handle:owner, Handle:hndl, const String:error[], any:data)&lt;br /&gt;
{&lt;br /&gt;
	new client;&lt;br /&gt;
&lt;br /&gt;
	/* Make sure the client didn't disconnect while the thread was running */&lt;br /&gt;
	if ((client = GetClientOfUserId(data)) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		return;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (hndl == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Query failed! %s&amp;quot;, error);&lt;br /&gt;
		KickClient(client, &amp;quot;Authorization failed&amp;quot;);&lt;br /&gt;
	} else if (!SQL_GetRowCount(hndl)) {&lt;br /&gt;
		KickClient(client, &amp;quot;You are not a member&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Locking==&lt;br /&gt;
It is possible to run both threaded and non-threaded queries on the same connection.  However, without the proper precautions, you could corrupt the network stream (even if it's local), corrupt memory, or otherwise cause a crash in the SQL driver.  To solve this, SourceMod has ''database locking''.  Locking is done via &amp;lt;tt&amp;gt;SQL_LockDatabase()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;SQL_UnlockDatabase&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Whenever performing any of the following non-threaded operations on a database, it is absolutely necessary to enclose the entire operation with a lock:&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt; (and &amp;lt;tt&amp;gt;SQL_FetchMoreResults&amp;lt;/tt&amp;gt; pairings)&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FastQuery&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_PrepareQuery&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Bind*&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;SQL_Execute&amp;lt;/tt&amp;gt; pairings&lt;br /&gt;
&lt;br /&gt;
The rule of thumb is: if your operation is going to use the database connection, it must be locked until the operation is fully completed.  &lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pawn&amp;gt;bool:GetByAge_Query(Handle:db, age)&lt;br /&gt;
{&lt;br /&gt;
	new String:query[100]&lt;br /&gt;
	Format(query, sizeof(query), &amp;quot;SELECT name FROM users WHERE age = %d&amp;quot;, age)&lt;br /&gt;
&lt;br /&gt;
	SQL_LockDatabase(db);&lt;br /&gt;
	new Handle:hQuery = SQL_Query(db, query)&lt;br /&gt;
	if (hQuery == INVALID_HANDLE)&lt;br /&gt;
	{&lt;br /&gt;
		SQL_UnlockDatabase(db);&lt;br /&gt;
		return false&lt;br /&gt;
	}&lt;br /&gt;
	SQL_UnlockDatabase(db);&lt;br /&gt;
&lt;br /&gt;
	PrintResults(hQuery)&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(hQuery)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that it was only necessary to lock the query; SourceMod pre-fetches the result set, and thus the network queue is clean.&lt;br /&gt;
&lt;br /&gt;
===Warnings===&lt;br /&gt;
*&amp;lt;b&amp;gt;Never&amp;lt;/b&amp;gt; call &amp;lt;tt&amp;gt;SQL_LockDatabase&amp;lt;/tt&amp;gt; right before a threaded operation.  You will deadlock the server and have to terminate/kill it.  &lt;br /&gt;
*&amp;lt;b&amp;gt;Always pair every Lock with an Unlock.&amp;lt;/b&amp;gt;  Otherwise you risk a deadlock.&lt;br /&gt;
*If your query returns multiple result sets, for example, a procedure call on MySQL that returns results, you must lock both the query and the entire fetch operation.  SourceMod is only able to fetch one result set at a time, and all result sets must be cleared before a new query is started.&lt;br /&gt;
&lt;br /&gt;
==Priority==&lt;br /&gt;
Threaded SQL operations are placed in a simple priority queue.  The priority levels are &amp;lt;tt&amp;gt;High&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;Medium&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;Low&amp;lt;/tt&amp;gt;.  Connections always have the highest priority.  &lt;br /&gt;
&lt;br /&gt;
Changing the priority can be useful if you have many queries with different purposes.  For example, a statistics plugin might execute 10 queries on death, and one query on join.  Because the statistics might rely on the join info, the join query might need to be high priority, while the death queries can be low priority.&lt;br /&gt;
&lt;br /&gt;
You should &amp;lt;b&amp;gt;never&amp;lt;/b&amp;gt; simply assign a high priority to all of your queries simply because you want them to get done fast.  Not only does it not work that way, but you may be inserting subtle problems into other plugins by being greedy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=SQLite=&lt;br /&gt;
==Introduction==&lt;br /&gt;
[http://www.sqlite.org/ SQLite] is a fast local-file SQL database engine and SourceMod provides a DBI driver for it.  SQLite differs from MySQL, and thus MySQL queries may not work in SQLite.  The driver type for connections is &amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Usage==&lt;br /&gt;
Since SQLite is local only, most of the connection parameters can be ignored.  The only connection parameter required is &amp;lt;tt&amp;gt;database&amp;lt;/tt&amp;gt;, which specifies the file name of the database.  Databases are created on demand if they do not already exist, and are stored in &amp;lt;tt&amp;gt;addons/sourcemod/data/sqlite&amp;lt;/tt&amp;gt;.  An extension of &amp;quot;.sq3&amp;quot; is automatically appended to the file name.&lt;br /&gt;
&lt;br /&gt;
Additionally, you can specify sub-folders in the database name.  For example, &amp;quot;cssdm/players&amp;quot; will become &amp;lt;tt&amp;gt;addons/sourcemod/data/sqlite/cssdm/players.sq3&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
SQLite supports the threading layer, and requires all of the same rules as the MySQL driver (including locks on shared connections).&lt;br /&gt;
&lt;br /&gt;
==External Links==&lt;br /&gt;
*[http://www.sqlite.org SQLite Homepage]&lt;br /&gt;
*[http://sqlitebrowser.sourceforge.net/ SQLite Browser]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Commands_(SourceMod_Scripting)&amp;diff=8957</id>
		<title>Commands (SourceMod Scripting)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Commands_(SourceMod_Scripting)&amp;diff=8957"/>
		<updated>2013-06-15T00:21:56Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Again, adding &amp;quot;Plugin_Handled&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[SourceMod]] allows you to create console commands similar to [[AMX Mod X]].  There are two main types of console commands:&lt;br /&gt;
*'''Server Commands''' - Fired from one of the following input methods:&lt;br /&gt;
**the server console itself&lt;br /&gt;
**the remote console (RCON)&lt;br /&gt;
**the ServerCommand() function, either from SourceMod or the [[Half-Life 2]] engine&lt;br /&gt;
*'''Console Commands''' - Fired from one of the following input methods:&lt;br /&gt;
**a client's console&lt;br /&gt;
**any of the server command input methods&lt;br /&gt;
&lt;br /&gt;
For server commands, there is no client index.  For console/client commands, there is a client index, but it may be 0 to indicate that the command is from the server.&lt;br /&gt;
&lt;br /&gt;
Note that button-bound &amp;quot;commands,&amp;quot; such as +attack and +duck, are not actually console commands.  They are a separate command stream sent over the network, as they need to be tied to a specific frame.  &lt;br /&gt;
&lt;br /&gt;
=Server Commands=&lt;br /&gt;
As noted above, server commands are fired through the server console, whether remote, local, or through the Source engine.  There is no client index associated with a server command.  &lt;br /&gt;
&lt;br /&gt;
Server commands are registered through the &amp;lt;tt&amp;gt;RegServerCmd()&amp;lt;/tt&amp;gt; function defined in &amp;lt;tt&amp;gt;console.inc&amp;lt;/tt&amp;gt;.  When registering a server command, you may be hooking an already existing command, and thus the return value is important.&lt;br /&gt;
*&amp;lt;b&amp;gt;&amp;lt;tt&amp;gt;Plugin_Continue&amp;lt;/tt&amp;gt;&amp;lt;/b&amp;gt; - The original server command will be processed, if there was one.  If the server command was created by a plugin, this has no effect.&lt;br /&gt;
*&amp;lt;b&amp;gt;&amp;lt;tt&amp;gt;Plugin_Handled&amp;lt;/tt&amp;gt;&amp;lt;/b&amp;gt; - The original server command will not be processed, if there was one.  If the server command was created by a plugin, this has no effect.&lt;br /&gt;
*&amp;lt;b&amp;gt;&amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt;&amp;lt;/b&amp;gt; - The original server command will not be processed, if there was one.  Additionally, no further hooks will be called for this command until it is fired again.&lt;br /&gt;
&lt;br /&gt;
==Adding Server Commands==&lt;br /&gt;
Let's say we want to add a test command to show how Half-Life 2 breaks down command arguments.  A possible implementation might be&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegServerCmd(&amp;quot;test_command&amp;quot;, Command_Test);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Test(args)&lt;br /&gt;
{&lt;br /&gt;
	new String:arg[128];&lt;br /&gt;
	new String:full[256];&lt;br /&gt;
&lt;br /&gt;
	GetCmdArgString(full, sizeof(full));&lt;br /&gt;
&lt;br /&gt;
	PrintToServer(&amp;quot;Argument string: %s&amp;quot;, full);&lt;br /&gt;
	PrintToServer(&amp;quot;Argument count: %d&amp;quot;, args);&lt;br /&gt;
	for (new i=1; i&amp;lt;=args; i++)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(i, arg, sizeof(arg));&lt;br /&gt;
		PrintToServer(&amp;quot;Argument %d: %s&amp;quot;, i, arg);&lt;br /&gt;
	}&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Blocking Server Commands==&lt;br /&gt;
Let's say we wanted to disable the &amp;quot;kickid&amp;quot; command on the server.  There's no real good reason to do this, but for example's sake:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegServerCmd(&amp;quot;kickid&amp;quot;, Command_KickId);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Kickid(args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Console Commands=&lt;br /&gt;
Unlike server commands, console commands can be triggered by either the server or the client, so the callback function receives a client index as well as the argument count.  If the server fired the command, the client index will be 0.  &lt;br /&gt;
&lt;br /&gt;
When returning the &amp;lt;tt&amp;gt;Action&amp;lt;/tt&amp;gt; from the callback, the following effects will happen:&lt;br /&gt;
*&amp;lt;tt&amp;gt;Plugin_Continue&amp;lt;/tt&amp;gt;: The original functionality of the command (if any) will still be processed.  If there was no original functionality, the client will receive &amp;quot;Unknown command&amp;quot; in their console.&lt;br /&gt;
*&amp;lt;tt&amp;gt;Plugin_Handled&amp;lt;/tt&amp;gt;: The original functionality of the command (if any) will be blocked.  If there was no functionality originally, this prevents clients from seeing &amp;quot;Unknown command&amp;quot; in their console.&lt;br /&gt;
*&amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt;: Same as &amp;lt;tt&amp;gt;Plugin_Handled&amp;lt;/tt&amp;gt;, except that this will be the last hook called.&lt;br /&gt;
&lt;br /&gt;
Note that, unlike AMX Mod X, SourceMod does not allow you to register command filters.  I.e., there is no equivalent to this notation:&lt;br /&gt;
&amp;lt;pawn&amp;gt;register_clcmd(&amp;quot;say /ff&amp;quot;, &amp;quot;Command_SayFF&amp;quot;);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This notation was removed to make our internal code simpler and faster.  Writing the same functionality is easy, and demonstrated below.&lt;br /&gt;
&lt;br /&gt;
==Adding Commands==&lt;br /&gt;
Adding client commands is very simple.  Let's port our earlier testing command to display information about the client as well.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;test_command&amp;quot;, Command_Test);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Test(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new String:arg[128];&lt;br /&gt;
	new String:full[256];&lt;br /&gt;
&lt;br /&gt;
	GetCmdArgString(full, sizeof(full));&lt;br /&gt;
&lt;br /&gt;
	if (client)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;Command from client: %N&amp;quot;, name);&lt;br /&gt;
	} else {&lt;br /&gt;
		PrintToServer(&amp;quot;Command from server.&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	PrintToServer(&amp;quot;Argument string: %s&amp;quot;, full);&lt;br /&gt;
	PrintToServer(&amp;quot;Argument count: %d&amp;quot;, args);&lt;br /&gt;
	for (new i=1; i&amp;lt;=args; i++)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(i, arg, sizeof(arg));&lt;br /&gt;
		PrintToServer(&amp;quot;Argument %d: %s&amp;quot;, i, arg);&lt;br /&gt;
	}&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Hooking Commands==&lt;br /&gt;
A common example is hooking the say command.  Let's say we want to tell players whether FF is enabled when they say '/ff' in game.  &lt;br /&gt;
&lt;br /&gt;
Before we implement this, a common point of confusion with the 'say' command is that Half-Life 2 (and Half-Life 1), by default, send it with the text in one big quoted string.  This means if you say &amp;quot;I like hot dogs,&amp;quot; your command will be broken down as such:&lt;br /&gt;
*Argument string: &amp;quot;I like hot dogs&amp;quot;&lt;br /&gt;
*Argument count: 1&lt;br /&gt;
*Argument #1: I like hot dogs&lt;br /&gt;
&lt;br /&gt;
However, if a player types this in their console: &amp;lt;tt&amp;gt;say I like yams&amp;lt;/tt&amp;gt;, it will be broken up as:&lt;br /&gt;
*Argument string: I like yams&lt;br /&gt;
*Argument count: 3&lt;br /&gt;
*Argument #1: I&lt;br /&gt;
*Argument #2: like&lt;br /&gt;
*Argument #3: yams&lt;br /&gt;
&lt;br /&gt;
Thus, to take into account both of these situations, we are going to use &amp;lt;tt&amp;gt;GetCmdArgString&amp;lt;/tt&amp;gt; and manually parse the input.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:ff = INVALID_HANDLE;&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	AddCommandListener(Command_Say, &amp;quot;say&amp;quot;);&lt;br /&gt;
	AddCommandListener(Command_Say, &amp;quot;say2&amp;quot;);&lt;br /&gt;
	AddCommandListener(Command_Say, &amp;quot;say_team&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	ff = FindConVar(&amp;quot;mp_friendlyfire&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Say(client, const String:command[], argc)&lt;br /&gt;
{&lt;br /&gt;
	decl String:text[192];&lt;br /&gt;
	new startidx = 0;&lt;br /&gt;
	if (GetCmdArgString(text, sizeof(text)) &amp;lt; 1)&lt;br /&gt;
	{&lt;br /&gt;
		return Plugin_Continue;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	if (text[strlen(text)-1] == '&amp;quot;')&lt;br /&gt;
	{&lt;br /&gt;
		text[strlen(text)-1] = '\0';&lt;br /&gt;
		startidx = 1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (strcmp(command, &amp;quot;say2&amp;quot;, false) == 0)&lt;br /&gt;
		startidx += 4;&lt;br /&gt;
&lt;br /&gt;
	if (strcmp(text[startidx], &amp;quot;ff&amp;quot;, false) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		if (ff != INVALID_HANDLE)&lt;br /&gt;
		{&lt;br /&gt;
			if (GetConVarBool(ff))&lt;br /&gt;
			{&lt;br /&gt;
				PrintToChat(client, &amp;quot;Friendly fire is enabled.&amp;quot;);&lt;br /&gt;
			} else {&lt;br /&gt;
				PrintToChat(client, &amp;quot;Friendly fire is disabled.&amp;quot;);&lt;br /&gt;
			}&lt;br /&gt;
			/* Block the client's messsage from broadcasting */&lt;br /&gt;
			return Plugin_Handled;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Let say continue normally */&lt;br /&gt;
	return Plugin_Continue;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Creating Admin Commands==&lt;br /&gt;
Let's create a simple admin command which kicks another player by their full name.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;admin_kick&amp;quot;,&lt;br /&gt;
		Command_Kick,&lt;br /&gt;
		ADMFLAG_KICK,&lt;br /&gt;
		&amp;quot;Kicks a player by name&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Kick(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (args &amp;lt; 1)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Usage: admin_kick &amp;lt;name&amp;gt;&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new String:name[32], target = -1;&lt;br /&gt;
	GetCmdArg(1, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	for (new i=1; i&amp;lt;=MaxClients; i++)&lt;br /&gt;
	{&lt;br /&gt;
		if (!IsClientConnected(i))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		decl String:other[32];&lt;br /&gt;
		GetClientName(i, other, sizeof(other));&lt;br /&gt;
		if (StrEqual(name, other))&lt;br /&gt;
		{&lt;br /&gt;
			target = i;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Could not find any player with the name: \&amp;quot;%s\&amp;quot;&amp;quot;, name);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	ServerCommand(&amp;quot;kickid %d&amp;quot;, GetClientUserId(target));&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Immunity==&lt;br /&gt;
In our previous example, we did not take immunity into account.  Immunity is a much more complex system in SourceMod than it was in AMX Mod X, and there is no simple flag to denote its permissions.  Instead, two functions are provided:&lt;br /&gt;
*&amp;lt;tt&amp;gt;CanAdminTarget&amp;lt;/tt&amp;gt;: Tests raw AdminId values for immunity.&lt;br /&gt;
*&amp;lt;tt&amp;gt;CanUserTarget&amp;lt;/tt&amp;gt;: Tests in-game clients for immunity.&lt;br /&gt;
&lt;br /&gt;
While immunity is generally tested ''player versus player'', it is possible you might want to check for immunity and not have a targetting client.  While there is no convenience function for this yet, a good idea might be to check for either ''default'' or ''global'' immunity on the player's groups (these can be user-defined for non-player targeted scenarios).&lt;br /&gt;
&lt;br /&gt;
When checking for immunity, the following heuristics are performed in this exact order:&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;If the targeting AdminId is &amp;lt;tt&amp;gt;INVALID_ADMIN_ID&amp;lt;/tt&amp;gt;, targeting fails.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targetted AdminId is &amp;lt;tt&amp;gt;INVALID_ADMIN_ID&amp;lt;/tt&amp;gt;, targeting succeeds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targeting admin has &amp;lt;tt&amp;gt;Admin_Root&amp;lt;/tt&amp;gt; (&amp;lt;tt&amp;gt;ADMFLAG_ROOT&amp;lt;/tt&amp;gt;), targeting succeeds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targetted admin has global immunity, targeting fails.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targetted admin has default immunity, and the targeting admin belongs to no groups, targeting fails.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targetted admin has specific immunity from the targeting admin via group immunities, targeting fails.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If no conclusion is reached via the previous steps, targeting succeeds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, how can we adapt our function about to use immunity?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action:Command_Kick(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (args &amp;lt; 1)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Usage: admin_kick &amp;lt;name&amp;gt;&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new String:name[32], target = -1;&lt;br /&gt;
	GetCmdArg(1, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	for (new i=1; i&amp;lt;=MaxClients; i++)&lt;br /&gt;
	{&lt;br /&gt;
		if (!IsClientConnected(i))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		decl String:other[32];&lt;br /&gt;
		GetClientName(i, other, sizeof(other));&lt;br /&gt;
		if (StrEqual(name, other))&lt;br /&gt;
		{&lt;br /&gt;
			target = i;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Could not find any player with the name: \&amp;quot;%s\&amp;quot;&amp;quot;, name);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (!CanUserTarget(client, target))&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;You cannot target this client.&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	ServerCommand(&amp;quot;kickid %d&amp;quot;, GetClientUserId(target));&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Client-Only Commands=&lt;br /&gt;
SourceMod exposes a forward that is called whenever a client executes any command string in their console, called &amp;lt;tt&amp;gt;OnClientCommand&amp;lt;/tt&amp;gt;.  An example of this looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action:OnClientCommand(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new String:cmd[16];&lt;br /&gt;
	GetCmdArg(0, cmd, sizeof(cmd));	/* Get command name */&lt;br /&gt;
&lt;br /&gt;
	if (StrEqual(cmd, &amp;quot;test_command&amp;quot;))&lt;br /&gt;
	{&lt;br /&gt;
		/* Got the client command! Block it... */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Continue;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is worth noting that not everything a client sends will be available through this command.  Command registered via external sources in C++ may not be available, especially if they are created via &amp;lt;tt&amp;gt;CON_COMMAND&amp;lt;/tt&amp;gt; in the game mod itself.  For example, &amp;quot;say&amp;quot; is usually implemented this way, because it can be used by both clients and the server, and thus it does not channel through this forward.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Chat Triggers=&lt;br /&gt;
SourceMod will automatically create chat triggers for every command you make. For example, if you create a console command called &amp;lt;tt&amp;gt;&amp;quot;sm_megaslap&amp;quot;&amp;lt;/tt&amp;gt;, administrators will be able to type any of the following commands in &amp;lt;tt&amp;gt;say&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;say_team&amp;lt;/tt&amp;gt; message modes:&lt;br /&gt;
&amp;lt;pre&amp;gt;!sm_megaslap&lt;br /&gt;
!megaslap&lt;br /&gt;
/sm_megaslap&lt;br /&gt;
/megaslap&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SourceMod then executes this command and its arguments as if it came from the client console.&lt;br /&gt;
*&amp;quot;&amp;lt;tt&amp;gt;!&amp;lt;/tt&amp;gt;&amp;quot; is the default ''public'' trigger (&amp;lt;tt&amp;gt;PublicChatTrigger&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;configs/core.cfg&amp;lt;/tt&amp;gt;) and your entry will be displayed to all clients as normal.&lt;br /&gt;
*&amp;quot;&amp;lt;tt&amp;gt;/&amp;lt;/tt&amp;gt;&amp;quot; is the default ''silent'' trigger (&amp;lt;tt&amp;gt;SilentChatTrigger&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;configs/core.cfg&amp;lt;/tt&amp;gt;) and your entry will be blocked from being displayed.&lt;br /&gt;
&lt;br /&gt;
SourceMod will only execute commands registered with &amp;lt;tt&amp;gt;RegConsoleCmd&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;RegAdminCmd&amp;lt;/tt&amp;gt;, and only if those commands are not already provided by Half-Life 2 or the game mod.  If the command is prefixed with &amp;quot;sm_&amp;quot; then the &amp;quot;sm_&amp;quot; can be omitted from the chat trigger.&lt;br /&gt;
&lt;br /&gt;
Console commands which wish to support usage as a chat trigger should not use &amp;lt;tt&amp;gt;PrintTo*&amp;lt;/tt&amp;gt; natives.  Instead, they should use &amp;lt;tt&amp;gt;ReplyToCommand()&amp;lt;/tt&amp;gt;, which will automatically print your message either as a chat message or to the client's console, depending on the source of the command.&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Commands_(SourceMod_Scripting)&amp;diff=8956</id>
		<title>Commands (SourceMod Scripting)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Commands_(SourceMod_Scripting)&amp;diff=8956"/>
		<updated>2013-06-15T00:20:53Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Returning Plugin_Handled to prevent &amp;quot;unknown command&amp;quot;-error after proper execution.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[SourceMod]] allows you to create console commands similar to [[AMX Mod X]].  There are two main types of console commands:&lt;br /&gt;
*'''Server Commands''' - Fired from one of the following input methods:&lt;br /&gt;
**the server console itself&lt;br /&gt;
**the remote console (RCON)&lt;br /&gt;
**the ServerCommand() function, either from SourceMod or the [[Half-Life 2]] engine&lt;br /&gt;
*'''Console Commands''' - Fired from one of the following input methods:&lt;br /&gt;
**a client's console&lt;br /&gt;
**any of the server command input methods&lt;br /&gt;
&lt;br /&gt;
For server commands, there is no client index.  For console/client commands, there is a client index, but it may be 0 to indicate that the command is from the server.&lt;br /&gt;
&lt;br /&gt;
Note that button-bound &amp;quot;commands,&amp;quot; such as +attack and +duck, are not actually console commands.  They are a separate command stream sent over the network, as they need to be tied to a specific frame.  &lt;br /&gt;
&lt;br /&gt;
=Server Commands=&lt;br /&gt;
As noted above, server commands are fired through the server console, whether remote, local, or through the Source engine.  There is no client index associated with a server command.  &lt;br /&gt;
&lt;br /&gt;
Server commands are registered through the &amp;lt;tt&amp;gt;RegServerCmd()&amp;lt;/tt&amp;gt; function defined in &amp;lt;tt&amp;gt;console.inc&amp;lt;/tt&amp;gt;.  When registering a server command, you may be hooking an already existing command, and thus the return value is important.&lt;br /&gt;
*&amp;lt;b&amp;gt;&amp;lt;tt&amp;gt;Plugin_Continue&amp;lt;/tt&amp;gt;&amp;lt;/b&amp;gt; - The original server command will be processed, if there was one.  If the server command was created by a plugin, this has no effect.&lt;br /&gt;
*&amp;lt;b&amp;gt;&amp;lt;tt&amp;gt;Plugin_Handled&amp;lt;/tt&amp;gt;&amp;lt;/b&amp;gt; - The original server command will not be processed, if there was one.  If the server command was created by a plugin, this has no effect.&lt;br /&gt;
*&amp;lt;b&amp;gt;&amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt;&amp;lt;/b&amp;gt; - The original server command will not be processed, if there was one.  Additionally, no further hooks will be called for this command until it is fired again.&lt;br /&gt;
&lt;br /&gt;
==Adding Server Commands==&lt;br /&gt;
Let's say we want to add a test command to show how Half-Life 2 breaks down command arguments.  A possible implementation might be&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegServerCmd(&amp;quot;test_command&amp;quot;, Command_Test);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Test(args)&lt;br /&gt;
{&lt;br /&gt;
	new String:arg[128];&lt;br /&gt;
	new String:full[256];&lt;br /&gt;
&lt;br /&gt;
	GetCmdArgString(full, sizeof(full));&lt;br /&gt;
&lt;br /&gt;
	PrintToServer(&amp;quot;Argument string: %s&amp;quot;, full);&lt;br /&gt;
	PrintToServer(&amp;quot;Argument count: %d&amp;quot;, args);&lt;br /&gt;
	for (new i=1; i&amp;lt;=args; i++)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(i, arg, sizeof(arg));&lt;br /&gt;
		PrintToServer(&amp;quot;Argument %d: %s&amp;quot;, i, arg);&lt;br /&gt;
	}&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Blocking Server Commands==&lt;br /&gt;
Let's say we wanted to disable the &amp;quot;kickid&amp;quot; command on the server.  There's no real good reason to do this, but for example's sake:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegServerCmd(&amp;quot;kickid&amp;quot;, Command_KickId);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Kickid(args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Console Commands=&lt;br /&gt;
Unlike server commands, console commands can be triggered by either the server or the client, so the callback function receives a client index as well as the argument count.  If the server fired the command, the client index will be 0.  &lt;br /&gt;
&lt;br /&gt;
When returning the &amp;lt;tt&amp;gt;Action&amp;lt;/tt&amp;gt; from the callback, the following effects will happen:&lt;br /&gt;
*&amp;lt;tt&amp;gt;Plugin_Continue&amp;lt;/tt&amp;gt;: The original functionality of the command (if any) will still be processed.  If there was no original functionality, the client will receive &amp;quot;Unknown command&amp;quot; in their console.&lt;br /&gt;
*&amp;lt;tt&amp;gt;Plugin_Handled&amp;lt;/tt&amp;gt;: The original functionality of the command (if any) will be blocked.  If there was no functionality originally, this prevents clients from seeing &amp;quot;Unknown command&amp;quot; in their console.&lt;br /&gt;
*&amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt;: Same as &amp;lt;tt&amp;gt;Plugin_Handled&amp;lt;/tt&amp;gt;, except that this will be the last hook called.&lt;br /&gt;
&lt;br /&gt;
Note that, unlike AMX Mod X, SourceMod does not allow you to register command filters.  I.e., there is no equivalent to this notation:&lt;br /&gt;
&amp;lt;pawn&amp;gt;register_clcmd(&amp;quot;say /ff&amp;quot;, &amp;quot;Command_SayFF&amp;quot;);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This notation was removed to make our internal code simpler and faster.  Writing the same functionality is easy, and demonstrated below.&lt;br /&gt;
&lt;br /&gt;
==Adding Commands==&lt;br /&gt;
Adding client commands is very simple.  Let's port our earlier testing command to display information about the client as well.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;test_command&amp;quot;, Command_Test);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Test(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new String:arg[128];&lt;br /&gt;
	new String:full[256];&lt;br /&gt;
&lt;br /&gt;
	GetCmdArgString(full, sizeof(full));&lt;br /&gt;
&lt;br /&gt;
	if (client)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;Command from client: %N&amp;quot;, name);&lt;br /&gt;
	} else {&lt;br /&gt;
		PrintToServer(&amp;quot;Command from server.&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	PrintToServer(&amp;quot;Argument string: %s&amp;quot;, full);&lt;br /&gt;
	PrintToServer(&amp;quot;Argument count: %d&amp;quot;, args);&lt;br /&gt;
	for (new i=1; i&amp;lt;=args; i++)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(i, arg, sizeof(arg));&lt;br /&gt;
		PrintToServer(&amp;quot;Argument %d: %s&amp;quot;, i, arg);&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Hooking Commands==&lt;br /&gt;
A common example is hooking the say command.  Let's say we want to tell players whether FF is enabled when they say '/ff' in game.  &lt;br /&gt;
&lt;br /&gt;
Before we implement this, a common point of confusion with the 'say' command is that Half-Life 2 (and Half-Life 1), by default, send it with the text in one big quoted string.  This means if you say &amp;quot;I like hot dogs,&amp;quot; your command will be broken down as such:&lt;br /&gt;
*Argument string: &amp;quot;I like hot dogs&amp;quot;&lt;br /&gt;
*Argument count: 1&lt;br /&gt;
*Argument #1: I like hot dogs&lt;br /&gt;
&lt;br /&gt;
However, if a player types this in their console: &amp;lt;tt&amp;gt;say I like yams&amp;lt;/tt&amp;gt;, it will be broken up as:&lt;br /&gt;
*Argument string: I like yams&lt;br /&gt;
*Argument count: 3&lt;br /&gt;
*Argument #1: I&lt;br /&gt;
*Argument #2: like&lt;br /&gt;
*Argument #3: yams&lt;br /&gt;
&lt;br /&gt;
Thus, to take into account both of these situations, we are going to use &amp;lt;tt&amp;gt;GetCmdArgString&amp;lt;/tt&amp;gt; and manually parse the input.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;new Handle:ff = INVALID_HANDLE;&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	AddCommandListener(Command_Say, &amp;quot;say&amp;quot;);&lt;br /&gt;
	AddCommandListener(Command_Say, &amp;quot;say2&amp;quot;);&lt;br /&gt;
	AddCommandListener(Command_Say, &amp;quot;say_team&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
	ff = FindConVar(&amp;quot;mp_friendlyfire&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Say(client, const String:command[], argc)&lt;br /&gt;
{&lt;br /&gt;
	decl String:text[192];&lt;br /&gt;
	new startidx = 0;&lt;br /&gt;
	if (GetCmdArgString(text, sizeof(text)) &amp;lt; 1)&lt;br /&gt;
	{&lt;br /&gt;
		return Plugin_Continue;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	if (text[strlen(text)-1] == '&amp;quot;')&lt;br /&gt;
	{&lt;br /&gt;
		text[strlen(text)-1] = '\0';&lt;br /&gt;
		startidx = 1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (strcmp(command, &amp;quot;say2&amp;quot;, false) == 0)&lt;br /&gt;
		startidx += 4;&lt;br /&gt;
&lt;br /&gt;
	if (strcmp(text[startidx], &amp;quot;ff&amp;quot;, false) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		if (ff != INVALID_HANDLE)&lt;br /&gt;
		{&lt;br /&gt;
			if (GetConVarBool(ff))&lt;br /&gt;
			{&lt;br /&gt;
				PrintToChat(client, &amp;quot;Friendly fire is enabled.&amp;quot;);&lt;br /&gt;
			} else {&lt;br /&gt;
				PrintToChat(client, &amp;quot;Friendly fire is disabled.&amp;quot;);&lt;br /&gt;
			}&lt;br /&gt;
			/* Block the client's messsage from broadcasting */&lt;br /&gt;
			return Plugin_Handled;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Let say continue normally */&lt;br /&gt;
	return Plugin_Continue;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Creating Admin Commands==&lt;br /&gt;
Let's create a simple admin command which kicks another player by their full name.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;admin_kick&amp;quot;,&lt;br /&gt;
		Command_Kick,&lt;br /&gt;
		ADMFLAG_KICK,&lt;br /&gt;
		&amp;quot;Kicks a player by name&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action:Command_Kick(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (args &amp;lt; 1)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Usage: admin_kick &amp;lt;name&amp;gt;&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new String:name[32], target = -1;&lt;br /&gt;
	GetCmdArg(1, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	for (new i=1; i&amp;lt;=MaxClients; i++)&lt;br /&gt;
	{&lt;br /&gt;
		if (!IsClientConnected(i))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		decl String:other[32];&lt;br /&gt;
		GetClientName(i, other, sizeof(other));&lt;br /&gt;
		if (StrEqual(name, other))&lt;br /&gt;
		{&lt;br /&gt;
			target = i;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Could not find any player with the name: \&amp;quot;%s\&amp;quot;&amp;quot;, name);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	ServerCommand(&amp;quot;kickid %d&amp;quot;, GetClientUserId(target));&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Immunity==&lt;br /&gt;
In our previous example, we did not take immunity into account.  Immunity is a much more complex system in SourceMod than it was in AMX Mod X, and there is no simple flag to denote its permissions.  Instead, two functions are provided:&lt;br /&gt;
*&amp;lt;tt&amp;gt;CanAdminTarget&amp;lt;/tt&amp;gt;: Tests raw AdminId values for immunity.&lt;br /&gt;
*&amp;lt;tt&amp;gt;CanUserTarget&amp;lt;/tt&amp;gt;: Tests in-game clients for immunity.&lt;br /&gt;
&lt;br /&gt;
While immunity is generally tested ''player versus player'', it is possible you might want to check for immunity and not have a targetting client.  While there is no convenience function for this yet, a good idea might be to check for either ''default'' or ''global'' immunity on the player's groups (these can be user-defined for non-player targeted scenarios).&lt;br /&gt;
&lt;br /&gt;
When checking for immunity, the following heuristics are performed in this exact order:&lt;br /&gt;
&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;If the targeting AdminId is &amp;lt;tt&amp;gt;INVALID_ADMIN_ID&amp;lt;/tt&amp;gt;, targeting fails.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targetted AdminId is &amp;lt;tt&amp;gt;INVALID_ADMIN_ID&amp;lt;/tt&amp;gt;, targeting succeeds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targeting admin has &amp;lt;tt&amp;gt;Admin_Root&amp;lt;/tt&amp;gt; (&amp;lt;tt&amp;gt;ADMFLAG_ROOT&amp;lt;/tt&amp;gt;), targeting succeeds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targetted admin has global immunity, targeting fails.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targetted admin has default immunity, and the targeting admin belongs to no groups, targeting fails.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If the targetted admin has specific immunity from the targeting admin via group immunities, targeting fails.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;If no conclusion is reached via the previous steps, targeting succeeds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, how can we adapt our function about to use immunity?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action:Command_Kick(client, args)&lt;br /&gt;
{&lt;br /&gt;
	if (args &amp;lt; 1)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Usage: admin_kick &amp;lt;name&amp;gt;&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	new String:name[32], target = -1;&lt;br /&gt;
	GetCmdArg(1, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	for (new i=1; i&amp;lt;=MaxClients; i++)&lt;br /&gt;
	{&lt;br /&gt;
		if (!IsClientConnected(i))&lt;br /&gt;
		{&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		decl String:other[32];&lt;br /&gt;
		GetClientName(i, other, sizeof(other));&lt;br /&gt;
		if (StrEqual(name, other))&lt;br /&gt;
		{&lt;br /&gt;
			target = i;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;Could not find any player with the name: \&amp;quot;%s\&amp;quot;&amp;quot;, name);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (!CanUserTarget(client, target))&lt;br /&gt;
	{&lt;br /&gt;
		PrintToConsole(client, &amp;quot;You cannot target this client.&amp;quot;);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	ServerCommand(&amp;quot;kickid %d&amp;quot;, GetClientUserId(target));&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Client-Only Commands=&lt;br /&gt;
SourceMod exposes a forward that is called whenever a client executes any command string in their console, called &amp;lt;tt&amp;gt;OnClientCommand&amp;lt;/tt&amp;gt;.  An example of this looks like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action:OnClientCommand(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new String:cmd[16];&lt;br /&gt;
	GetCmdArg(0, cmd, sizeof(cmd));	/* Get command name */&lt;br /&gt;
&lt;br /&gt;
	if (StrEqual(cmd, &amp;quot;test_command&amp;quot;))&lt;br /&gt;
	{&lt;br /&gt;
		/* Got the client command! Block it... */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Continue;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is worth noting that not everything a client sends will be available through this command.  Command registered via external sources in C++ may not be available, especially if they are created via &amp;lt;tt&amp;gt;CON_COMMAND&amp;lt;/tt&amp;gt; in the game mod itself.  For example, &amp;quot;say&amp;quot; is usually implemented this way, because it can be used by both clients and the server, and thus it does not channel through this forward.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Chat Triggers=&lt;br /&gt;
SourceMod will automatically create chat triggers for every command you make. For example, if you create a console command called &amp;lt;tt&amp;gt;&amp;quot;sm_megaslap&amp;quot;&amp;lt;/tt&amp;gt;, administrators will be able to type any of the following commands in &amp;lt;tt&amp;gt;say&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;say_team&amp;lt;/tt&amp;gt; message modes:&lt;br /&gt;
&amp;lt;pre&amp;gt;!sm_megaslap&lt;br /&gt;
!megaslap&lt;br /&gt;
/sm_megaslap&lt;br /&gt;
/megaslap&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SourceMod then executes this command and its arguments as if it came from the client console.&lt;br /&gt;
*&amp;quot;&amp;lt;tt&amp;gt;!&amp;lt;/tt&amp;gt;&amp;quot; is the default ''public'' trigger (&amp;lt;tt&amp;gt;PublicChatTrigger&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;configs/core.cfg&amp;lt;/tt&amp;gt;) and your entry will be displayed to all clients as normal.&lt;br /&gt;
*&amp;quot;&amp;lt;tt&amp;gt;/&amp;lt;/tt&amp;gt;&amp;quot; is the default ''silent'' trigger (&amp;lt;tt&amp;gt;SilentChatTrigger&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;configs/core.cfg&amp;lt;/tt&amp;gt;) and your entry will be blocked from being displayed.&lt;br /&gt;
&lt;br /&gt;
SourceMod will only execute commands registered with &amp;lt;tt&amp;gt;RegConsoleCmd&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;RegAdminCmd&amp;lt;/tt&amp;gt;, and only if those commands are not already provided by Half-Life 2 or the game mod.  If the command is prefixed with &amp;quot;sm_&amp;quot; then the &amp;quot;sm_&amp;quot; can be omitted from the chat trigger.&lt;br /&gt;
&lt;br /&gt;
Console commands which wish to support usage as a chat trigger should not use &amp;lt;tt&amp;gt;PrintTo*&amp;lt;/tt&amp;gt; natives.  Instead, they should use &amp;lt;tt&amp;gt;ReplyToCommand()&amp;lt;/tt&amp;gt;, which will automatically print your message either as a chat message or to the client's console, depending on the source of the command.&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Left_4_Voting_2&amp;diff=8954</id>
		<title>Left 4 Voting 2</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Left_4_Voting_2&amp;diff=8954"/>
		<updated>2013-06-14T02:06:39Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: /* Values for &amp;quot;issue&amp;quot; and &amp;quot;param1&amp;quot; in standard votes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Left 4 Dead 2 has a VGUI voting system controller by a bunch of Events and UserMessages.  You can use either a string from the resource file, or L4D_TargetID_Player which will let you create any vote you want.&lt;br /&gt;
&lt;br /&gt;
== How voting works ==&lt;br /&gt;
# A client issues a callvote with the vote type and argument.&lt;br /&gt;
# The VoteStart User Message is sent.&lt;br /&gt;
# Clients use the &amp;quot;Vote&amp;quot; command to register their votes (&amp;quot;Yes&amp;quot; or &amp;quot;No&amp;quot;), after which the server sends a VoteRegistered UserMessage to that player to acknowledge their vote.  A vote_changed event is sent to all players with updated numbers.&lt;br /&gt;
# When the vote is complete, the server sends either a VotePass or VoteFail UserMessage.&lt;br /&gt;
&lt;br /&gt;
== Server Entity ==&lt;br /&gt;
&lt;br /&gt;
The server should update this as appropriate.  Unfortunately, the valid values for m_iActiveIssueIndex is unknown.&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|vote_controller (CVoteController)|string}}&lt;br /&gt;
{{hl2msg|int|m_activeIssueIndex|Number of the active issue}}&lt;br /&gt;
{{hl2msg|int|m_onlyTeamToVote|Corresponds to VoteStart's team argument.}}&lt;br /&gt;
{{hl2msg|int|m_votesYes|Current Yes votes}}&lt;br /&gt;
{{hl2msg|int|m_votesNo|Current No votes}}&lt;br /&gt;
{{hl2msg|int|m_potentialVotes|Number of players eligible to vote}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== Console Commands ==&lt;br /&gt;
&lt;br /&gt;
=== Vote ===&lt;br /&gt;
&lt;br /&gt;
{{qnotice|This command is only valid when a vote is ongoing.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{begin-hl2msg|Vote|string}}&lt;br /&gt;
{{hl2msg|string|option|&amp;quot;Yes&amp;quot; or &amp;quot;No&amp;quot;}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== User Messages ==&lt;br /&gt;
{{qnotice|These user messages use unsigned bytes for the team number.  Since Valve represents no team as -1, no team is instead represented as its two's complement, 255.}}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===VoteStart===&lt;br /&gt;
{{qnotice|Sent to all players in the vote group, corresponding with teams.  The default implementation also sends it to bots.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VoteStart|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{hl2msg|byte|initiator|Client index (NOT USERID) of person who started the vote, or 99 for the server.}}&lt;br /&gt;
{{hl2msg|string|issue|Vote issue translation string}}&lt;br /&gt;
{{hl2msg|string|param1|Vote issue text}}&lt;br /&gt;
{{hl2msg|string|initiatorName|Name of person who started the vote}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
====Values for &amp;quot;issue&amp;quot; and &amp;quot;param1&amp;quot; in standard votes====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Vote type !! issue !! param1&lt;br /&gt;
|-&lt;br /&gt;
| Kick || #L4D_vote_kick_player || Nickname of the person to be kicked without &amp;quot;#&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| ReturnToLobby || #L4D_vote_return_to_lobby || &lt;br /&gt;
|-&lt;br /&gt;
| ChangeCampaign || #L4D_vote_mission_change || #L4D360UI_CampaignName_C5&lt;br /&gt;
|-&lt;br /&gt;
| RestartChapter || #L4D_vote_passed_versus_level_restart ||&lt;br /&gt;
|-&lt;br /&gt;
| ChangeDifficulty (easy) || #L4D_vote_change_difficulty || #L4D_DifficultyEasy&lt;br /&gt;
|-&lt;br /&gt;
| ChangeDifficulty (hard) || #L4D_vote_change_difficulty || #L4D_DifficultyHard&lt;br /&gt;
|-&lt;br /&gt;
| Alltalk On || #L4D_vote_alltalk_change || #L4D_vote_alltalk_enable&lt;br /&gt;
|-&lt;br /&gt;
| Alltalk Off || #L4D_vote_alltalk_change || #L4D_vote_alltalk_disable&lt;br /&gt;
|}&lt;br /&gt;
*Campaigns aren't specified by map name but by the index the campaign's entry has in the game menus. Example: &amp;quot;5&amp;quot; specifies the campaign &amp;quot;The Parish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===VoteRegistered===&lt;br /&gt;
{{qnotice|Only sent to player who voted}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{begin-hl2msg|VoteRegistered|string}}&lt;br /&gt;
{{hl2msg|byte|vote|0 for No, 1 for Yes}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
===VotePass===&lt;br /&gt;
{{qnotice|Sent to all players after a vote passes.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VotePass|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{hl2msg|string|details|Vote success translation string}}&lt;br /&gt;
{{hl2msg|string|param1|Vote winner}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
===VoteFail===&lt;br /&gt;
{{qnotice|Sent to all players after a vote fails.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VoteFail|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== Events ==&lt;br /&gt;
{{qnotice|team is -1 when sent to all players)}}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== vote_changed ===&lt;br /&gt;
{{begin-hl2msg|vote_changed|string}}&lt;br /&gt;
{{hl2msg|byte|yesVotes|}}&lt;br /&gt;
{{hl2msg|byte|noVotes|}}&lt;br /&gt;
{{hl2msg|byte|potentialVotes|}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
== Example voting plugin ==&lt;br /&gt;
This is a basic plugin that starts a vote, &amp;quot;Is gaben fat?&amp;quot;.  It does not ensure the same client does not vote multiple times, nor does it actually kick the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define L4D2_TEAM_ALL -1&lt;br /&gt;
&lt;br /&gt;
new yesvotes;&lt;br /&gt;
new novotes;&lt;br /&gt;
#define MAX_VOTES 4&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;testvote&amp;quot;,Callvote_Handler);&lt;br /&gt;
	RegConsoleCmd(&amp;quot;Vote&amp;quot;,vote);&lt;br /&gt;
}&lt;br /&gt;
public Action:Callvote_Handler(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:bf = StartMessageAll(&amp;quot;VoteStart&amp;quot;, USERMSG_RELIABLE);&lt;br /&gt;
&lt;br /&gt;
	BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
	BfWriteByte(bf, 0);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;#L4D_TargetID_Player&amp;quot;);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;Is gaben fat?&amp;quot;);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;Server&amp;quot;);&lt;br /&gt;
	EndMessage();&lt;br /&gt;
	&lt;br /&gt;
	yesvotes = 0;&lt;br /&gt;
	novotes = 0;&lt;br /&gt;
	UpdateVotes();&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
public UpdateVotes()&lt;br /&gt;
{&lt;br /&gt;
	new Handle:msg = CreateEvent(&amp;quot;vote_changed&amp;quot;);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;yesVotes&amp;quot;,yesvotes);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;noVotes&amp;quot;,novotes);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;potentialVotes&amp;quot;,MAX_VOTES);&lt;br /&gt;
	FireEvent(msg);&lt;br /&gt;
	&lt;br /&gt;
	if (yesvotes+novotes == MAX_VOTES)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;voting complete!&amp;quot;);&lt;br /&gt;
		if (yesvotes &amp;gt; novotes)&lt;br /&gt;
		{&lt;br /&gt;
			new Handle:bf = StartMessageAll(&amp;quot;VotePass&amp;quot;);&lt;br /&gt;
			BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
			BfWriteString(bf, &amp;quot;#L4D_TargetID_Player&amp;quot;);&lt;br /&gt;
			BfWriteString(bf, &amp;quot;Gaben is fat&amp;quot;);&lt;br /&gt;
			EndMessage();&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			new Handle:bf = StartMessageAll(&amp;quot;VoteFailed&amp;quot;);&lt;br /&gt;
			BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
			EndMessage();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
public Action:vote(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new String:arg[8];&lt;br /&gt;
	GetCmdArg(1,arg,8);&lt;br /&gt;
	PrintToServer(&amp;quot;Got vote %s from %i&amp;quot;,arg,client);&lt;br /&gt;
	if (strcmp(arg,&amp;quot;Yes&amp;quot;,true) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		yesvotes++;&lt;br /&gt;
	}&lt;br /&gt;
	else if (strcmp(arg,&amp;quot;No&amp;quot;,true) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		novotes++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	UpdateVotes();&lt;br /&gt;
	return Plugin_Continue;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[Left 4 Voting]]&lt;br /&gt;
* [[TF2 Voting]]&lt;br /&gt;
* [https://forums.alliedmods.net/showthread.php?t=162164 BuiltinVotes], a SourceMod extension that exposes a voting API that uses this voting system.&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Left_4_Voting_2&amp;diff=8953</id>
		<title>Left 4 Voting 2</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Left_4_Voting_2&amp;diff=8953"/>
		<updated>2013-06-14T00:57:21Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: /* Values for &amp;quot;issue&amp;quot; and &amp;quot;param1&amp;quot; in standard votes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Left 4 Dead 2 has a VGUI voting system controller by a bunch of Events and UserMessages.  You can use either a string from the resource file, or L4D_TargetID_Player which will let you create any vote you want.&lt;br /&gt;
&lt;br /&gt;
== How voting works ==&lt;br /&gt;
# A client issues a callvote with the vote type and argument.&lt;br /&gt;
# The VoteStart User Message is sent.&lt;br /&gt;
# Clients use the &amp;quot;Vote&amp;quot; command to register their votes (&amp;quot;Yes&amp;quot; or &amp;quot;No&amp;quot;), after which the server sends a VoteRegistered UserMessage to that player to acknowledge their vote.  A vote_changed event is sent to all players with updated numbers.&lt;br /&gt;
# When the vote is complete, the server sends either a VotePass or VoteFail UserMessage.&lt;br /&gt;
&lt;br /&gt;
== Server Entity ==&lt;br /&gt;
&lt;br /&gt;
The server should update this as appropriate.  Unfortunately, the valid values for m_iActiveIssueIndex is unknown.&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|vote_controller (CVoteController)|string}}&lt;br /&gt;
{{hl2msg|int|m_activeIssueIndex|Number of the active issue}}&lt;br /&gt;
{{hl2msg|int|m_onlyTeamToVote|Corresponds to VoteStart's team argument.}}&lt;br /&gt;
{{hl2msg|int|m_votesYes|Current Yes votes}}&lt;br /&gt;
{{hl2msg|int|m_votesNo|Current No votes}}&lt;br /&gt;
{{hl2msg|int|m_potentialVotes|Number of players eligible to vote}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== Console Commands ==&lt;br /&gt;
&lt;br /&gt;
=== Vote ===&lt;br /&gt;
&lt;br /&gt;
{{qnotice|This command is only valid when a vote is ongoing.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{begin-hl2msg|Vote|string}}&lt;br /&gt;
{{hl2msg|string|option|&amp;quot;Yes&amp;quot; or &amp;quot;No&amp;quot;}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== User Messages ==&lt;br /&gt;
{{qnotice|These user messages use unsigned bytes for the team number.  Since Valve represents no team as -1, no team is instead represented as its two's complement, 255.}}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===VoteStart===&lt;br /&gt;
{{qnotice|Sent to all players in the vote group, corresponding with teams.  The default implementation also sends it to bots.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VoteStart|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{hl2msg|byte|initiator|Client index (NOT USERID) of person who started the vote, or 99 for the server.}}&lt;br /&gt;
{{hl2msg|string|issue|Vote issue translation string}}&lt;br /&gt;
{{hl2msg|string|param1|Vote issue text}}&lt;br /&gt;
{{hl2msg|string|initiatorName|Name of person who started the vote}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
====Values for &amp;quot;issue&amp;quot; and &amp;quot;param1&amp;quot; in standard votes====&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Vote type !! issue !! param1&lt;br /&gt;
|-&lt;br /&gt;
| Kick || #L4D_vote_kick_player || Nickname of the person to be kicked without &amp;quot;#&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| ReturnToLobby || #L4D_vote_return_to_lobby || &lt;br /&gt;
|-&lt;br /&gt;
| ChangeCampaign || #L4D_vote_mission_change || #L4D360UI_CampaignName_C5&lt;br /&gt;
|}&lt;br /&gt;
*Campaigns aren't specified by map name but by the index the campaign's entry has in the game menus. Example: &amp;quot;5&amp;quot; specifies the campaign &amp;quot;The Parish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===VoteRegistered===&lt;br /&gt;
{{qnotice|Only sent to player who voted}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{begin-hl2msg|VoteRegistered|string}}&lt;br /&gt;
{{hl2msg|byte|vote|0 for No, 1 for Yes}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
===VotePass===&lt;br /&gt;
{{qnotice|Sent to all players after a vote passes.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VotePass|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{hl2msg|string|details|Vote success translation string}}&lt;br /&gt;
{{hl2msg|string|param1|Vote winner}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
===VoteFail===&lt;br /&gt;
{{qnotice|Sent to all players after a vote fails.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VoteFail|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== Events ==&lt;br /&gt;
{{qnotice|team is -1 when sent to all players)}}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== vote_changed ===&lt;br /&gt;
{{begin-hl2msg|vote_changed|string}}&lt;br /&gt;
{{hl2msg|byte|yesVotes|}}&lt;br /&gt;
{{hl2msg|byte|noVotes|}}&lt;br /&gt;
{{hl2msg|byte|potentialVotes|}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
== Example voting plugin ==&lt;br /&gt;
This is a basic plugin that starts a vote, &amp;quot;Is gaben fat?&amp;quot;.  It does not ensure the same client does not vote multiple times, nor does it actually kick the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define L4D2_TEAM_ALL -1&lt;br /&gt;
&lt;br /&gt;
new yesvotes;&lt;br /&gt;
new novotes;&lt;br /&gt;
#define MAX_VOTES 4&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;testvote&amp;quot;,Callvote_Handler);&lt;br /&gt;
	RegConsoleCmd(&amp;quot;Vote&amp;quot;,vote);&lt;br /&gt;
}&lt;br /&gt;
public Action:Callvote_Handler(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:bf = StartMessageAll(&amp;quot;VoteStart&amp;quot;, USERMSG_RELIABLE);&lt;br /&gt;
&lt;br /&gt;
	BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
	BfWriteByte(bf, 0);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;#L4D_TargetID_Player&amp;quot;);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;Is gaben fat?&amp;quot;);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;Server&amp;quot;);&lt;br /&gt;
	EndMessage();&lt;br /&gt;
	&lt;br /&gt;
	yesvotes = 0;&lt;br /&gt;
	novotes = 0;&lt;br /&gt;
	UpdateVotes();&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
public UpdateVotes()&lt;br /&gt;
{&lt;br /&gt;
	new Handle:msg = CreateEvent(&amp;quot;vote_changed&amp;quot;);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;yesVotes&amp;quot;,yesvotes);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;noVotes&amp;quot;,novotes);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;potentialVotes&amp;quot;,MAX_VOTES);&lt;br /&gt;
	FireEvent(msg);&lt;br /&gt;
	&lt;br /&gt;
	if (yesvotes+novotes == MAX_VOTES)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;voting complete!&amp;quot;);&lt;br /&gt;
		if (yesvotes &amp;gt; novotes)&lt;br /&gt;
		{&lt;br /&gt;
			new Handle:bf = StartMessageAll(&amp;quot;VotePass&amp;quot;);&lt;br /&gt;
			BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
			BfWriteString(bf, &amp;quot;#L4D_TargetID_Player&amp;quot;);&lt;br /&gt;
			BfWriteString(bf, &amp;quot;Gaben is fat&amp;quot;);&lt;br /&gt;
			EndMessage();&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			new Handle:bf = StartMessageAll(&amp;quot;VoteFailed&amp;quot;);&lt;br /&gt;
			BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
			EndMessage();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
public Action:vote(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new String:arg[8];&lt;br /&gt;
	GetCmdArg(1,arg,8);&lt;br /&gt;
	PrintToServer(&amp;quot;Got vote %s from %i&amp;quot;,arg,client);&lt;br /&gt;
	if (strcmp(arg,&amp;quot;Yes&amp;quot;,true) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		yesvotes++;&lt;br /&gt;
	}&lt;br /&gt;
	else if (strcmp(arg,&amp;quot;No&amp;quot;,true) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		novotes++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	UpdateVotes();&lt;br /&gt;
	return Plugin_Continue;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[Left 4 Voting]]&lt;br /&gt;
* [[TF2 Voting]]&lt;br /&gt;
* [https://forums.alliedmods.net/showthread.php?t=162164 BuiltinVotes], a SourceMod extension that exposes a voting API that uses this voting system.&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Left_4_Voting_2&amp;diff=8952</id>
		<title>Left 4 Voting 2</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Left_4_Voting_2&amp;diff=8952"/>
		<updated>2013-06-14T00:57:04Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Added subsection &amp;quot;Values for &amp;quot;issue&amp;quot; and &amp;quot;param1&amp;quot; in standard votes&amp;quot;. More coming soon.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Left 4 Dead 2 has a VGUI voting system controller by a bunch of Events and UserMessages.  You can use either a string from the resource file, or L4D_TargetID_Player which will let you create any vote you want.&lt;br /&gt;
&lt;br /&gt;
== How voting works ==&lt;br /&gt;
# A client issues a callvote with the vote type and argument.&lt;br /&gt;
# The VoteStart User Message is sent.&lt;br /&gt;
# Clients use the &amp;quot;Vote&amp;quot; command to register their votes (&amp;quot;Yes&amp;quot; or &amp;quot;No&amp;quot;), after which the server sends a VoteRegistered UserMessage to that player to acknowledge their vote.  A vote_changed event is sent to all players with updated numbers.&lt;br /&gt;
# When the vote is complete, the server sends either a VotePass or VoteFail UserMessage.&lt;br /&gt;
&lt;br /&gt;
== Server Entity ==&lt;br /&gt;
&lt;br /&gt;
The server should update this as appropriate.  Unfortunately, the valid values for m_iActiveIssueIndex is unknown.&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|vote_controller (CVoteController)|string}}&lt;br /&gt;
{{hl2msg|int|m_activeIssueIndex|Number of the active issue}}&lt;br /&gt;
{{hl2msg|int|m_onlyTeamToVote|Corresponds to VoteStart's team argument.}}&lt;br /&gt;
{{hl2msg|int|m_votesYes|Current Yes votes}}&lt;br /&gt;
{{hl2msg|int|m_votesNo|Current No votes}}&lt;br /&gt;
{{hl2msg|int|m_potentialVotes|Number of players eligible to vote}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== Console Commands ==&lt;br /&gt;
&lt;br /&gt;
=== Vote ===&lt;br /&gt;
&lt;br /&gt;
{{qnotice|This command is only valid when a vote is ongoing.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{begin-hl2msg|Vote|string}}&lt;br /&gt;
{{hl2msg|string|option|&amp;quot;Yes&amp;quot; or &amp;quot;No&amp;quot;}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== User Messages ==&lt;br /&gt;
{{qnotice|These user messages use unsigned bytes for the team number.  Since Valve represents no team as -1, no team is instead represented as its two's complement, 255.}}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===VoteStart===&lt;br /&gt;
{{qnotice|Sent to all players in the vote group, corresponding with teams.  The default implementation also sends it to bots.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VoteStart|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{hl2msg|byte|initiator|Client index (NOT USERID) of person who started the vote, or 99 for the server.}}&lt;br /&gt;
{{hl2msg|string|issue|Vote issue translation string}}&lt;br /&gt;
{{hl2msg|string|param1|Vote issue text}}&lt;br /&gt;
{{hl2msg|string|initiatorName|Name of person who started the vote}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
====Values for &amp;quot;issue&amp;quot; and &amp;quot;param1&amp;quot; in standard votes====&lt;br /&gt;
Coming soon...&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Vote type !! issue !! param1&lt;br /&gt;
|-&lt;br /&gt;
| Kick || #L4D_vote_kick_player || Nickname of the person to be kicked without &amp;quot;#&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| ReturnToLobby || #L4D_vote_return_to_lobby || &lt;br /&gt;
|-&lt;br /&gt;
| ChangeCampaign || #L4D_vote_mission_change || #L4D360UI_CampaignName_C5&lt;br /&gt;
|}&lt;br /&gt;
*Campaigns aren't specified by map name but by the index the campaign's entry has in the game menus. Example: &amp;quot;5&amp;quot; specifies the campaign &amp;quot;The Parish&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===VoteRegistered===&lt;br /&gt;
{{qnotice|Only sent to player who voted}}&amp;lt;br /&amp;gt;&lt;br /&gt;
{{begin-hl2msg|VoteRegistered|string}}&lt;br /&gt;
{{hl2msg|byte|vote|0 for No, 1 for Yes}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
===VotePass===&lt;br /&gt;
{{qnotice|Sent to all players after a vote passes.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VotePass|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{hl2msg|string|details|Vote success translation string}}&lt;br /&gt;
{{hl2msg|string|param1|Vote winner}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
===VoteFail===&lt;br /&gt;
{{qnotice|Sent to all players after a vote fails.}}&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{begin-hl2msg|VoteFail|string}}&lt;br /&gt;
{{hl2msg|byte|team|Team index or 255 for all}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
&lt;br /&gt;
== Events ==&lt;br /&gt;
{{qnotice|team is -1 when sent to all players)}}&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== vote_changed ===&lt;br /&gt;
{{begin-hl2msg|vote_changed|string}}&lt;br /&gt;
{{hl2msg|byte|yesVotes|}}&lt;br /&gt;
{{hl2msg|byte|noVotes|}}&lt;br /&gt;
{{hl2msg|byte|potentialVotes|}}&lt;br /&gt;
{{end-hl2msg}}&lt;br /&gt;
== Example voting plugin ==&lt;br /&gt;
This is a basic plugin that starts a vote, &amp;quot;Is gaben fat?&amp;quot;.  It does not ensure the same client does not vote multiple times, nor does it actually kick the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define L4D2_TEAM_ALL -1&lt;br /&gt;
&lt;br /&gt;
new yesvotes;&lt;br /&gt;
new novotes;&lt;br /&gt;
#define MAX_VOTES 4&lt;br /&gt;
&lt;br /&gt;
public OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegConsoleCmd(&amp;quot;testvote&amp;quot;,Callvote_Handler);&lt;br /&gt;
	RegConsoleCmd(&amp;quot;Vote&amp;quot;,vote);&lt;br /&gt;
}&lt;br /&gt;
public Action:Callvote_Handler(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:bf = StartMessageAll(&amp;quot;VoteStart&amp;quot;, USERMSG_RELIABLE);&lt;br /&gt;
&lt;br /&gt;
	BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
	BfWriteByte(bf, 0);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;#L4D_TargetID_Player&amp;quot;);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;Is gaben fat?&amp;quot;);&lt;br /&gt;
	BfWriteString(bf, &amp;quot;Server&amp;quot;);&lt;br /&gt;
	EndMessage();&lt;br /&gt;
	&lt;br /&gt;
	yesvotes = 0;&lt;br /&gt;
	novotes = 0;&lt;br /&gt;
	UpdateVotes();&lt;br /&gt;
	&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&lt;br /&gt;
public UpdateVotes()&lt;br /&gt;
{&lt;br /&gt;
	new Handle:msg = CreateEvent(&amp;quot;vote_changed&amp;quot;);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;yesVotes&amp;quot;,yesvotes);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;noVotes&amp;quot;,novotes);&lt;br /&gt;
	SetEventInt(msg,&amp;quot;potentialVotes&amp;quot;,MAX_VOTES);&lt;br /&gt;
	FireEvent(msg);&lt;br /&gt;
	&lt;br /&gt;
	if (yesvotes+novotes == MAX_VOTES)&lt;br /&gt;
	{&lt;br /&gt;
		PrintToServer(&amp;quot;voting complete!&amp;quot;);&lt;br /&gt;
		if (yesvotes &amp;gt; novotes)&lt;br /&gt;
		{&lt;br /&gt;
			new Handle:bf = StartMessageAll(&amp;quot;VotePass&amp;quot;);&lt;br /&gt;
			BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
			BfWriteString(bf, &amp;quot;#L4D_TargetID_Player&amp;quot;);&lt;br /&gt;
			BfWriteString(bf, &amp;quot;Gaben is fat&amp;quot;);&lt;br /&gt;
			EndMessage();&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			new Handle:bf = StartMessageAll(&amp;quot;VoteFailed&amp;quot;);&lt;br /&gt;
			BfWriteByte(bf, L4D2_TEAM_ALL);&lt;br /&gt;
			EndMessage();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
public Action:vote(client, args)&lt;br /&gt;
{&lt;br /&gt;
	new String:arg[8];&lt;br /&gt;
	GetCmdArg(1,arg,8);&lt;br /&gt;
	PrintToServer(&amp;quot;Got vote %s from %i&amp;quot;,arg,client);&lt;br /&gt;
	if (strcmp(arg,&amp;quot;Yes&amp;quot;,true) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		yesvotes++;&lt;br /&gt;
	}&lt;br /&gt;
	else if (strcmp(arg,&amp;quot;No&amp;quot;,true) == 0)&lt;br /&gt;
	{&lt;br /&gt;
		novotes++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	UpdateVotes();&lt;br /&gt;
	return Plugin_Continue;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[Left 4 Voting]]&lt;br /&gt;
* [[TF2 Voting]]&lt;br /&gt;
* [https://forums.alliedmods.net/showthread.php?t=162164 BuiltinVotes], a SourceMod extension that exposes a voting API that uses this voting system.&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Talk:Left_4_Voting_2&amp;diff=8951</id>
		<title>Talk:Left 4 Voting 2</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Talk:Left_4_Voting_2&amp;diff=8951"/>
		<updated>2013-06-13T07:39:06Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Wouldn't it be a better idea to hook the user message &amp;quot;VoteRegistered&amp;quot; and use const players[] (which length would be 1 and would contain the userid or clientid(?)) to determine which player did the vote? This would be more accurate than parsing the vote command and filtering out who has already voted since &amp;quot;VoteRegistered&amp;quot; would only be sent when the vote was actually accepted and not when someone attempts to vote (again or isn't allowed to).&lt;br /&gt;
:If you're manually creating the vote, the game server won't handle the vote processing for you and ''you'' will be responsible for sending the VoteRegistered UserMessage yourself based on what Vote command they send to you.&lt;br /&gt;
&lt;br /&gt;
:Granted, at this point you might as well use the SourceMod NativeVotes plugin as it's a wrapper for the L4D, L4D2, TF2, and CS:GO vote systems.&lt;br /&gt;
:--[[User:Powerlord|Powerlord]] ([[User talk:Powerlord|talk]]) 22:46, 12 June 2013 (CDT)&lt;br /&gt;
&lt;br /&gt;
::You're right, it's already mentioned in the explanation on how it works. However, the usermessage is not created and sent in the example. At this point I'm wondering what might happen in that case considering visual acknowledgement aka box getting checked. Does the engine handle it automatically with/on each vote command that's been received, is it the &amp;quot;vote_changed&amp;quot;-event that checks the box or is there no box-checking at all (and it's the usermessage that actually triggers this)? I guess I'll just check that out by my own later when I'll have time to play around with the usermessages.&lt;br /&gt;
::And yes, your plugin should usually be used as a wrapper, however, I'm actually interested in making it possible for a plugin to register votes and their momentary status and parameters. I was only mentioning the above because it crossed my mind during the read.&lt;br /&gt;
::--[[User:Dr. Greg House|Dr. Greg House]] ([[User talk:Dr. Greg House|talk]]) 02:37, 13 June 2013 (CDT)&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Talk:Left_4_Voting_2&amp;diff=8950</id>
		<title>Talk:Left 4 Voting 2</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Talk:Left_4_Voting_2&amp;diff=8950"/>
		<updated>2013-06-13T07:37:49Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Wouldn't it be a better idea to hook the user message &amp;quot;VoteRegistered&amp;quot; and use const players[] (which length would be 1 and would contain the userid or clientid(?)) to determine which player did the vote? This would be more accurate than parsing the vote command and filtering out who has already voted since &amp;quot;VoteRegistered&amp;quot; would only be sent when the vote was actually accepted and not when someone attempts to vote (again or isn't allowed to).&lt;br /&gt;
:If you're manually creating the vote, the game server won't handle the vote processing for you and ''you'' will be responsible for sending the VoteRegistered UserMessage yourself based on what Vote command they send to you.&lt;br /&gt;
&lt;br /&gt;
:Granted, at this point you might as well use the SourceMod NativeVotes plugin as it's a wrapper for the L4D, L4D2, TF2, and CS:GO vote systems.&lt;br /&gt;
:--[[User:Powerlord|Powerlord]] ([[User talk:Powerlord|talk]]) 22:46, 12 June 2013 (CDT)&lt;br /&gt;
&lt;br /&gt;
:You're right, it's already mentioned in the explanation on how it works. However, the usermessage is not created and sent in the example. At this point I'm wondering what might happen in that case considering visual acknowledgement aka box getting checked. Does the engine handle it automatically with/on each vote command that's been received, is it the &amp;quot;vote_changed&amp;quot;-event that checks the box or is there no box-checking at all (and it's the usermessage that actually triggers this)? I guess I'll just check that out by my own later when I'll have time to play around with the usermessages.&lt;br /&gt;
And yes, your plugin should usually be used as a wrapper, however, I'm actually interested in making it possible for a plugin to register votes and their momentary status and parameters. I was only mentioning the above because it crossed my mind during the read.&lt;br /&gt;
--[[User:Dr. Greg House|Dr. Greg House]] ([[User talk:Dr. Greg House|talk]]) 02:37, 13 June 2013 (CDT)&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Talk:Left_4_Voting_2&amp;diff=8948</id>
		<title>Talk:Left 4 Voting 2</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Talk:Left_4_Voting_2&amp;diff=8948"/>
		<updated>2013-06-13T03:23:40Z</updated>

		<summary type="html">&lt;p&gt;Dr. Greg House: Use a hook into &amp;quot;VoteRegistered&amp;quot; instead of hooking the vote command?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Wouldn't it be a better idea to hook the user message &amp;quot;VoteRegistered&amp;quot; and use const players[] (which length would be 1 and would contain the userid or clientid(?)) to determine which player did the vote? This would be more accurate than parsing the vote command and filtering out who has already voted since &amp;quot;VoteRegistered&amp;quot; would only be sent when the vote was actually accepted and not when someone attempts to vote (again or isn't allowed to).&lt;/div&gt;</summary>
		<author><name>Dr. Greg House</name></author>
		
	</entry>
</feed>