<?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=11530</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=11530"/>
	<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/Special:Contributions/11530"/>
	<updated>2026-06-05T21:03:35Z</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=8968</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=8968"/>
		<updated>2013-07-24T15:21:33Z</updated>

		<summary type="html">&lt;p&gt;11530: Changed incorrect native name from KeyValuesFromFile to KeyValuesToFile&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;
=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;KvJumpToKey&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;KvGetString&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;KvGotoNextKey&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;KvGotoFirstSubKey&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;KvGoBack&amp;lt;/tt&amp;gt; - Pops the traversal stack (moves up one level).&lt;br /&gt;
*&amp;lt;tt&amp;gt;KvRewind&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 String:steamid[], String:name[], maxlength)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:kv = CreateKeyValues(&amp;quot;MyFile&amp;quot;);&lt;br /&gt;
	FileToKeyValues(kv, &amp;quot;myfile.txt&amp;quot;);&lt;br /&gt;
	if (!KvJumpToKey(kv, steamid))&lt;br /&gt;
	{&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
	KvGetString(kv, &amp;quot;name&amp;quot;, name, maxlength);&lt;br /&gt;
	CloseHandle(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;KvJumpToKey&amp;lt;/tt&amp;gt; is a traversal native that changes the nesting level of the traversal stack.  However, &amp;lt;tt&amp;gt;CloseHandle&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 String:steamid[], String:name[], maxlength)&lt;br /&gt;
{&lt;br /&gt;
	new Handle:kv = CreateKeyValues(&amp;quot;MyFile&amp;quot;);&lt;br /&gt;
	FileToKeyValues(kv, &amp;quot;myfile.txt&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	if (!KvGotoFirstSubKey(kv))&lt;br /&gt;
	{&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	decl String:buffer[255];&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		KvGetSectionName(kv, buffer, sizeof(buffer));&lt;br /&gt;
		if (StrEqual(buffer, steamid))&lt;br /&gt;
		{&lt;br /&gt;
			KvGetString(kv, &amp;quot;name&amp;quot;, name, maxlength);&lt;br /&gt;
			CloseHandle(kv);&lt;br /&gt;
			return true;&lt;br /&gt;
		}&lt;br /&gt;
	} while (KvGotoNextKey(kv));&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(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;KvGotoNextKey&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;KvGoBack&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(Handle: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 (KvGotoFirstSubKey(kv, false))&lt;br /&gt;
		{&lt;br /&gt;
			// Current key is a section. Browse it recursively.&lt;br /&gt;
			BrowseKeyValues(kv);&lt;br /&gt;
			KvGoBack(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 (KvGetDataType(kv, 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 KvGetSectionName 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 (KvGotoNextKey(kv, 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;KvGotoFirstSubKey&amp;lt;/tt&amp;gt; is paired with a call to &amp;lt;tt&amp;gt;KvGoBack&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;KvGotoFirstSubKey&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;KvGotoNextKey&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;KvGotoFirstSubKey&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;KvGotoFirstSubKey&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;KvDeleteKey&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;KvDeleteThis&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;RemoveSteamID(const String:steamid[])&lt;br /&gt;
{&lt;br /&gt;
	new Handle:kv = CreateKeyValues(&amp;quot;MyFile&amp;quot;)&lt;br /&gt;
	FileToKeyValues(kv, &amp;quot;myfile.txt&amp;quot;)&lt;br /&gt;
	if (!KvGotoFirstSubKey(kv))&lt;br /&gt;
	{&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
	KvDeleteKey(kv, steamid)&lt;br /&gt;
	KvRewind(kv)&lt;br /&gt;
	KeyValuesToFile(kv, &amp;quot;myfile.txt&amp;quot;)&lt;br /&gt;
	CloseHandle(kv)&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Note:''' We called &amp;lt;tt&amp;gt;KvRewind&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;KvDeleteThis&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 String:steamid[])&lt;br /&gt;
{&lt;br /&gt;
	new Handle:kv = CreateKeyValues(&amp;quot;MyFile&amp;quot;)&lt;br /&gt;
	FileToKeyValues(kv, &amp;quot;myfile.txt&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	if (!KvGotoFirstSubKey(kv))&lt;br /&gt;
	{&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	decl String:buffer[255]&lt;br /&gt;
	do&lt;br /&gt;
	{&lt;br /&gt;
		KvGetSectionName(kv, buffer, sizeof(buffer))&lt;br /&gt;
		if (StrEqual(buffer, steamid))&lt;br /&gt;
		{&lt;br /&gt;
			KvDeleteThis(kv)&lt;br /&gt;
			CloseHandle(kv)&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
	} while (KvGotoNextKey(kv))&lt;br /&gt;
&lt;br /&gt;
	CloseHandle(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;KvDeleteThis&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;KvGotoNextKey&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(Handle:kv)&lt;br /&gt;
{&lt;br /&gt;
	if (!KvGotoFirstSubKey(kv))&lt;br /&gt;
	{&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (;;)&lt;br /&gt;
	{&lt;br /&gt;
		decl String:name[4]&lt;br /&gt;
		KvGetString(kv, name, sizeof(name))&lt;br /&gt;
		if (name[0] == '\0')&lt;br /&gt;
		{&lt;br /&gt;
			if (KvDeleteThis(kv) &amp;lt; 1)&lt;br /&gt;
			{&lt;br /&gt;
				break&lt;br /&gt;
			}&lt;br /&gt;
		} else if (!KvGotoNextKey(kv)) {&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;KvDeleteThis&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;KvGotoNextKey&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;new Handle:kv = CreateKeyValues(&amp;quot;MyFile&amp;quot;);&lt;br /&gt;
KvJumpToKey(kv, &amp;quot;STEAM_0:0:7&amp;quot;, true);&lt;br /&gt;
KvSetString(kv, &amp;quot;name&amp;quot;, &amp;quot;crab&amp;quot;);&lt;br /&gt;
KvRewind(kv);&lt;br /&gt;
KeyValuesToFile(kv, &amp;quot;C:\javalia.txt&amp;quot;);&lt;br /&gt;
CloseHandle(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>11530</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Entity_References_(SourceMod)&amp;diff=8619</id>
		<title>Entity References (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Entity_References_(SourceMod)&amp;diff=8619"/>
		<updated>2012-08-09T20:47:14Z</updated>

		<summary type="html">&lt;p&gt;11530: /* Using References to handle a logic entity */ Changing code to use references instead of indices&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;An entity reference is a combination of a standard entity index (used for all entities in SourceMod previously) and a pseudo-unique serial number that identifies the entity. Since entities are constantly being created and destroyed a cached index doesn't guarantee the underlying entity is the same, references provide a way of verifying the same entity still exists. Generally speaking, if you have an entity index you wish to cache you should convert it to a reference first. All natives (provided by SourceMod) should be able to accept references in place of indexes (wherever the parameter asks for an entity - Not clients), and these will check use the serial to check that the entity index contained in the reference is the same as when the reference was created.&lt;br /&gt;
&lt;br /&gt;
SourceMod now also supports handling of logical (non-networked entities) and these use references exclusively. Natives that used to return an entity index will continue to do so in the case of networked entities (for backwards compatability) and will only return a reference for non-networked ones.&lt;br /&gt;
&lt;br /&gt;
==Converting a backwards-compat index/ref to a guaranteed reference for safely caching==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
new index = GetEntPropEnt(client, Prop_Send, &amp;quot;m_hActiveWeapon&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
/* Convert our backwards-compat index/ref to a guaranteed reference */&lt;br /&gt;
new ref = EntIndexToEntRef(index);&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Converting a reference back to an entity index==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
new index = EntRefToEntIndex(ref);&lt;br /&gt;
&lt;br /&gt;
if (index == INVALID_ENT_REFERENCE)&lt;br /&gt;
{&lt;br /&gt;
	/* Reference wasn't valid - Entity must have been deleted */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Using References to handle a logic entity==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
new ent = -1;  &lt;br /&gt;
   &lt;br /&gt;
while ((ent = FindEntityByClassname(ent, &amp;quot;logic_auto&amp;quot;)) != -1)  &lt;br /&gt;
{  &lt;br /&gt;
    /**   &lt;br /&gt;
     * Get the entity index from this reference - This works on references and indexes for convenience.   &lt;br /&gt;
     * Should be a number &amp;gt;2048 since logic_auto is a non networked entity   &lt;br /&gt;
     */  &lt;br /&gt;
    new ref = EntIndexToEntRef(ent);  &lt;br /&gt;
   &lt;br /&gt;
    /* Fire an input on this entity - We use the reference version since this includes a serial check */  &lt;br /&gt;
    AcceptEntityInput(ref, &amp;quot;Kill&amp;quot;);  &lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;/div&gt;</summary>
		<author><name>11530</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Overriding_Command_Access_(SourceMod)&amp;diff=8577</id>
		<title>Overriding Command Access (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Overriding_Command_Access_(SourceMod)&amp;diff=8577"/>
		<updated>2012-06-25T01:04:17Z</updated>

		<summary type="html">&lt;p&gt;11530: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This article explains how, without editing plugin source code, you can change the flags or permissions on any command, either globally, or for a specific group.&lt;br /&gt;
&lt;br /&gt;
=Introduction=&lt;br /&gt;
Command access overrides are one of the most powerful aspects of the SourceMod administration system.  They effectively let you:&lt;br /&gt;
*Change the access to any admin command without modifying plugin source code;&lt;br /&gt;
*Change the access to entire groups of commands without modifying source code;&lt;br /&gt;
*Create custom access levels;&lt;br /&gt;
*Allow or deny commands and groups of commands to an administrator group regardless of the command flags.&lt;br /&gt;
&lt;br /&gt;
When you change the permissions to a SourceMod object, it is called an ''override''.  An override is an arbitrary string.  If the override string matches a command name, then the command permissions will be inherited from that override.&lt;br /&gt;
&lt;br /&gt;
This concept is important for two reasons:&lt;br /&gt;
*An override can change the permissions of a command.&lt;br /&gt;
*An override can be used as a custom access flag.&lt;br /&gt;
&lt;br /&gt;
For example, a plugin might require access &amp;quot;g&amp;quot; to use the &amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt; command.  However, an override could explicitly allow/deny usage of this command to a given group, and/or it could change the default flag for &amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt; to be &amp;quot;k&amp;quot; instead.&lt;br /&gt;
&lt;br /&gt;
More interestingly, a plugin could require &amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt; access in order to use a specific menu option.  In this case, a user would have to be able to access &amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt;, rather than have a hardcoded flag.&lt;br /&gt;
&lt;br /&gt;
As a final example of the flexibility of this system, a plugin might say that users must be able to access &amp;lt;tt&amp;gt;plugin_crab_usage&amp;lt;/tt&amp;gt;, which isn't a command at all.  Instead, the plugin assumes a ''default'' access level internally, and users can opt to override it as they wish.  This demonstrates that overrides are separate from commands, however, commands themselves inherit their permissions from overrides of the same name.&lt;br /&gt;
&lt;br /&gt;
=Override Types=&lt;br /&gt;
Overrides come in two flavors: ''command'' overrides and ''command group'' overrides.  Command overrides are overrides that, if they have the same name as a command, that command will automatically inherit that override's permissions.&lt;br /&gt;
&lt;br /&gt;
Similarly, a ''command group'' override is an override that which, if a command has the same group name as a ''command group'' override, that command will inherit the override's permissions.&lt;br /&gt;
&lt;br /&gt;
Example 1: If a command override exists for &amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt;, any admin command named &amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt; will inherit those permissions.&lt;br /&gt;
&lt;br /&gt;
Example 2: If a command group override exists for &amp;lt;tt&amp;gt;CSDM&amp;lt;/tt&amp;gt;, any admin command labelled as a &amp;quot;CSDM&amp;quot; command will inherit those permissions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Global Configuration=&lt;br /&gt;
Access levels for overrides can be globally reconfigured via &amp;lt;tt&amp;gt;configs/admin_overrides.cfg&amp;lt;/tt&amp;gt;.  The file format is very simple:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Overrides&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;[name1]&amp;quot;	&amp;quot;[flags]&amp;quot;&lt;br /&gt;
	&amp;quot;@[group1]&amp;quot;	&amp;quot;[flags]&amp;quot;&lt;br /&gt;
	/* ... */&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Command groups are specified with an 'at' sign ('@') preceding the name.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Overrides&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;sm_map&amp;quot;	&amp;quot;k&amp;quot;	//Change &amp;quot;sm_map&amp;quot; to the &amp;quot;k&amp;quot; flag.&lt;br /&gt;
	&amp;quot;@CSDM&amp;quot;		&amp;quot;m&amp;quot;	//Change all CSDM commands to the &amp;quot;m&amp;quot; flag.&lt;br /&gt;
	&amp;quot;sm_chat&amp;quot;	&amp;quot;&amp;quot;	//Allow anyone to use &amp;quot;sm_chat&amp;quot;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that given an arbitrary number of flags, the client must require access to '''all''' specified flags in the string, as opposed to just any one of them.&lt;br /&gt;
&lt;br /&gt;
=Group Configuration=&lt;br /&gt;
Group overrides are given on an allow or deny basis.  That is, rather than changing flags per-group, the override is simply whether it is allowed or denied to members of that group.  &lt;br /&gt;
&lt;br /&gt;
For more information, visit [[Adding_Groups_%28SourceMod%29#File_Format|Adding Groups]].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>11530</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourceMod_Profiler&amp;diff=8435</id>
		<title>SourceMod Profiler</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourceMod_Profiler&amp;diff=8435"/>
		<updated>2012-03-29T21:26:25Z</updated>

		<summary type="html">&lt;p&gt;11530: Changing example code from &amp;quot;*&amp;quot; to &amp;quot;adminhelp&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The SourceMod profiler is a built-in tool for measuring the performance of SourceMod natives and plugins.  &lt;br /&gt;
&lt;br /&gt;
It is currently an &amp;quot;alpha&amp;quot; addition.  While it was tested to work, there may be cases where it breaks.  Do not use it in a production environment -- it is intended as a tool for developers.&lt;br /&gt;
&lt;br /&gt;
=Introduction=&lt;br /&gt;
The SourceMod Profiler tracks &amp;quot;profilable items.&amp;quot;  These items are:&lt;br /&gt;
* Native calls from plugins.&lt;br /&gt;
* Callbacks into plugins.&lt;br /&gt;
* Internal function calls in plugins.&lt;br /&gt;
&lt;br /&gt;
The following statistics are tracked for each item:&lt;br /&gt;
* Number calls.&lt;br /&gt;
* Total time spent in all calls.&lt;br /&gt;
* Minimum time spent in any call.&lt;br /&gt;
* Maximum time spent in any call.&lt;br /&gt;
&lt;br /&gt;
These statistics can be used to gauge how well your plugin is performing.  It can also be used to tweak and optimize your code where necessary.&lt;br /&gt;
&lt;br /&gt;
=Usage=&lt;br /&gt;
==Configuration==&lt;br /&gt;
The profiler can only be enabled in &amp;lt;tt&amp;gt;configs/plugin_settings.cfg&amp;lt;/tt&amp;gt;.  You can enable it on any number of plugins at a time.  However, it is a good idea to only profile what you need.&lt;br /&gt;
&lt;br /&gt;
The profiler can be configured with the &amp;quot;profile&amp;quot; option key in an &amp;quot;Options&amp;quot; section.  The value should be a bits added up:&lt;br /&gt;
* 1 - Profile natives.&lt;br /&gt;
* 2 - Profile callbacks.&lt;br /&gt;
* 4 - Profile functions (implies 2).&lt;br /&gt;
&lt;br /&gt;
Example for profiling &amp;quot;adminhelp&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;quot;adminhelp&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;pause&amp;quot;			&amp;quot;no&amp;quot;&lt;br /&gt;
		&amp;quot;lifetime&amp;quot;		&amp;quot;mapsync&amp;quot;&lt;br /&gt;
		&lt;br /&gt;
		&amp;quot;Options&amp;quot;&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;debug&amp;quot;		&amp;quot;no&amp;quot;&lt;br /&gt;
			&amp;quot;profile&amp;quot;	&amp;quot;7&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that one can also use the &amp;quot;*&amp;quot; wildcard (as seen in the default config) in place of a plugin name in order to change the settings of all loaded plugins.&lt;br /&gt;
&lt;br /&gt;
==Getting Results==&lt;br /&gt;
You can use &amp;quot;sm profiler flush&amp;quot; in your console to dump and reset the profiler statistics.  The output will be written to an XML file in your &amp;lt;tt&amp;gt;sourcemod/logs&amp;lt;/tt&amp;gt; folder.  For example: &amp;lt;tt&amp;gt;addons/sourcemod/logs/profile_1204422886.xml&amp;lt;/tt&amp;gt; (note that a timestamp is included).&lt;br /&gt;
&lt;br /&gt;
There is no way to stop the profiler entirely save from editing &amp;lt;tt&amp;gt;plugin_settings.cfg&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Interpreting Results=&lt;br /&gt;
[[image:Sm_profiler.PNG|thumb|right|300px|Profiler Screenshot]]&lt;br /&gt;
The easiest way to interpret the results is to use the &amp;lt;tt&amp;gt;profviewer.exe&amp;lt;/tt&amp;gt; tool available on SourceMod's downloads page.  It requires the [http://www.microsoft.com/downloads/details.aspx?FamilyID=0856EACB-4362-4B0D-8EDD-AAB15C5E04F5&amp;amp;displaylang=en .NET 2.0 Framework].  You can open profiler .xml files with this tool and sort the various result metrics (descending order only).&lt;br /&gt;
&lt;br /&gt;
*All times are in seconds.  The &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; times are single measurements.  The &amp;quot;average&amp;quot; time is the total time spent divided by the number of calls.  &lt;br /&gt;
*Function names are displayed as raw names for natives, or &amp;quot;a!b&amp;quot; for plugins, where &amp;quot;a&amp;quot; is the plugin filename and &amp;quot;b&amp;quot; is the function name.&lt;br /&gt;
&lt;br /&gt;
It is important to note how the time values are actually computed.  You may find the results to be deceiving otherwise.  Take the following list of events:&lt;br /&gt;
*Core calls &amp;lt;tt&amp;gt;OnClientCommand&amp;lt;/tt&amp;gt; in plugin &amp;lt;tt&amp;gt;a.smx&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;a.smx!OnClientCommand&amp;lt;/tt&amp;gt; calls &amp;lt;tt&amp;gt;a.smx!StrEqual&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;a.smx!StrEqual&amp;lt;/tt&amp;gt; calls &amp;lt;tt&amp;gt;strcmp()&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SourceMod computes each item's time by adding in its children's time.  For example, the time &amp;lt;tt&amp;gt;t(a.smx!OnClientCommand)&amp;lt;/tt&amp;gt; will be equal to:&lt;br /&gt;
&amp;lt;pre&amp;gt;t(a.smx!OnClientCommand) + t(a.smx!StrEqual) + t(strcmp)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Likewise, the time of &amp;lt;tt&amp;gt;StrEqual&amp;lt;/tt&amp;gt; will be equal to:&lt;br /&gt;
&amp;lt;pre&amp;gt;t(a.smx!StrEqual) + t(strcmp)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This has two major implications:&lt;br /&gt;
*'''You cannot profile recursive functions'''.  The data will be nonsensical.  Use &amp;lt;tt&amp;gt;profiler.inc&amp;lt;/tt&amp;gt; for manually timing recursive calls.&lt;br /&gt;
*The time of re-entrant functions may be deceiving.  For example, &amp;lt;tt&amp;gt;DisplayMenu()&amp;lt;/tt&amp;gt; may appear very slow, but that's because it may fire dozens of callbacks.  Similarly, the custom sorting natives' time will include their callbacks' time.  That doesn't necessarily mean the natives themselves are slow, but that they are slow because of the callbacks they are invoking.&lt;br /&gt;
&lt;br /&gt;
''Note: The profiler does not include its own internal operations when timing items.  There is a small margin of error, however.''&lt;br /&gt;
&lt;br /&gt;
In general, you should not go insane trying to optimize unless there is a good reason.  For example, an &amp;lt;tt&amp;gt;OnGameFrame&amp;lt;/tt&amp;gt; callback may be called 200 times per minute, and thus if its average time exceeds reasonable values (say, a dozen milliseconds) then you may want to consider optimizing it.  However, if you see that a particular call to &amp;lt;tt&amp;gt;LoadTranslations()&amp;lt;/tt&amp;gt; is taking 150ms, you shouldn't worry too much - that function only gets called on load.&lt;br /&gt;
&lt;br /&gt;
Use your judgment when interpreting the results, and don't go crazy.  If you need advice, post on the forums.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=File Format=&lt;br /&gt;
The file format is XML.  The contents are fairly self-explanatory, as there are only three elements.  Sample parsers are included in the SourceMod source code tree:&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;tt&amp;gt;tools/csharp&amp;lt;/tt&amp;gt; - Source code for the profviewer.exe program.  &amp;lt;tt&amp;gt;ProfReport.cs&amp;lt;/tt&amp;gt; implements the parser.&lt;br /&gt;
*&amp;lt;tt&amp;gt;php/ProfFileParser.class.php&amp;lt;/tt&amp;gt; - Source code for a PHP parser.  Requires PHP5 and libexpat (XML) support.&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Development]]&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;/div&gt;</summary>
		<author><name>11530</name></author>
		
	</entry>
</feed>