<?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=MisakaSora</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=MisakaSora"/>
	<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/Special:Contributions/MisakaSora"/>
	<updated>2026-06-06T00:21:54Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.6</generator>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=User:MisakaSora&amp;diff=10878</id>
		<title>User:MisakaSora</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=User:MisakaSora&amp;diff=10878"/>
		<updated>2019-11-06T08:23:14Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: add a dd declaration.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;我是个DD。&lt;br /&gt;
&lt;br /&gt;
来，跟我一起成为DD把。&lt;br /&gt;
&lt;br /&gt;
每周五晚19:30，[http://live.bilibili.com/14578426 战斗吧歌姬]见。&lt;br /&gt;
&lt;br /&gt;
我永远喜欢卡缇娅·乌拉诺娃、神宫司玉藻、李清歌、墨汐、伊莎贝拉·霍利、罗兹·巴雷特。&lt;br /&gt;
&lt;br /&gt;
人之初，性本D。&lt;br /&gt;
誰でも，大好き!&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_Admins_(SourceMod)&amp;diff=10865</id>
		<title>SQL Admins (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_Admins_(SourceMod)&amp;diff=10865"/>
		<updated>2019-09-20T10:06:30Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: add a language template&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SQL Admins (SourceMod)}}&lt;br /&gt;
SourceMod has support for loading and managing admins via SQL, using any of the supported SQL drivers provided (currently, MySQL and SQLite).  This can be very advantageous if you have a large number of admins, you wish to manage your admins via external tools, or you wish to share admins across servers.  &lt;br /&gt;
&lt;br /&gt;
This article will explain how to set up your server to use admins through SQL.  No knowledge of SQL is required for basic administration; however, by using this feature you should realize that SourceMod does not provide easy functionality for full SQL control.  You will need to use a third party tool or learn SQL yourself if you need greater functionality.&lt;br /&gt;
&lt;br /&gt;
However, it is assumed that you:&lt;br /&gt;
*Have access to an SQL database;&lt;br /&gt;
*Know how to access the SQL database;&lt;br /&gt;
*Know how to execute commands or scripts on the SQL database.&lt;br /&gt;
&lt;br /&gt;
=Configuration=&lt;br /&gt;
==MySQL==&lt;br /&gt;
Open &amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt; on your server.  You should see a section called &amp;lt;tt&amp;gt;&amp;quot;default&amp;quot;&amp;lt;/tt&amp;gt;.  You can either use this section, or create a new one called &amp;lt;tt&amp;gt;&amp;quot;admins&amp;quot;&amp;lt;/tt&amp;gt; if you wish to separate your connections.  You must fill out the &amp;lt;tt&amp;gt;driver&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;host&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;database&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;user&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;pass&amp;lt;/tt&amp;gt; fields.  If you do not need a field, use empty quotation marks as the value (&amp;lt;tt&amp;gt;&amp;quot;&amp;quot;&amp;lt;/tt&amp;gt;).  A restart of each 'srcds' instance that will be using the 'SQL Admins' is required for the database support to take affect.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;quot;default&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;driver&amp;quot;			&amp;quot;mysql&amp;quot;&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;myaccount&amp;quot;&lt;br /&gt;
		&amp;quot;pass&amp;quot;				&amp;quot;mypassword&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==SQLite==&lt;br /&gt;
Add a section to your server's &amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt; file as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;quot;admins&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;driver&amp;quot;			&amp;quot;sqlite&amp;quot;&lt;br /&gt;
		&amp;quot;database&amp;quot;			&amp;quot;admins-sqlite&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may choose to name the database any valid file name.  We chose &amp;quot;admins-sqlite&amp;quot; because a pre-made database, &amp;quot;admins-sqlite.sq3&amp;quot; is available in &amp;lt;tt&amp;gt;configs/sql-init-scripts/sqlite&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Database Setup=&lt;br /&gt;
==Automatic==&lt;br /&gt;
Make sure &amp;lt;tt&amp;gt;sql-admin-manager.smx&amp;lt;/tt&amp;gt; is loaded, and then run the &amp;lt;tt&amp;gt;sm_create_adm_tables&amp;lt;/tt&amp;gt; command from your '''server console'''.  If using MySQL, you must have &amp;lt;tt&amp;gt;CREATE&amp;lt;/tt&amp;gt; permissions on your database.&lt;br /&gt;
&lt;br /&gt;
==Manual==&lt;br /&gt;
===MySQL===&lt;br /&gt;
If you have not already created a database on your MySQL server, do so.  &lt;br /&gt;
&lt;br /&gt;
Find the script &amp;lt;tt&amp;gt;configs/sql-init-scripts/mysql/create_admins.sql&amp;lt;/tt&amp;gt; in the SourceMod distribution.  Then, using either the command line or a tool such as [http://www.phpmyadmin.net/ phpMyAdmin], run its contents.  This will create the necessary tables.&lt;br /&gt;
&lt;br /&gt;
===SQLite===&lt;br /&gt;
SourceMod distributes a pre-made SQLite database file with all of the admin tables created.  If you wish to manually create a database or add tables to a pre-existing database, use the contents of the &amp;lt;tt&amp;gt;configs/sql-init-scripts/sqlite/create_admins.sql&amp;lt;/tt&amp;gt; script.&lt;br /&gt;
&lt;br /&gt;
Copy the &amp;lt;tt&amp;gt;configs/sql-init-scripts/sqlite/admins-sqlite.sq3&amp;lt;/tt&amp;gt; binary from the SourceMod distribution into your server's &amp;lt;tt&amp;gt;data/sqlite&amp;lt;/tt&amp;gt; folder.  If you chose to rename the database in &amp;lt;tt&amp;gt;databases.cfg&amp;lt;/tt&amp;gt;, you should rename this file as well.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Enabling Plugins=&lt;br /&gt;
By default, all of the SQL plugins are located in the &amp;lt;tt&amp;gt;plugins/disabled&amp;lt;/tt&amp;gt; folder on your server -- they are not loaded by default.  To enable plugins, move them from the &amp;lt;tt&amp;gt;plugins/disabled&amp;lt;/tt&amp;gt; folder and into &amp;lt;tt&amp;gt;plugins&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You will usually want to enable &amp;lt;tt&amp;gt;sql-admin-manager.smx&amp;lt;/tt&amp;gt;.  This plugin adds some helpful console commands for very basic admin management.  However, you should only enable '''ONE''' of the other two SQL plugins:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;tt&amp;gt;admin-sql-prefetch.smx&amp;lt;/tt&amp;gt;: This plugin is ideal for small to medium sized databases without much connection lag.  It loads the entire database admin contents at map load (or whenever a refresh is requested).  If you have serious database delay or want to be able to add/delete/edit admins without refreshing the per-map cache on each server,  this isn't the plugin for you.&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;PROS:&amp;lt;/b&amp;gt; Very simple plugin with no extra complexity.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;CONS:&amp;lt;/b&amp;gt; Gameserver will stall whenever the cache is refreshed (mapload or when using &amp;lt;tt&amp;gt;sm_reloadadmins&amp;lt;/tt&amp;gt;).  If you add, remove, or edit admins, the changes are not reflected until the cache is refreshed.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;tt&amp;gt;admin-sql-threaded.smx&amp;lt;/tt&amp;gt;: This plugin is completely threaded.  This makes it much more complex, and thus it is currently &amp;quot;experimental&amp;quot; (although it has been tested to work fine).  It pre-caches all group and override information.  Admin-lookup is done completely dynamically.&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;PROS:&amp;lt;/b&amp;gt; A faulty/slow database connection will never halt the server.  If you add/remove/edit admins, the changes will be reflected as soon as the player connects to the server.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;CONS:&amp;lt;/b&amp;gt; It is very complex and thus not trivial to edit for custom changes.  It also may have unexpected functionality (for example, &amp;lt;tt&amp;gt;sm_reloadadmins&amp;lt;/tt&amp;gt; will have a slight delayed reaction because the plugin is multi-threaded).&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have enabled the manager, and enabled &amp;lt;b&amp;gt;ONE&amp;lt;/b&amp;gt; of the &amp;lt;tt&amp;gt;admin-sql&amp;lt;/tt&amp;gt; plugins, you are ready to go!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Updating Tables=&lt;br /&gt;
==Automatic==&lt;br /&gt;
Make sure &amp;lt;tt&amp;gt;sql-admin-manager.smx&amp;lt;/tt&amp;gt; is loaded, and run &amp;lt;tt&amp;gt;sm_update_adm_tables&amp;lt;/tt&amp;gt; from your server console.  If using MySQL, you must have &amp;lt;tt&amp;gt;CREATE&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ALTER&amp;lt;/tt&amp;gt; permissions for the database.&lt;br /&gt;
&lt;br /&gt;
==Manual==&lt;br /&gt;
If you are using an older version of the SourceMod table layouts, it is important to update the table layout for the latest schema. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;In summary&amp;lt;/b&amp;gt;:&lt;br /&gt;
*Back up all of your data.&lt;br /&gt;
*Find what revision your tables were created against.  Unfortunately the only way to do this right now is by remembering the version you used.&lt;br /&gt;
*Find all of the &amp;lt;tt&amp;gt;update_admins-rXXXX&amp;lt;/tt&amp;gt; scripts in your driver's folder, where &amp;lt;tt&amp;gt;XXXX&amp;lt;/tt&amp;gt; is higher than the version from the second step, and &amp;lt;tt&amp;gt;XXXX&amp;lt;/tt&amp;gt; is lower than or equal to the current version you're using.&lt;br /&gt;
*Execute each matching script in order of increasing revision number (lowest first, highest last).&lt;br /&gt;
*Upgrade the SQL plugin you use, and any 3rd party software that relies on the admin tables.&lt;br /&gt;
&lt;br /&gt;
Scripts for updating table structures are in &amp;lt;tt&amp;gt;configs/sql-init-scripts/&amp;amp;lt;driver&amp;amp;gt;/update_admins-rXXXX&amp;lt;/tt&amp;gt;, where XXXX is the revision number.  &lt;br /&gt;
&lt;br /&gt;
A potential scenario is provided below (&amp;lt;b&amp;gt;it is 100% fabricated; actual revision numbers are entirely different&amp;lt;/b&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
Your tables were created with SourceMod r2978.  The current version is 3512.  Say the following files might exist:&lt;br /&gt;
*&amp;lt;tt&amp;gt;update_admins-r2500.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;update_admins-r3243.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;update_admins-r3444.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, you should run the scripts for &amp;lt;tt&amp;gt;3243&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;3444&amp;lt;/tt&amp;gt;, in that order, to bring your table up to date.  All data will be fully retained, but it is always recommended that you backup your data before proceeding with database changes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Management Commands=&lt;br /&gt;
As a convenience, SourceMod provides a few basic SQL admin management commands via the &amp;lt;tt&amp;gt;sql-admin-manager.smx&amp;lt;/tt&amp;gt; plugin.  All of the commands require the &amp;lt;tt&amp;gt;root&amp;lt;/tt&amp;gt; admin flag.&lt;br /&gt;
&lt;br /&gt;
The following conventions are used:&lt;br /&gt;
*When &amp;amp;lt;authtype&amp;amp;gt; is requested, it means one of the following three values: &amp;lt;tt&amp;gt;steam&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ip&amp;lt;/tt&amp;gt;, or &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;.&lt;br /&gt;
*When &amp;amp;lt;identity&amp;amp;gt; is requested, it should be the unique string to be paired with the &amp;lt;tt&amp;gt;authtype&amp;lt;/tt&amp;gt;.  For example, a &amp;lt;tt&amp;gt;steam&amp;lt;/tt&amp;gt; identity would be a Steam ID.  An &amp;lt;tt&amp;gt;ip&amp;lt;/tt&amp;gt; identity would be an IP address, and a &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; identity would be a Half-Life 2 player name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note: if a string has spaces, it must be in quotes!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note: A colon is a break character and Steam IDs must be in quotes!&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Command&lt;br /&gt;
| Format&lt;br /&gt;
| Description&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_sql_addadmin&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;alias&amp;gt; &amp;lt;authtype&amp;gt; &amp;lt;identity&amp;gt; &amp;lt;flags&amp;gt; [immunity] [password]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Adds a new entry to the &amp;lt;tt&amp;gt;sm_admins&amp;lt;/tt&amp;gt; table.  The &amp;lt;tt&amp;gt;alias&amp;lt;/tt&amp;gt; can be any value and is usually used to assign a readable name to an IP/SteamID.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_sql_deladmin&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;authtype&amp;gt; &amp;lt;identity&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Removes an admin.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_sql_addgroup&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;name&amp;gt; &amp;lt;flags&amp;gt; &amp;lt;immunity&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Adds a new group with the specified flags and immunity&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_sql_delgroup&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Removes the specified group.  Quotation marks are optional if the name has odd characters.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot; &lt;br /&gt;
| sm_sql_setadmingroups&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;authtype&amp;gt; &amp;lt;identity&amp;gt; [group1] ... [group N]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Sets an admin's group list.  The inheritance order is the order the groups are specified in.  This sets, and does not add; thus specifying no groups removes the user from all groups.  Example:&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_sql_setadmingroups steam &amp;quot;STEAM_0:1:16&amp;quot; &amp;quot;Full Admins&amp;quot;&amp;lt;/tt&amp;gt;&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_create_adm_tables&lt;br /&gt;
| &lt;br /&gt;
| Creates the administration tables for SourceMod.  Can only be run from the server console.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_update_adm_tables&lt;br /&gt;
|&lt;br /&gt;
| Updates the administration tables to the latest schema, preserving data as applicable.  Can only be run from the server console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
&amp;lt;b&amp;gt;Q:&amp;lt;/b&amp;gt; Can I use &amp;lt;tt&amp;gt;admin-sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;admin-flatfile&amp;lt;/tt&amp;gt; at the same time?&lt;br /&gt;
&amp;lt;b&amp;gt;A:&amp;lt;/b&amp;gt; Yes.  The data will be merged together in SourceMod's cache.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Q:&amp;lt;/b&amp;gt; Can I use both the &amp;lt;tt&amp;gt;threaded&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;prefetch&amp;lt;/tt&amp;gt; SQL plugins at the same time?&lt;br /&gt;
&amp;lt;b&amp;gt;A:&amp;lt;/b&amp;gt; No.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Q:&amp;lt;/b&amp;gt; Can duplicate groups/admins be in the flat files and the SQL database?&lt;br /&gt;
&amp;lt;b&amp;gt;A:&amp;lt;/b&amp;gt; Yup.  They will be merged safely.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Schemas=&lt;br /&gt;
This section documents the required portions of the admin table schema.  You do not need to read or learn this unless you plan to use SQL directly.&lt;br /&gt;
&lt;br /&gt;
The exact schemas for each driver are located in &amp;lt;tt&amp;gt;configs/sql-init-scripts&amp;lt;/tt&amp;gt;.  The purpose of this document is to explain the fields rather than list the exact structures.&lt;br /&gt;
&lt;br /&gt;
==sm_admins==&lt;br /&gt;
This table is used to store administrators.  Although the primary key is &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt;, applications should enforce that &amp;lt;tt&amp;gt;authtype&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;identity&amp;lt;/tt&amp;gt; have no combined duplicates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; (auto increments).&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| id&lt;br /&gt;
| integer&lt;br /&gt;
| Unique integer identifying the row.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| authtype&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
Constrained to 'steam', 'name', or 'ip'&lt;br /&gt;
| Authentication type the identity is against.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| identity&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Steam ID, name, or IP address.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| password&lt;br /&gt;
| string&lt;br /&gt;
| Password, if any, the admin must use.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| flags&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Permission flag string.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| name&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Alias used for external tools (all but ignored in SourceMod).&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| immunity&lt;br /&gt;
| integer NOT NULL&lt;br /&gt;
| Immunity level value.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_groups==&lt;br /&gt;
This table is used to store all groups entries.  Although the primary key is &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt;, applications should enforce that the &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; field stays unique.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; (auto increments).&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| id&lt;br /&gt;
| integer&lt;br /&gt;
| Unique integer identifying the row.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| flags&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Permissions flag string.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| name&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Unique name of the group.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| immunity_level&lt;br /&gt;
| integer NOT NULL&lt;br /&gt;
| Immunity level value.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_admins_groups==&lt;br /&gt;
This table is used to map admins to the groups they will inherit.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;admin_id&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;group_id&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| admin_id &lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_admins.id&amp;lt;/tt&amp;gt; field.  Specifies the admin inheriting the group.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| group_id&lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_groups.id&amp;lt;/tt&amp;gt; field.  Specifies the group the admin is inheriting.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| inherit_order&lt;br /&gt;
| integer NOT NULL&lt;br /&gt;
| Order of inheritance for the given admin.  Lower means earlier inheritance.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_group_immunity==&lt;br /&gt;
This table is used to map which groups are immune from other groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;group_id&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;other_id&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| group_id&lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_groups.id&amp;lt;/tt&amp;gt; field.  Specifies the group gaining immunity.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| other_id&lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_groups.id&amp;lt;/tt&amp;gt; field.  Specifies who &amp;lt;tt&amp;gt;group_id&amp;lt;/tt&amp;gt; is becoming immune from.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==sm_group_overrides==&lt;br /&gt;
This table is used to specify group-based command overrides.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;group_id&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| group_id&lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_groups.id&amp;lt;/tt&amp;gt; field.  Specifies the group the override is for.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| type&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
Constrained to 'command' or 'group'.&lt;br /&gt;
| Specifies whether the override is a command or a command group.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| name&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Command name.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| access&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
Constrained to 'allow' or 'deny'.&lt;br /&gt;
| Whether the command is allowed or denied to this group.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_overrides==&lt;br /&gt;
This table is used to specify global command overrides.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| type&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
Constrained to 'command' or 'group'.&lt;br /&gt;
| Specifies whether the override is a command or a command group.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| name&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Command name.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| flags&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Permissions flag string.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_config==&lt;br /&gt;
This table is used to specify configuration options.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;cfg_key&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| cfg_key&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Configuration key.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| cfg_value&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Configuration value.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
''Note:'' As of this writing, the only configuration value is &amp;lt;tt&amp;gt;admin_version&amp;lt;/tt&amp;gt;, and it specifies the revision the schema last changed.&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_Admins_(SourceMod)/zh&amp;diff=10864</id>
		<title>SQL Admins (SourceMod)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_Admins_(SourceMod)/zh&amp;diff=10864"/>
		<updated>2019-09-20T10:05:02Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: half translated&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SQL Admins (SourceMod)}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
SourceMod已经支持通过SQL来加载管理admin，现在支持是数据库是MySQL和SQLite。如果你有大量管理员、你想通过外部工具管理或者你想在不同服务器中 共享管理员，使用数据库是非常有效的。 &lt;br /&gt;
&lt;br /&gt;
本文会介绍如何配置服务器来使用SQL管理admin。基础管理不需要SQL知识，但是使用这个功能，你必须意识到SourceMod并不会为完整的SQL控制提供简单的功能。如果你需要更好的功能，你需要使用三方工具或者自学SQL。&lt;br /&gt;
&lt;br /&gt;
不过，先假设你会:&lt;br /&gt;
*能访问一个数据库，&lt;br /&gt;
*明白如何访问一个数据库;&lt;br /&gt;
*明白如何在数据库中执行指令或者脚本。&lt;br /&gt;
&lt;br /&gt;
=配置=&lt;br /&gt;
==MySQL==&lt;br /&gt;
打开服务器的&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;文件，你应该会看到有一个叫&amp;lt;tt&amp;gt;&amp;quot;default&amp;quot;&amp;lt;/tt&amp;gt;的节点。你可以使用这个，如果你想分开连接也可以创建一个新的&amp;lt;tt&amp;gt;&amp;quot;admins&amp;quot;&amp;lt;/tt&amp;gt;节点。你必须填充&amp;lt;tt&amp;gt;driver&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;host&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;database&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;user&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;pass&amp;lt;/tt&amp;gt;的值，如果有值不需要填，填个双引号即可（&amp;lt;tt&amp;gt;&amp;quot;&amp;quot;&amp;lt;/tt&amp;gt;），每个'srcds'的实例都需要重启，否则改动不生效。&lt;br /&gt;
&lt;br /&gt;
例子：&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;quot;default&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;driver&amp;quot;			&amp;quot;mysql&amp;quot;&lt;br /&gt;
		&amp;quot;host&amp;quot;				&amp;quot;数据库&amp;quot;&lt;br /&gt;
		&amp;quot;database&amp;quot;			&amp;quot;数据库名字&amp;quot;&lt;br /&gt;
		&amp;quot;user&amp;quot;				&amp;quot;账号&amp;quot;&lt;br /&gt;
		&amp;quot;pass&amp;quot;				&amp;quot;密码&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==SQLite==&lt;br /&gt;
在你服务器的&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;文件中添加一个如下的节点：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;quot;admins&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;driver&amp;quot;			&amp;quot;sqlite&amp;quot;&lt;br /&gt;
		&amp;quot;database&amp;quot;			&amp;quot;admins-sqlite&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你可以使用任意合法的文件名作为数据库的名字，这里用“admins-sqlite”是因为这是预置的数据库，&amp;lt;tt&amp;gt;configs/sql-init-scripts/sqlite&amp;lt;/tt&amp;gt;目录下可以找到&amp;lt;tt&amp;gt;admins-sqlite&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=数据库设置=&lt;br /&gt;
==自动==&lt;br /&gt;
确保&amp;lt;tt&amp;gt;sql-admin-manager.smx&amp;lt;/tt&amp;gt;已加载，然后在'''服务器控制台'''中执行&amp;lt;tt&amp;gt;sm_create_adm_tables&amp;lt;/tt&amp;gt;。如果使用的是MySQL，你需要有数据库的&amp;lt;tt&amp;gt;CREATE&amp;lt;/tt&amp;gt;权限。&lt;br /&gt;
&lt;br /&gt;
==手动==&lt;br /&gt;
===MySQL===&lt;br /&gt;
如果你还没有在你的MySQL中创建数据库里，这样做。&lt;br /&gt;
&lt;br /&gt;
找到SourceMod中的&amp;lt;tt&amp;gt;configs/sql-init-scripts/mysql/create_admins.sql&amp;lt;/tt&amp;gt;脚本。然后通过数据库命令行或者类似[http://www.phpmyadmin.net/ phpMyAdmin]的工具执行里面的内容，这个脚本会创建必要的表。&lt;br /&gt;
&lt;br /&gt;
===SQLite===&lt;br /&gt;
SourceMod发布有一个预置的SQLite数据库文件，里面已经建好所有的表。如果你想手动创建数据库或者添加表，使用&amp;lt;tt&amp;gt;configs/sql-init-scripts/sqlite/create_admins.sql&amp;lt;/tt&amp;gt;脚本的内容。&lt;br /&gt;
&lt;br /&gt;
把二进制文件&amp;lt;tt&amp;gt;configs/sql-init-scripts/sqlite/admins-sqlite.sq3&amp;lt;/tt&amp;gt;拷贝至你服务器的&amp;lt;tt&amp;gt;data/sqlite&amp;lt;/tt&amp;gt;目录下，如果你在&amp;lt;tt&amp;gt;databases.cfg&amp;lt;/tt&amp;gt;中重命名了数据库，那么你应该也同时重命名这个文件。&lt;br /&gt;
&lt;br /&gt;
=启用插件=&lt;br /&gt;
所有的SQL插件都在&amp;lt;tt&amp;gt;plugins/disabled&amp;lt;/tt&amp;gt;目录下——默认不加载。要启用插件，只需要把他们从&amp;lt;tt&amp;gt;plugins/disabled&amp;lt;/tt&amp;gt;目录移动到&amp;lt;tt&amp;gt;plugins&amp;lt;/tt&amp;gt;目录下。&lt;br /&gt;
&lt;br /&gt;
你会比较想启用&amp;lt;tt&amp;gt;sql-admin-manager.smx&amp;lt;/tt&amp;gt;插件，因为它提供了一些非常有用的控制台指令来进行管理。但是，另外两个插件，你同时只能启用其中'''一个'''： However, you should only enable '''ONE''' of the other two SQL plugins:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;tt&amp;gt;admin-sql-prefetch.smx&amp;lt;/tt&amp;gt;: 这个插件是中小型低延迟数据库的理想选择，它在地图加载时加载权限（或者主动刷新时），如果数据库延迟很大，或者你希望增删改权限后不需要刷新每个服务器的缓存，那么这个插件不适合你。&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;优点:&amp;lt;/b&amp;gt; 非常简单的插件，不复杂&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;缺点:&amp;lt;/b&amp;gt; 当缓存刷新时服务器会有延迟（地图加载或者使用&amp;lt;tt&amp;gt;sm_reloadadmins&amp;lt;/tt&amp;gt;）。如果你增删改权限表了，在缓存刷新前改动不会有体现。&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;tt&amp;gt;admin-sql-threaded.smx&amp;lt;/tt&amp;gt;: 这个插件完全线程化了。因此它十分复杂，现在也是“实验”性质的（尽管测试运行良好）。它预加载所有的组权限和覆写信息，管理员查找也是动态完成的。&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;优点:&amp;lt;/b&amp;gt;糟糕的数据库连接不会卡顿服务器。如果你增删改了管理员，变动会在玩家连接服务器时实时体现。&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;b&amp;gt;缺点:&amp;lt;/b&amp;gt;代码非常复杂，不适合作自定义修改，同时它有一些副作用（比如，&amp;lt;tt&amp;gt;sm_reloadadmins&amp;lt;/tt&amp;gt;指令会有轻微的延迟，因为插件是多线程的）&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
一旦你启用了管理插件，并且启用了&amp;lt;tt&amp;gt;admin-sql&amp;lt;/tt&amp;gt;插件的其中&amp;lt;b&amp;gt;一个&amp;lt;/b&amp;gt;，那么可以继续往下了。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=更新表=&lt;br /&gt;
==自动==&lt;br /&gt;
确保插件&amp;lt;tt&amp;gt;sql-admin-manager.smx&amp;lt;/tt&amp;gt;已加载，然后在服务器控制台中执行&amp;lt;tt&amp;gt;sm_update_adm_tables&amp;lt;/tt&amp;gt;指令，如果你使用的是MySQL，你必须就有数据库的&amp;lt;tt&amp;gt;CREATE&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;ALTER&amp;lt;/tt&amp;gt;权限。&lt;br /&gt;
&lt;br /&gt;
==手动==&lt;br /&gt;
如果你正在使用的是旧版SourceMod表结构，那么更新最新架构的表结构非常重要。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;总结一下&amp;lt;/b&amp;gt;:&lt;br /&gt;
*先备份所有数据。&lt;br /&gt;
*要明确你的旧表是依据什么建立的，不幸的是，目前唯一的办法就是记住你当时使用的SourceMod版本。&lt;br /&gt;
*找到所有服务器上像&amp;lt;tt&amp;gt;update_admins-rXXXX&amp;lt;/tt&amp;gt;这个格式的脚本，其中&amp;lt;tt&amp;gt;XXXX&amp;lt;/tt&amp;gt;是修订的版本号，需要的是版本比第二步中的高，但比你现在使用的版本低或者相同的脚本。&lt;br /&gt;
*按照递增的修订号执行匹配的脚本（最低的先执行，最高的最后执行）。&lt;br /&gt;
*升级SQL插件，以及任何依赖于管理员表的第三方软件。&lt;br /&gt;
&lt;br /&gt;
升级表结构的脚本在&amp;lt;tt&amp;gt;configs/sql-init-scripts/&amp;amp;lt;driver&amp;amp;gt;/update_admins-rXXXX&amp;lt;/tt&amp;gt;，其中XXXX是修订版本号。&lt;br /&gt;
&lt;br /&gt;
下面是一种可能的场景。（&amp;lt;b&amp;gt;数据是编的，实际的修订版本号完全不同&amp;lt;/b&amp;gt;）。&lt;br /&gt;
&lt;br /&gt;
你的表是在r2978版的SourceMod下建立的，现在的版本是3512，那么下面是可能存在的文件：&lt;br /&gt;
*&amp;lt;tt&amp;gt;update_admins-r2500.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;update_admins-r3243.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;update_admins-r3444.sql&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
在这种情况下，你应当依次执行脚本&amp;lt;tt&amp;gt;3243&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;3444&amp;lt;/tt&amp;gt;，来把你的表升级到最新版。所有的数据都会被保留，但是还是推荐你在作数据库变更时进行数据备份。&lt;br /&gt;
&lt;br /&gt;
=管理命令=&lt;br /&gt;
出于方便考虑，SourceMod通过插件&amp;lt;tt&amp;gt;sql-admin-manager.smx&amp;lt;/tt&amp;gt;提供了一些基础的数据库的管理员管理命令。所有的命令都需要&amp;lt;tt&amp;gt;root&amp;lt;tt&amp;gt;的管理flag。&lt;br /&gt;
&lt;br /&gt;
下面是一些使用惯例：&lt;br /&gt;
*当&amp;amp;lt;authtype&amp;amp;gt;被请求时，它意味着下面三者之一的值，&amp;lt;tt&amp;gt;steam&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;ip&amp;lt;/tt&amp;gt;, or &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*当&amp;amp;lt;identity&amp;amp;gt;被请求时，它是跟&amp;lt;tt&amp;gt;authtype&amp;lt;/tt&amp;gt;匹配的唯一字符串。例如，&amp;lt;tt&amp;gt;steam&amp;lt;/tt&amp;gt;代表Steam Id，&amp;lt;tt&amp;gt;ip&amp;lt;/tt&amp;gt;代表IP地址，&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;代表半条命2中玩家的昵称。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;注意: 如果字符串中有空格，那么它要用引号括起来！（译注：整个子串用引号括起来）&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;注意: 冒号是中断字符，所以Steam Id必须用引号括起来！&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Command&lt;br /&gt;
| Format&lt;br /&gt;
| Description&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_sql_addadmin&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;alias&amp;gt; &amp;lt;authtype&amp;gt; &amp;lt;identity&amp;gt; &amp;lt;flags&amp;gt; [immunity] [password]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Adds a new entry to the &amp;lt;tt&amp;gt;sm_admins&amp;lt;/tt&amp;gt; table.  The &amp;lt;tt&amp;gt;alias&amp;lt;/tt&amp;gt; can be any value and is usually used to assign a readable name to an IP/SteamID.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_sql_deladmin&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;authtype&amp;gt; &amp;lt;identity&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Removes an admin.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_sql_addgroup&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;name&amp;gt; &amp;lt;flags&amp;gt; &amp;lt;immunity&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Adds a new group with the specified flags and immunity&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_sql_delgroup&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;name&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Removes the specified group.  Quotation marks are optional if the name has odd characters.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot; &lt;br /&gt;
| sm_sql_setadmingroups&lt;br /&gt;
| &amp;lt;nowiki&amp;gt;&amp;lt;authtype&amp;gt; &amp;lt;identity&amp;gt; [group1] ... [group N]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
| Sets an admin's group list.  The inheritance order is the order the groups are specified in.  This sets, and does not add; thus specifying no groups removes the user from all groups.  Example:&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_sql_setadmingroups steam &amp;quot;STEAM_0:1:16&amp;quot; &amp;quot;Full Admins&amp;quot;&amp;lt;/tt&amp;gt;&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_create_adm_tables&lt;br /&gt;
| &lt;br /&gt;
| Creates the administration tables for SourceMod.  Can only be run from the server console.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| sm_update_adm_tables&lt;br /&gt;
|&lt;br /&gt;
| Updates the administration tables to the latest schema, preserving data as applicable.  Can only be run from the server console.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
&amp;lt;b&amp;gt;Q:&amp;lt;/b&amp;gt; Can I use &amp;lt;tt&amp;gt;admin-sql&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;admin-flatfile&amp;lt;/tt&amp;gt; at the same time?&lt;br /&gt;
&amp;lt;b&amp;gt;A:&amp;lt;/b&amp;gt; Yes.  The data will be merged together in SourceMod's cache.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Q:&amp;lt;/b&amp;gt; Can I use both the &amp;lt;tt&amp;gt;threaded&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;prefetch&amp;lt;/tt&amp;gt; SQL plugins at the same time?&lt;br /&gt;
&amp;lt;b&amp;gt;A:&amp;lt;/b&amp;gt; No.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Q:&amp;lt;/b&amp;gt; Can duplicate groups/admins be in the flat files and the SQL database?&lt;br /&gt;
&amp;lt;b&amp;gt;A:&amp;lt;/b&amp;gt; Yup.  They will be merged safely.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Schemas=&lt;br /&gt;
This section documents the required portions of the admin table schema.  You do not need to read or learn this unless you plan to use SQL directly.&lt;br /&gt;
&lt;br /&gt;
The exact schemas for each driver are located in &amp;lt;tt&amp;gt;configs/sql-init-scripts&amp;lt;/tt&amp;gt;.  The purpose of this document is to explain the fields rather than list the exact structures.&lt;br /&gt;
&lt;br /&gt;
==sm_admins==&lt;br /&gt;
This table is used to store administrators.  Although the primary key is &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt;, applications should enforce that &amp;lt;tt&amp;gt;authtype&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;identity&amp;lt;/tt&amp;gt; have no combined duplicates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; (auto increments).&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| id&lt;br /&gt;
| integer&lt;br /&gt;
| Unique integer identifying the row.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| authtype&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
Constrained to 'steam', 'name', or 'ip'&lt;br /&gt;
| Authentication type the identity is against.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| identity&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Steam ID, name, or IP address.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| password&lt;br /&gt;
| string&lt;br /&gt;
| Password, if any, the admin must use.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| flags&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Permission flag string.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| name&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Alias used for external tools (all but ignored in SourceMod).&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| immunity&lt;br /&gt;
| integer NOT NULL&lt;br /&gt;
| Immunity level value.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_groups==&lt;br /&gt;
This table is used to store all groups entries.  Although the primary key is &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt;, applications should enforce that the &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; field stays unique.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; (auto increments).&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| id&lt;br /&gt;
| integer&lt;br /&gt;
| Unique integer identifying the row.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| flags&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Permissions flag string.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| name&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Unique name of the group.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| immunity_level&lt;br /&gt;
| integer NOT NULL&lt;br /&gt;
| Immunity level value.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_admins_groups==&lt;br /&gt;
This table is used to map admins to the groups they will inherit.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;admin_id&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;group_id&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| admin_id &lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_admins.id&amp;lt;/tt&amp;gt; field.  Specifies the admin inheriting the group.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| group_id&lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_groups.id&amp;lt;/tt&amp;gt; field.  Specifies the group the admin is inheriting.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| inherit_order&lt;br /&gt;
| integer NOT NULL&lt;br /&gt;
| Order of inheritance for the given admin.  Lower means earlier inheritance.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_group_immunity==&lt;br /&gt;
This table is used to map which groups are immune from other groups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;group_id&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;other_id&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| group_id&lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_groups.id&amp;lt;/tt&amp;gt; field.  Specifies the group gaining immunity.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| other_id&lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_groups.id&amp;lt;/tt&amp;gt; field.  Specifies who &amp;lt;tt&amp;gt;group_id&amp;lt;/tt&amp;gt; is becoming immune from.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==sm_group_overrides==&lt;br /&gt;
This table is used to specify group-based command overrides.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;group_id&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| group_id&lt;br /&gt;
| integer&lt;br /&gt;
| Reference to the &amp;lt;tt&amp;gt;sm_groups.id&amp;lt;/tt&amp;gt; field.  Specifies the group the override is for.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| type&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
Constrained to 'command' or 'group'.&lt;br /&gt;
| Specifies whether the override is a command or a command group.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| name&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Command name.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| access&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
Constrained to 'allow' or 'deny'.&lt;br /&gt;
| Whether the command is allowed or denied to this group.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_overrides==&lt;br /&gt;
This table is used to specify global command overrides.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;type&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| type&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
Constrained to 'command' or 'group'.&lt;br /&gt;
| Specifies whether the override is a command or a command group.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| name&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Command name.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| flags&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Permissions flag string.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==sm_config==&lt;br /&gt;
This table is used to specify configuration options.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Primary key:&amp;lt;/b&amp;gt; &amp;lt;tt&amp;gt;cfg_key&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
:{|&lt;br /&gt;
|- class=&amp;quot;t2th&amp;quot;&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| Purpose&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| cfg_key&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Configuration key.&lt;br /&gt;
|- class=&amp;quot;t2td&amp;quot;&lt;br /&gt;
| cfg_value&lt;br /&gt;
| string NOT NULL&lt;br /&gt;
| Configuration value.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
''Note:'' As of this writing, the only configuration value is &amp;lt;tt&amp;gt;admin_version&amp;lt;/tt&amp;gt;, and it specifies the revision the schema last changed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=User:MisakaSora&amp;diff=10863</id>
		<title>User:MisakaSora</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=User:MisakaSora&amp;diff=10863"/>
		<updated>2019-09-18T04:49:54Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: Created page with &amp;quot;我是DD。  来，跟我一起成为DD把。  每周五晚19:30，[http://live.bilibili.com/14578426 战斗吧歌姬]见。  人之初，性本D。 誰でも，大好き!&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;我是DD。&lt;br /&gt;
&lt;br /&gt;
来，跟我一起成为DD把。&lt;br /&gt;
&lt;br /&gt;
每周五晚19:30，[http://live.bilibili.com/14578426 战斗吧歌姬]见。&lt;br /&gt;
&lt;br /&gt;
人之初，性本D。&lt;br /&gt;
誰でも，大好き!&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Adding_Groups_(SourceMod)/zh&amp;diff=10848</id>
		<title>Adding Groups (SourceMod)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Adding_Groups_(SourceMod)/zh&amp;diff=10848"/>
		<updated>2019-09-12T07:37:48Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Adding Groups (SourceMod)}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
本文将介绍如何通过&amp;lt;tt&amp;gt;configs/admin_groups.cfg&amp;lt;/tt&amp;gt;添加与配置组权限。&amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;会对这个文件内容进行解析并加载。&lt;br /&gt;
&lt;br /&gt;
=介绍=&lt;br /&gt;
当你想给多位管理员配置相似的属性和权限时，使用权限组是非常方便的。比如，假如说你希望有15个管理员都权限和权限豁免。在这种情况下，你只需要给这些管理员配置一个权限组，之后就可以在一个地方同时修改所有成员的权限了。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=文件格式=&lt;br /&gt;
权限组是位于“Group”代码块下的特定的代码块。每个组必须有个唯一名称，如果名称不是唯一的，权限会从现有组中扩展/覆盖。这就是说，如果一个组同时在配置文件中和外部源中（比如数据库）有配置，那么最终的权限会根据解析的顺序来合并/覆盖。你可以用[http://forums.alliedmods.net/showthread.php?t=81160 KVManager]来修改&amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
&lt;br /&gt;
权限组代码块长这样:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;Groups&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Group Name&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;[option1]&amp;quot;	&amp;quot;[value1]&amp;quot;&lt;br /&gt;
		&amp;quot;[option2]&amp;quot;	&amp;quot;[value2]&amp;quot;&lt;br /&gt;
		/* ... */&lt;br /&gt;
		&amp;quot;Overrides&amp;quot;&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;[override1]&amp;quot;	&amp;quot;[allow|deny]&amp;quot;&lt;br /&gt;
			&amp;quot;[override2]&amp;quot;	&amp;quot;[allow|deny]&amp;quot;&lt;br /&gt;
			/* ... */&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
所有的选项都是可选的（也就说，他们不需要明确定义 ），类似，&amp;quot;Overrides&amp;quot;节点也是完全可选的，甚至可以省略，完整的可用选项包括：&lt;br /&gt;
*&amp;lt;tt&amp;gt;flags&amp;lt;/tt&amp;gt;: 这个组的成员继承Flag串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;immunity&amp;lt;/tt&amp;gt;: 如果是数字，且比用户自身权限值高时，会被用户继承。如果是'@'开头的字符串，那么它表明，来自对应组的用户的指令无法作用于本组成员。&lt;br /&gt;
&lt;br /&gt;
“Overrides”节点可以指定本组成员是否允许执行特定指令/组的指令。这是个非常强力的节点。例如，你可以设置让成员没有&amp;quot;map&amp;quot;的flag，但是允许他们执行&amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt;，对应地你也可以让他们拥有&amp;quot;map&amp;quot;的flag但是不能执行&amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
命令权限组在[[Overriding Command Access (SourceMod)|此文]]中会有更详细的介绍。&lt;br /&gt;
=== 权限覆写的例子： ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;Groups&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Basic Admin&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;flags&amp;quot;			&amp;quot;abc&amp;quot;	//保留位置、一般管理、踢人&lt;br /&gt;
		&amp;quot;immunity&amp;quot;		&amp;quot;1&amp;quot;	//低权限豁免值&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;Overrides&amp;quot;&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;sm_map&amp;quot;	&amp;quot;allow&amp;quot;	//即便没有map的flag，也允许使用sm_map指令&lt;br /&gt;
			&amp;quot;@CSDM&amp;quot;		&amp;quot;deny&amp;quot;	//禁止执行来自CSDM组的指令&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=继承=&lt;br /&gt;
权限组是不能被嵌套的，这就是说，一个组不能从另外一个组中继承权限。但是，一个管理员可以从任意多个权限组中继承权限。权限是可以叠加的，这也意味着，只有当更多权限被赋予时，才有体现。（译注：这话很绕，我估摸着大致意思是，不同组权限是完美合并的）&lt;br /&gt;
&lt;br /&gt;
比如，如果一个用户有&amp;lt;tt&amp;gt;bcd&amp;lt;/tt&amp;gt;的flag，一个组提供了&amp;lt;tt&amp;gt;ae&amp;lt;/tt&amp;gt;的flag，另一个组提供了&amp;lt;tt&amp;gt;ae&amp;lt;/tt&amp;gt;的flag，由于继承的存在用户会同时拥有&amp;lt;tt&amp;gt;abcde&amp;lt;/tt&amp;gt;的flag。同样的，当组的权限豁免值大于用户当前的值时，会被用户继承。&lt;br /&gt;
&lt;br /&gt;
唯一例外是组内的权限覆写。如果一个命令在A组是允许的，B组禁止的，那么当一个用户同时继承A组和B组，用户能否使用指令依赖于继承的顺序。如果A先继承，那么指令是允许执行的，如果B先继承，那么命令是被禁止的。&lt;br /&gt;
&lt;br /&gt;
最后，需要注意的是，用户继承于一个权限组，此时对于组的权限的修改，不会立即生效，这是一种优化。如果你想立即生效，需要重新加载一下权限。（可以通过断开连接或指令&amp;lt;tt&amp;gt;sm_reloadadmins&amp;lt;/tt&amp;gt;实现）&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=相关链接=&lt;br /&gt;
*[[Overriding Command Access (SourceMod)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Adding_Groups_(SourceMod)&amp;diff=10847</id>
		<title>Adding Groups (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Adding_Groups_(SourceMod)&amp;diff=10847"/>
		<updated>2019-09-12T07:37:26Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Adding_Groups (SourceMod)}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
This article explains how to add and configure groups through &amp;lt;tt&amp;gt;configs/admin_groups.cfg&amp;lt;/tt&amp;gt;.  This file is parsed and loaded via &amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Introduction=&lt;br /&gt;
Groups are a convenient method of applying similar properties and permissions to multiple administrators.  For example, let's say you wish for 15 administrators to all have the same permissions and immunity.  In this case, you'd set up a group for these admins, after which changing the permissions for all member admins can be done via one location.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=File Format=&lt;br /&gt;
Groups are specified as blocks within a master &amp;quot;Group&amp;quot; block.  Each group must have a unique name.  If the name is not unique, the permissions will simply be extended/overwritten from the permissions of the existing group.  That is to say, if a group exists in both the config file and an external source (say, SQL), then the permissions will end up being combined/overwritten in the order of parsing. You can create and / or modify &amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt; files with [http://forums.alliedmods.net/showthread.php?t=81160 KVManager].&lt;br /&gt;
&lt;br /&gt;
The group blocks appear in this format:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;Groups&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Group Name&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;[option1]&amp;quot;	&amp;quot;[value1]&amp;quot;&lt;br /&gt;
		&amp;quot;[option2]&amp;quot;	&amp;quot;[value2]&amp;quot;&lt;br /&gt;
		/* ... */&lt;br /&gt;
		&amp;quot;Overrides&amp;quot;&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;[override1]&amp;quot;	&amp;quot;[allow|deny]&amp;quot;&lt;br /&gt;
			&amp;quot;[override2]&amp;quot;	&amp;quot;[allow|deny]&amp;quot;&lt;br /&gt;
			/* ... */&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All options are optional (that is, they do not need to be explicitly defined).  Similarly, the &amp;quot;Overrides&amp;quot; section is completely optional and may be omitted.  The full options list contains:&lt;br /&gt;
*&amp;lt;tt&amp;gt;flags&amp;lt;/tt&amp;gt;: Flag string that users of the group will inherit.&lt;br /&gt;
*&amp;lt;tt&amp;gt;immunity&amp;lt;/tt&amp;gt;: If a numerical value, specifies the immunity value users will inherit if higher than their current immunity value.  If preceded by an '@' symbol, then it specifies a group that the user will always be immune from.&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;Overrides&amp;quot; section allows specific commands or groups of commands to be completely allowed or denied to members of the group.  This is a very powerful section.  For example, you can opt to not give members the &amp;quot;map&amp;quot; flag, but allow them access to &amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt;.  Conversely, you could opt to give members the &amp;quot;map&amp;quot; flag, but deny them access to &amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
Command groups are explained further in the [[Overriding Command Access (SourceMod)]] article.&lt;br /&gt;
=== Combined example of a group with overrides: ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;Groups&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Basic Admin&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;flags&amp;quot;			&amp;quot;abc&amp;quot;	//reservation, generic admin, kick&lt;br /&gt;
		&amp;quot;immunity&amp;quot;		&amp;quot;1&amp;quot;	//low immunity value&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;Overrides&amp;quot;&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;sm_map&amp;quot;	&amp;quot;allow&amp;quot;	//Let them use sm_map even without the flag&lt;br /&gt;
			&amp;quot;@CSDM&amp;quot;		&amp;quot;deny&amp;quot;	//deny any commands from the CSDM group&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Inheritance=&lt;br /&gt;
Groups cannot be nested; that is to say, one group cannot inherit permissions from another group.  However, an administrator can inherit any number of groups.  The permissions will be &amp;lt;tt&amp;gt;additive&amp;lt;/tt&amp;gt;, meaning that they will be assumed if and only if they result in more permissions being granted.&lt;br /&gt;
&lt;br /&gt;
For example, if a user has flags &amp;lt;tt&amp;gt;bcd&amp;lt;/tt&amp;gt; and a group assigns flags &amp;lt;tt&amp;gt;ae&amp;lt;/tt&amp;gt;, inheritance would result in that user having the flags &amp;lt;tt&amp;gt;abcde&amp;lt;/tt&amp;gt;.  Similarly, a group's immunity value will only be inherited if it is larger than the user's current immunity value.&lt;br /&gt;
&lt;br /&gt;
The only case this changes is with group-specific overrides.  If a command is specifically allowed to group A and denied in group B, and a user inherits both groups A and B, whether or not the user can use the command will depend on the order of inheritance.  If A is inherited first, the command will be allowed.  If B is inherited first, the command will be denied.&lt;br /&gt;
&lt;br /&gt;
Lastly, it is worth noting that once a user inherits permissions from a group, changes to the group will not affect the active permissions.  This is an optimization.  In order for changes to groups to affect that user, their administrative permissions must be entirely reset (either by a disconnect, or via &amp;lt;tt&amp;gt;sm_reloadadmins&amp;lt;/tt&amp;gt;, which effectively reloads all permissions anyway).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=See Also=&lt;br /&gt;
*[[Overriding Command Access (SourceMod)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Adding_Groups_(SourceMod)/zh&amp;diff=10846</id>
		<title>Adding Groups (SourceMod)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Adding_Groups_(SourceMod)/zh&amp;diff=10846"/>
		<updated>2019-09-12T07:36:55Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: Created page with &amp;quot;{{Languages|Adding_Groups_(SourceMod)}} __FORCETOC__  本文将介绍如何通过&amp;lt;tt&amp;gt;configs/admin_groups.cfg&amp;lt;/tt&amp;gt;添加与配置组权限。&amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;会对...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Adding_Groups_(SourceMod)}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
本文将介绍如何通过&amp;lt;tt&amp;gt;configs/admin_groups.cfg&amp;lt;/tt&amp;gt;添加与配置组权限。&amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;会对这个文件内容进行解析并加载。&lt;br /&gt;
&lt;br /&gt;
=介绍=&lt;br /&gt;
当你想给多位管理员配置相似的属性和权限时，使用权限组是非常方便的。比如，假如说你希望有15个管理员都权限和权限豁免。在这种情况下，你只需要给这些管理员配置一个权限组，之后就可以在一个地方同时修改所有成员的权限了。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=文件格式=&lt;br /&gt;
权限组是位于“Group”代码块下的特定的代码块。每个组必须有个唯一名称，如果名称不是唯一的，权限会从现有组中扩展/覆盖。这就是说，如果一个组同时在配置文件中和外部源中（比如数据库）有配置，那么最终的权限会根据解析的顺序来合并/覆盖。你可以用[http://forums.alliedmods.net/showthread.php?t=81160 KVManager]来修改&amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
&lt;br /&gt;
权限组代码块长这样:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;Groups&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Group Name&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;[option1]&amp;quot;	&amp;quot;[value1]&amp;quot;&lt;br /&gt;
		&amp;quot;[option2]&amp;quot;	&amp;quot;[value2]&amp;quot;&lt;br /&gt;
		/* ... */&lt;br /&gt;
		&amp;quot;Overrides&amp;quot;&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;[override1]&amp;quot;	&amp;quot;[allow|deny]&amp;quot;&lt;br /&gt;
			&amp;quot;[override2]&amp;quot;	&amp;quot;[allow|deny]&amp;quot;&lt;br /&gt;
			/* ... */&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
所有的选项都是可选的（也就说，他们不需要明确定义 ），类似，&amp;quot;Overrides&amp;quot;节点也是完全可选的，甚至可以省略，完整的可用选项包括：&lt;br /&gt;
*&amp;lt;tt&amp;gt;flags&amp;lt;/tt&amp;gt;: 这个组的成员继承Flag串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;immunity&amp;lt;/tt&amp;gt;: 如果是数字，且比用户自身权限值高时，会被用户继承。如果是'@'开头的字符串，那么它表明，来自对应组的用户的指令无法作用于本组成员。&lt;br /&gt;
&lt;br /&gt;
“Overrides”节点可以指定本组成员是否允许执行特定指令/组的指令。这是个非常强力的节点。例如，你可以设置让成员没有&amp;quot;map&amp;quot;的flag，但是允许他们执行&amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt;，对应地你也可以让他们拥有&amp;quot;map&amp;quot;的flag但是不能执行&amp;lt;tt&amp;gt;sm_map&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
命令权限组在[[Overriding Command Access (SourceMod)|此文]]中会有更详细的介绍。&lt;br /&gt;
=== 权限覆写的例子： ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;Groups&amp;quot;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;Basic Admin&amp;quot;&lt;br /&gt;
	{&lt;br /&gt;
		&amp;quot;flags&amp;quot;			&amp;quot;abc&amp;quot;	//保留位置、一般管理、踢人&lt;br /&gt;
		&amp;quot;immunity&amp;quot;		&amp;quot;1&amp;quot;	//低权限豁免值&lt;br /&gt;
&lt;br /&gt;
		&amp;quot;Overrides&amp;quot;&lt;br /&gt;
		{&lt;br /&gt;
			&amp;quot;sm_map&amp;quot;	&amp;quot;allow&amp;quot;	//即便没有map的flag，也允许使用sm_map指令&lt;br /&gt;
			&amp;quot;@CSDM&amp;quot;		&amp;quot;deny&amp;quot;	//禁止执行来自CSDM组的指令&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=继承=&lt;br /&gt;
权限组是不能被嵌套的，这就是说，一个组不能从另外一个组中继承权限。但是，一个管理员可以从任意多个权限组中继承权限。权限是可以叠加的，这也意味着，只有当更多权限被赋予时，才有体现。（译注：这话很绕，我估摸着大致意思是，不同组权限是完美合并的）&lt;br /&gt;
&lt;br /&gt;
比如，如果一个用户有&amp;lt;tt&amp;gt;bcd&amp;lt;/tt&amp;gt;的flag，一个组提供了&amp;lt;tt&amp;gt;ae&amp;lt;/tt&amp;gt;的flag，另一个组提供了&amp;lt;tt&amp;gt;ae&amp;lt;/tt&amp;gt;的flag，由于继承的存在用户会同时拥有&amp;lt;tt&amp;gt;abcde&amp;lt;/tt&amp;gt;的flag。同样的，当组的权限豁免值大于用户当前的值时，会被用户继承。&lt;br /&gt;
&lt;br /&gt;
唯一例外是组内的权限覆写。如果一个命令在A组是允许的，B组禁止的，那么当一个用户同时继承A组和B组，用户能否使用指令依赖于继承的顺序。如果A先继承，那么指令是允许执行的，如果B先继承，那么命令是被禁止的。&lt;br /&gt;
&lt;br /&gt;
最后，需要注意的是，用户继承于一个权限组，此时对于组的权限的修改，不会立即生效，这是一种优化。如果你想立即生效，需要重新加载一下权限。（可以通过断开连接或指令&amp;lt;tt&amp;gt;sm_reloadadmins&amp;lt;/tt&amp;gt;实现）&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=相关链接=&lt;br /&gt;
*[[Overriding Command Access (SourceMod)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10845</id>
		<title>SourceMod Configuration/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10845"/>
		<updated>2019-09-12T03:39:43Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 插件加载 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SourceMod_Configuration}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
此文档会概述一些[[SourceMod]]中配置相关的概念。&lt;br /&gt;
&lt;br /&gt;
=配置的类型=&lt;br /&gt;
SourceMod的配置文件一般处于下面两个文件夹中：&lt;br /&gt;
*&amp;lt;tt&amp;gt;cfg/sourcemod&amp;lt;/tt&amp;gt; - 这里的都是.cfg文件，里面都是控制台变量和指令，这些文件完全可以在服务器控制台中通过exec指令直接运行。&lt;br /&gt;
*&amp;lt;tt&amp;gt;addons/sourcemod/configs&amp;lt;/tt&amp;gt; - 这里的文件内容都是有着特殊或高级格式的，请谨慎编辑，他们不可以在控制台直接运行。&lt;br /&gt;
&lt;br /&gt;
=管理员相关=&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admins_simple.ini&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admins.cfg&amp;lt;/tt&amp;gt;，请参阅[[Adding_Admins_%28SourceMod%29%2Fzh|添加管理员]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;请参阅[[Adding_Groups_%28SourceMod%29%2Fzh|添加组]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_overrides.cfg&amp;lt;/tt&amp;gt;请参阅[[Overriding_Command_Access_%28SourceMod%29%2Fzh|覆写命令权限]]。&lt;br /&gt;
&lt;br /&gt;
文件&amp;lt;tt&amp;gt;admin_levels.cfg&amp;lt;/tt&amp;gt;不应该修改。&lt;br /&gt;
&lt;br /&gt;
=管理员菜单=&lt;br /&gt;
关于&amp;lt;tt&amp;gt;adminmenu_cfgs.txt&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;adminmenu_sorting.txt&amp;lt;/tt&amp;gt;的修改，请参阅[[Admin_Menu_Configuration_%28SourceMod%29%2Fzh|管理员菜单配置]]。&lt;br /&gt;
&lt;br /&gt;
=核心配置=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/core.cfg&amp;lt;/tt&amp;gt;的内容是一些几乎不需要修改的配置，在这里你可以修改跟下面内容相关的配置：&lt;br /&gt;
*日志&lt;br /&gt;
*服务器语言&lt;br /&gt;
*聊天触发器表现&lt;br /&gt;
*管理员密码&lt;br /&gt;
*菜单声音&lt;br /&gt;
&lt;br /&gt;
=Cvar和sourcemod.cfg=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;cfg/sourcemod/sourcemod.cfg&amp;lt;/tt&amp;gt;里包含了SourceMod默认带的所有cvar。每个cvar都有注释：&lt;br /&gt;
*简短文档介绍&lt;br /&gt;
*默认值，以防忘记&lt;br /&gt;
*提供此cvar的插件，如果是Core本身，则不会显示&lt;br /&gt;
&lt;br /&gt;
更多详细信息，请参考实际文件。&lt;br /&gt;
&lt;br /&gt;
有许多SourceMod相关的cvar，并不在&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;中。如果插件编写时依照了正确的规则，SourceMod会为每个插件自动生成一个符合上面规则的配置文件。例如，一个叫&amp;lt;tt&amp;gt;hat.smx&amp;lt;/tt&amp;gt;的插件，那么就会生成文件&amp;lt;tt&amp;gt;cfg/sourcemod/plugin.hat.cfg&amp;lt;/tt&amp;gt;，它的内容可能是这样的：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This file was auto-generated by SourceMod (v1.0.0.986)&lt;br /&gt;
// ConVars for plugin &amp;quot;hat.smx&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// MySQL database&lt;br /&gt;
// -&lt;br /&gt;
// Default: &amp;quot;&amp;quot;&lt;br /&gt;
mysqlk_database &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
每次更变地图时，在&amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;之后，SourceMod会执行&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;和其他插件的配置文件。 is ran.&lt;br /&gt;
&lt;br /&gt;
=数据库=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/databases.cfg&amp;lt;/tt&amp;gt;允许你创建数据库配置，插件通过这些配置建立数据库连接。更多关于编辑此文件，请参阅[[SQL_Admins_%28SourceMod%29#Configuration/zh|Configuring SQL]]。&lt;br /&gt;
&lt;br /&gt;
=地图列表=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/maplists.cfg&amp;lt;/tt&amp;gt;是用来配置SourceMod构建地图列表的。每一小节都是一个可以被插件使用的列表。如果插件请求了一个不存在的列表，它会指向&amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt;小节，然后找到cvar&amp;lt;tt&amp;gt;mapcyclefile&amp;lt;/tt&amp;gt;所指定的文件，并最终指向&amp;lt;tt&amp;gt;maps&amp;lt;/tt&amp;gt;文件夹中的地图。&lt;br /&gt;
&lt;br /&gt;
通过调整这个文件的配置，你可以把所有的地图列表整合到一个中，或者分开以用于不同的需求。&lt;br /&gt;
&lt;br /&gt;
这个文件的头部有更多详细的文档。&lt;br /&gt;
&lt;br /&gt;
=插件加载=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/plugin_settings.cfg&amp;lt;/tt&amp;gt;有一个简单的目的：“永久”把特定插件加入到debug模式，一般来说，其他设置不需要修改。&lt;br /&gt;
&lt;br /&gt;
通过这个文件，你可以禁止某些插件的运行。例如，SourceBans需要禁用&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;，你可以让Core禁止&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;的加载。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation|返回目录]]&lt;br /&gt;
&lt;br /&gt;
友情翻译 [[User:MisakaSora|MisakaSora]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10844</id>
		<title>SourceMod Configuration/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10844"/>
		<updated>2019-09-12T03:38:18Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 管理员相关 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SourceMod_Configuration}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
此文档会概述一些[[SourceMod]]中配置相关的概念。&lt;br /&gt;
&lt;br /&gt;
=配置的类型=&lt;br /&gt;
SourceMod的配置文件一般处于下面两个文件夹中：&lt;br /&gt;
*&amp;lt;tt&amp;gt;cfg/sourcemod&amp;lt;/tt&amp;gt; - 这里的都是.cfg文件，里面都是控制台变量和指令，这些文件完全可以在服务器控制台中通过exec指令直接运行。&lt;br /&gt;
*&amp;lt;tt&amp;gt;addons/sourcemod/configs&amp;lt;/tt&amp;gt; - 这里的文件内容都是有着特殊或高级格式的，请谨慎编辑，他们不可以在控制台直接运行。&lt;br /&gt;
&lt;br /&gt;
=管理员相关=&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admins_simple.ini&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admins.cfg&amp;lt;/tt&amp;gt;，请参阅[[Adding_Admins_%28SourceMod%29%2Fzh|添加管理员]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;请参阅[[Adding_Groups_%28SourceMod%29%2Fzh|添加组]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_overrides.cfg&amp;lt;/tt&amp;gt;请参阅[[Overriding_Command_Access_%28SourceMod%29%2Fzh|覆写命令权限]]。&lt;br /&gt;
&lt;br /&gt;
文件&amp;lt;tt&amp;gt;admin_levels.cfg&amp;lt;/tt&amp;gt;不应该修改。&lt;br /&gt;
&lt;br /&gt;
=管理员菜单=&lt;br /&gt;
关于&amp;lt;tt&amp;gt;adminmenu_cfgs.txt&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;adminmenu_sorting.txt&amp;lt;/tt&amp;gt;的修改，请参阅[[Admin_Menu_Configuration_%28SourceMod%29%2Fzh|管理员菜单配置]]。&lt;br /&gt;
&lt;br /&gt;
=核心配置=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/core.cfg&amp;lt;/tt&amp;gt;的内容是一些几乎不需要修改的配置，在这里你可以修改跟下面内容相关的配置：&lt;br /&gt;
*日志&lt;br /&gt;
*服务器语言&lt;br /&gt;
*聊天触发器表现&lt;br /&gt;
*管理员密码&lt;br /&gt;
*菜单声音&lt;br /&gt;
&lt;br /&gt;
=Cvar和sourcemod.cfg=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;cfg/sourcemod/sourcemod.cfg&amp;lt;/tt&amp;gt;里包含了SourceMod默认带的所有cvar。每个cvar都有注释：&lt;br /&gt;
*简短文档介绍&lt;br /&gt;
*默认值，以防忘记&lt;br /&gt;
*提供此cvar的插件，如果是Core本身，则不会显示&lt;br /&gt;
&lt;br /&gt;
更多详细信息，请参考实际文件。&lt;br /&gt;
&lt;br /&gt;
有许多SourceMod相关的cvar，并不在&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;中。如果插件编写时依照了正确的规则，SourceMod会为每个插件自动生成一个符合上面规则的配置文件。例如，一个叫&amp;lt;tt&amp;gt;hat.smx&amp;lt;/tt&amp;gt;的插件，那么就会生成文件&amp;lt;tt&amp;gt;cfg/sourcemod/plugin.hat.cfg&amp;lt;/tt&amp;gt;，它的内容可能是这样的：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This file was auto-generated by SourceMod (v1.0.0.986)&lt;br /&gt;
// ConVars for plugin &amp;quot;hat.smx&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// MySQL database&lt;br /&gt;
// -&lt;br /&gt;
// Default: &amp;quot;&amp;quot;&lt;br /&gt;
mysqlk_database &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
每次更变地图时，在&amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;之后，SourceMod会执行&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;和其他插件的配置文件。 is ran.&lt;br /&gt;
&lt;br /&gt;
=数据库=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/databases.cfg&amp;lt;/tt&amp;gt;允许你创建数据库配置，插件通过这些配置建立数据库连接。更多关于编辑此文件，请参阅[[SQL_Admins_%28SourceMod%29#Configuration/zh|Configuring SQL]]。&lt;br /&gt;
&lt;br /&gt;
=地图列表=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/maplists.cfg&amp;lt;/tt&amp;gt;是用来配置SourceMod构建地图列表的。每一小节都是一个可以被插件使用的列表。如果插件请求了一个不存在的列表，它会指向&amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt;小节，然后找到cvar&amp;lt;tt&amp;gt;mapcyclefile&amp;lt;/tt&amp;gt;所指定的文件，并最终指向&amp;lt;tt&amp;gt;maps&amp;lt;/tt&amp;gt;文件夹中的地图。&lt;br /&gt;
&lt;br /&gt;
通过调整这个文件的配置，你可以把所有的地图列表整合到一个中，或者分开以用于不同的需求。&lt;br /&gt;
&lt;br /&gt;
这个文件的头部有更多详细的文档。&lt;br /&gt;
&lt;br /&gt;
=插件加载=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/plugin_settings.cfg&amp;lt;/tt&amp;gt;有一个简单的目的：“永久”把特定插件加入到debug模式，一般来说，其他设置不需要修改。&lt;br /&gt;
&lt;br /&gt;
通过这个文件，你可以禁止某些插件的运行。例如，SourceBans需要禁用&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;，你可以让Core禁止&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;的加载。&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10843</id>
		<title>SourceMod Configuration/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10843"/>
		<updated>2019-09-12T03:35:54Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 管理员菜单 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SourceMod_Configuration}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
此文档会概述一些[[SourceMod]]中配置相关的概念。&lt;br /&gt;
&lt;br /&gt;
=配置的类型=&lt;br /&gt;
SourceMod的配置文件一般处于下面两个文件夹中：&lt;br /&gt;
*&amp;lt;tt&amp;gt;cfg/sourcemod&amp;lt;/tt&amp;gt; - 这里的都是.cfg文件，里面都是控制台变量和指令，这些文件完全可以在服务器控制台中通过exec指令直接运行。&lt;br /&gt;
*&amp;lt;tt&amp;gt;addons/sourcemod/configs&amp;lt;/tt&amp;gt; - 这里的文件内容都是有着特殊或高级格式的，请谨慎编辑，他们不可以在控制台直接运行。&lt;br /&gt;
&lt;br /&gt;
=管理员相关=&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admins_simple.ini&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admins.cfg&amp;lt;/tt&amp;gt;，请参阅[[Adding_Admins_%28SourceMod%29|添加管理员]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;请参阅[[Adding_Groups_%28SourceMod%29|添加组]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_overrides.cfg&amp;lt;/tt&amp;gt;请参阅[[Overriding_Command_Access_%28SourceMod%29|覆写命令权限]]。&lt;br /&gt;
&lt;br /&gt;
文件&amp;lt;tt&amp;gt;admin_levels.cfg&amp;lt;/tt&amp;gt;不应该修改。&lt;br /&gt;
&lt;br /&gt;
=管理员菜单=&lt;br /&gt;
关于&amp;lt;tt&amp;gt;adminmenu_cfgs.txt&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;adminmenu_sorting.txt&amp;lt;/tt&amp;gt;的修改，请参阅[[Admin_Menu_Configuration_%28SourceMod%29%2Fzh|管理员菜单配置]]。&lt;br /&gt;
&lt;br /&gt;
=核心配置=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/core.cfg&amp;lt;/tt&amp;gt;的内容是一些几乎不需要修改的配置，在这里你可以修改跟下面内容相关的配置：&lt;br /&gt;
*日志&lt;br /&gt;
*服务器语言&lt;br /&gt;
*聊天触发器表现&lt;br /&gt;
*管理员密码&lt;br /&gt;
*菜单声音&lt;br /&gt;
&lt;br /&gt;
=Cvar和sourcemod.cfg=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;cfg/sourcemod/sourcemod.cfg&amp;lt;/tt&amp;gt;里包含了SourceMod默认带的所有cvar。每个cvar都有注释：&lt;br /&gt;
*简短文档介绍&lt;br /&gt;
*默认值，以防忘记&lt;br /&gt;
*提供此cvar的插件，如果是Core本身，则不会显示&lt;br /&gt;
&lt;br /&gt;
更多详细信息，请参考实际文件。&lt;br /&gt;
&lt;br /&gt;
有许多SourceMod相关的cvar，并不在&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;中。如果插件编写时依照了正确的规则，SourceMod会为每个插件自动生成一个符合上面规则的配置文件。例如，一个叫&amp;lt;tt&amp;gt;hat.smx&amp;lt;/tt&amp;gt;的插件，那么就会生成文件&amp;lt;tt&amp;gt;cfg/sourcemod/plugin.hat.cfg&amp;lt;/tt&amp;gt;，它的内容可能是这样的：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This file was auto-generated by SourceMod (v1.0.0.986)&lt;br /&gt;
// ConVars for plugin &amp;quot;hat.smx&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// MySQL database&lt;br /&gt;
// -&lt;br /&gt;
// Default: &amp;quot;&amp;quot;&lt;br /&gt;
mysqlk_database &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
每次更变地图时，在&amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;之后，SourceMod会执行&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;和其他插件的配置文件。 is ran.&lt;br /&gt;
&lt;br /&gt;
=数据库=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/databases.cfg&amp;lt;/tt&amp;gt;允许你创建数据库配置，插件通过这些配置建立数据库连接。更多关于编辑此文件，请参阅[[SQL_Admins_%28SourceMod%29#Configuration/zh|Configuring SQL]]。&lt;br /&gt;
&lt;br /&gt;
=地图列表=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/maplists.cfg&amp;lt;/tt&amp;gt;是用来配置SourceMod构建地图列表的。每一小节都是一个可以被插件使用的列表。如果插件请求了一个不存在的列表，它会指向&amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt;小节，然后找到cvar&amp;lt;tt&amp;gt;mapcyclefile&amp;lt;/tt&amp;gt;所指定的文件，并最终指向&amp;lt;tt&amp;gt;maps&amp;lt;/tt&amp;gt;文件夹中的地图。&lt;br /&gt;
&lt;br /&gt;
通过调整这个文件的配置，你可以把所有的地图列表整合到一个中，或者分开以用于不同的需求。&lt;br /&gt;
&lt;br /&gt;
这个文件的头部有更多详细的文档。&lt;br /&gt;
&lt;br /&gt;
=插件加载=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/plugin_settings.cfg&amp;lt;/tt&amp;gt;有一个简单的目的：“永久”把特定插件加入到debug模式，一般来说，其他设置不需要修改。&lt;br /&gt;
&lt;br /&gt;
通过这个文件，你可以禁止某些插件的运行。例如，SourceBans需要禁用&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;，你可以让Core禁止&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;的加载。&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10842</id>
		<title>SourceMod Configuration/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10842"/>
		<updated>2019-09-12T03:28:45Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SourceMod_Configuration}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
此文档会概述一些[[SourceMod]]中配置相关的概念。&lt;br /&gt;
&lt;br /&gt;
=配置的类型=&lt;br /&gt;
SourceMod的配置文件一般处于下面两个文件夹中：&lt;br /&gt;
*&amp;lt;tt&amp;gt;cfg/sourcemod&amp;lt;/tt&amp;gt; - 这里的都是.cfg文件，里面都是控制台变量和指令，这些文件完全可以在服务器控制台中通过exec指令直接运行。&lt;br /&gt;
*&amp;lt;tt&amp;gt;addons/sourcemod/configs&amp;lt;/tt&amp;gt; - 这里的文件内容都是有着特殊或高级格式的，请谨慎编辑，他们不可以在控制台直接运行。&lt;br /&gt;
&lt;br /&gt;
=管理员相关=&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admins_simple.ini&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admins.cfg&amp;lt;/tt&amp;gt;，请参阅[[Adding_Admins_%28SourceMod%29|添加管理员]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;请参阅[[Adding_Groups_%28SourceMod%29|添加组]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_overrides.cfg&amp;lt;/tt&amp;gt;请参阅[[Overriding_Command_Access_%28SourceMod%29|覆写命令权限]]。&lt;br /&gt;
&lt;br /&gt;
文件&amp;lt;tt&amp;gt;admin_levels.cfg&amp;lt;/tt&amp;gt;不应该修改。&lt;br /&gt;
&lt;br /&gt;
=管理员菜单=&lt;br /&gt;
关于&amp;lt;tt&amp;gt;adminmenu_cfgs.txt&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;adminmenu_sorting.txt&amp;lt;/tt&amp;gt;的修改，请参阅[[Admin_Menu_Configuration_%28SourceMod%29|管理员菜单配置]]。&lt;br /&gt;
&lt;br /&gt;
=核心配置=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/core.cfg&amp;lt;/tt&amp;gt;的内容是一些几乎不需要修改的配置，在这里你可以修改跟下面内容相关的配置：&lt;br /&gt;
*日志&lt;br /&gt;
*服务器语言&lt;br /&gt;
*聊天触发器表现&lt;br /&gt;
*管理员密码&lt;br /&gt;
*菜单声音&lt;br /&gt;
&lt;br /&gt;
=Cvar和sourcemod.cfg=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;cfg/sourcemod/sourcemod.cfg&amp;lt;/tt&amp;gt;里包含了SourceMod默认带的所有cvar。每个cvar都有注释：&lt;br /&gt;
*简短文档介绍&lt;br /&gt;
*默认值，以防忘记&lt;br /&gt;
*提供此cvar的插件，如果是Core本身，则不会显示&lt;br /&gt;
&lt;br /&gt;
更多详细信息，请参考实际文件。&lt;br /&gt;
&lt;br /&gt;
有许多SourceMod相关的cvar，并不在&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;中。如果插件编写时依照了正确的规则，SourceMod会为每个插件自动生成一个符合上面规则的配置文件。例如，一个叫&amp;lt;tt&amp;gt;hat.smx&amp;lt;/tt&amp;gt;的插件，那么就会生成文件&amp;lt;tt&amp;gt;cfg/sourcemod/plugin.hat.cfg&amp;lt;/tt&amp;gt;，它的内容可能是这样的：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This file was auto-generated by SourceMod (v1.0.0.986)&lt;br /&gt;
// ConVars for plugin &amp;quot;hat.smx&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// MySQL database&lt;br /&gt;
// -&lt;br /&gt;
// Default: &amp;quot;&amp;quot;&lt;br /&gt;
mysqlk_database &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
每次更变地图时，在&amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;之后，SourceMod会执行&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;和其他插件的配置文件。 is ran.&lt;br /&gt;
&lt;br /&gt;
=数据库=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/databases.cfg&amp;lt;/tt&amp;gt;允许你创建数据库配置，插件通过这些配置建立数据库连接。更多关于编辑此文件，请参阅[[SQL_Admins_%28SourceMod%29#Configuration/zh|Configuring SQL]]。&lt;br /&gt;
&lt;br /&gt;
=地图列表=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/maplists.cfg&amp;lt;/tt&amp;gt;是用来配置SourceMod构建地图列表的。每一小节都是一个可以被插件使用的列表。如果插件请求了一个不存在的列表，它会指向&amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt;小节，然后找到cvar&amp;lt;tt&amp;gt;mapcyclefile&amp;lt;/tt&amp;gt;所指定的文件，并最终指向&amp;lt;tt&amp;gt;maps&amp;lt;/tt&amp;gt;文件夹中的地图。&lt;br /&gt;
&lt;br /&gt;
通过调整这个文件的配置，你可以把所有的地图列表整合到一个中，或者分开以用于不同的需求。&lt;br /&gt;
&lt;br /&gt;
这个文件的头部有更多详细的文档。&lt;br /&gt;
&lt;br /&gt;
=插件加载=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/plugin_settings.cfg&amp;lt;/tt&amp;gt;有一个简单的目的：“永久”把特定插件加入到debug模式，一般来说，其他设置不需要修改。&lt;br /&gt;
&lt;br /&gt;
通过这个文件，你可以禁止某些插件的运行。例如，SourceBans需要禁用&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;，你可以让Core禁止&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;的加载。&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10841</id>
		<title>SourceMod Configuration/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10841"/>
		<updated>2019-09-12T03:28:20Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SourceMod Configuration }}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
此文档会概述一些[[SourceMod]]中配置相关的概念。&lt;br /&gt;
&lt;br /&gt;
=配置的类型=&lt;br /&gt;
SourceMod的配置文件一般处于下面两个文件夹中：&lt;br /&gt;
*&amp;lt;tt&amp;gt;cfg/sourcemod&amp;lt;/tt&amp;gt; - 这里的都是.cfg文件，里面都是控制台变量和指令，这些文件完全可以在服务器控制台中通过exec指令直接运行。&lt;br /&gt;
*&amp;lt;tt&amp;gt;addons/sourcemod/configs&amp;lt;/tt&amp;gt; - 这里的文件内容都是有着特殊或高级格式的，请谨慎编辑，他们不可以在控制台直接运行。&lt;br /&gt;
&lt;br /&gt;
=管理员相关=&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admins_simple.ini&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admins.cfg&amp;lt;/tt&amp;gt;，请参阅[[Adding_Admins_%28SourceMod%29|添加管理员]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;请参阅[[Adding_Groups_%28SourceMod%29|添加组]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_overrides.cfg&amp;lt;/tt&amp;gt;请参阅[[Overriding_Command_Access_%28SourceMod%29|覆写命令权限]]。&lt;br /&gt;
&lt;br /&gt;
文件&amp;lt;tt&amp;gt;admin_levels.cfg&amp;lt;/tt&amp;gt;不应该修改。&lt;br /&gt;
&lt;br /&gt;
=管理员菜单=&lt;br /&gt;
关于&amp;lt;tt&amp;gt;adminmenu_cfgs.txt&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;adminmenu_sorting.txt&amp;lt;/tt&amp;gt;的修改，请参阅[[Admin_Menu_Configuration_%28SourceMod%29|管理员菜单配置]]。&lt;br /&gt;
&lt;br /&gt;
=核心配置=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/core.cfg&amp;lt;/tt&amp;gt;的内容是一些几乎不需要修改的配置，在这里你可以修改跟下面内容相关的配置：&lt;br /&gt;
*日志&lt;br /&gt;
*服务器语言&lt;br /&gt;
*聊天触发器表现&lt;br /&gt;
*管理员密码&lt;br /&gt;
*菜单声音&lt;br /&gt;
&lt;br /&gt;
=Cvar和sourcemod.cfg=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;cfg/sourcemod/sourcemod.cfg&amp;lt;/tt&amp;gt;里包含了SourceMod默认带的所有cvar。每个cvar都有注释：&lt;br /&gt;
*简短文档介绍&lt;br /&gt;
*默认值，以防忘记&lt;br /&gt;
*提供此cvar的插件，如果是Core本身，则不会显示&lt;br /&gt;
&lt;br /&gt;
更多详细信息，请参考实际文件。&lt;br /&gt;
&lt;br /&gt;
有许多SourceMod相关的cvar，并不在&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;中。如果插件编写时依照了正确的规则，SourceMod会为每个插件自动生成一个符合上面规则的配置文件。例如，一个叫&amp;lt;tt&amp;gt;hat.smx&amp;lt;/tt&amp;gt;的插件，那么就会生成文件&amp;lt;tt&amp;gt;cfg/sourcemod/plugin.hat.cfg&amp;lt;/tt&amp;gt;，它的内容可能是这样的：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This file was auto-generated by SourceMod (v1.0.0.986)&lt;br /&gt;
// ConVars for plugin &amp;quot;hat.smx&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// MySQL database&lt;br /&gt;
// -&lt;br /&gt;
// Default: &amp;quot;&amp;quot;&lt;br /&gt;
mysqlk_database &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
每次更变地图时，在&amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;之后，SourceMod会执行&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;和其他插件的配置文件。 is ran.&lt;br /&gt;
&lt;br /&gt;
=数据库=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/databases.cfg&amp;lt;/tt&amp;gt;允许你创建数据库配置，插件通过这些配置建立数据库连接。更多关于编辑此文件，请参阅[[SQL_Admins_%28SourceMod%29#Configuration/zh|Configuring SQL]]。&lt;br /&gt;
&lt;br /&gt;
=地图列表=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/maplists.cfg&amp;lt;/tt&amp;gt;是用来配置SourceMod构建地图列表的。每一小节都是一个可以被插件使用的列表。如果插件请求了一个不存在的列表，它会指向&amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt;小节，然后找到cvar&amp;lt;tt&amp;gt;mapcyclefile&amp;lt;/tt&amp;gt;所指定的文件，并最终指向&amp;lt;tt&amp;gt;maps&amp;lt;/tt&amp;gt;文件夹中的地图。&lt;br /&gt;
&lt;br /&gt;
通过调整这个文件的配置，你可以把所有的地图列表整合到一个中，或者分开以用于不同的需求。&lt;br /&gt;
&lt;br /&gt;
这个文件的头部有更多详细的文档。&lt;br /&gt;
&lt;br /&gt;
=插件加载=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/plugin_settings.cfg&amp;lt;/tt&amp;gt;有一个简单的目的：“永久”把特定插件加入到debug模式，一般来说，其他设置不需要修改。&lt;br /&gt;
&lt;br /&gt;
通过这个文件，你可以禁止某些插件的运行。例如，SourceBans需要禁用&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;，你可以让Core禁止&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;的加载。&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration&amp;diff=10840</id>
		<title>SourceMod Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration&amp;diff=10840"/>
		<updated>2019-09-12T03:27:51Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SourceMod Configuration}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
This document overviews some of the configuration concepts in [[SourceMod]].&lt;br /&gt;
&lt;br /&gt;
=Configuration Types=&lt;br /&gt;
SourceMod's configuration files fall into two folders:&lt;br /&gt;
*&amp;lt;tt&amp;gt;cfg/sourcemod&amp;lt;/tt&amp;gt; - These are .cfg files which contain cvars/commands.  They can be run via 'exec' in the server console.&lt;br /&gt;
*&amp;lt;tt&amp;gt;addons/sourcemod/configs&amp;lt;/tt&amp;gt; - These are files that contain special or advanced formats.  Take care when editing them; they cannot be run via the server console.&lt;br /&gt;
&lt;br /&gt;
=Admins=&lt;br /&gt;
*For &amp;lt;tt&amp;gt;admins_simple.ini&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;admins.cfg&amp;lt;/tt&amp;gt;, see [[Adding_Admins_%28SourceMod%29|Adding Admins]].&lt;br /&gt;
*For &amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;, see [[Adding_Groups_%28SourceMod%29|Adding Groups]].&lt;br /&gt;
*For &amp;lt;tt&amp;gt;admin_overrides.cfg&amp;lt;/tt&amp;gt;, see [[Overriding_Command_Access_%28SourceMod%29|Overriding Command Access]].&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;admin_levels.cfg&amp;lt;/tt&amp;gt; should never be edited.&lt;br /&gt;
&lt;br /&gt;
=Admin Menu=&lt;br /&gt;
For editing &amp;lt;tt&amp;gt;adminmenu_cfgs.txt&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;adminmenu_sorting.txt&amp;lt;/tt&amp;gt;, see [[Admin_Menu_Configuration_%28SourceMod%29|Admin Menu Configuration]].&lt;br /&gt;
&lt;br /&gt;
=Core Configuration=&lt;br /&gt;
The &amp;lt;tt&amp;gt;addons/sourcemod/configs/core.cfg&amp;lt;/tt&amp;gt; file contains settings that will rarely need to be changed.  Here you can change settings related to:&lt;br /&gt;
*Logging&lt;br /&gt;
*Server Language&lt;br /&gt;
*Chat Trigger Behavior&lt;br /&gt;
*Admin Password Variable&lt;br /&gt;
*Menu Sounds&lt;br /&gt;
&lt;br /&gt;
=Cvars and sourcemod.cfg=&lt;br /&gt;
The &amp;lt;tt&amp;gt;cfg/sourcemod/sourcemod.cfg&amp;lt;/tt&amp;gt; file contains all cvars that are included by default with SourceMod.  Each cvar is accompanied comments containing:&lt;br /&gt;
*A small bit of documentation.&lt;br /&gt;
*The default value, in case you forget.&lt;br /&gt;
*The plugin which provides the cvar, or none if Core is the provider.&lt;br /&gt;
&lt;br /&gt;
Reference the actual file for further information.&lt;br /&gt;
&lt;br /&gt;
There are many cvars associated with SourceMod that are not in the &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt; file.  For plugins which follow proper guidelines, SourceMod will automatically generate a configuration file in the above format for each plugin.  For example, a plugin called &amp;lt;tt&amp;gt;hat.smx&amp;lt;/tt&amp;gt; would have a file called &amp;lt;tt&amp;gt;cfg/sourcemod/plugin.hat.cfg&amp;lt;/tt&amp;gt;.  Its contents might look something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This file was auto-generated by SourceMod (v1.0.0.986)&lt;br /&gt;
// ConVars for plugin &amp;quot;hat.smx&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// MySQL database&lt;br /&gt;
// -&lt;br /&gt;
// Default: &amp;quot;&amp;quot;&lt;br /&gt;
mysqlk_database &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SourceMod executes &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt; and each of these plugin files every mapchange, directly after &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; is ran.&lt;br /&gt;
&lt;br /&gt;
=Databases or SQL=&lt;br /&gt;
The &amp;lt;tt&amp;gt;addons/sourcemod/configs/databases.cfg&amp;lt;/tt&amp;gt; file allows you to create named SQL database configurations.  Plugins use these named configurations to establish database connections.  For more information on editing this file, see [[SQL_Admins_%28SourceMod%29#Configuration|Configuring SQL]].&lt;br /&gt;
&lt;br /&gt;
=Map Lists=&lt;br /&gt;
The &amp;lt;tt&amp;gt;addons/sourcemod/configs/maplists.cfg&amp;lt;/tt&amp;gt; file is used to configure how SourceMod builds lists of maps.  Each section is a named list that can be used by plugins.  If a plugin requests a list that does not exist, it will fall back to a &amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt; section, then to the file referenced via the &amp;lt;tt&amp;gt;mapcyclefile&amp;lt;/tt&amp;gt; cvar, and finally to the list of maps in the &amp;lt;tt&amp;gt;maps&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
By tweaking this file, you can consolidate all map lists into one file, or use separate map lists for individual needs.&lt;br /&gt;
&lt;br /&gt;
More documentation is contained inside the file header.&lt;br /&gt;
&lt;br /&gt;
=Plugin Loading=&lt;br /&gt;
The &amp;lt;tt&amp;gt;addons/sourcemod/configs/plugin_settings.cfg&amp;lt;/tt&amp;gt; has one primary purpose: &amp;quot;permanently&amp;quot; putting specific or any plugins into debug mode for the life time of the server.  Generally, the other settings should not be touched.&lt;br /&gt;
&lt;br /&gt;
You can also block plugins from loading using this file.  For example, SourceBans requires the disabling of &amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;.  You can tell Core to block &amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt; from ever loading.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10839</id>
		<title>SourceMod Configuration/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SourceMod_Configuration/zh&amp;diff=10839"/>
		<updated>2019-09-12T03:27:12Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: Created page with &amp;quot;{{Languages|SourceMod Configuration}} __FORCETOC__  此文档会概述一些SourceMod中配置相关的概念。  =配置的类型= SourceMod的配置文件一般处于下...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SourceMod Configuration}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
&lt;br /&gt;
此文档会概述一些[[SourceMod]]中配置相关的概念。&lt;br /&gt;
&lt;br /&gt;
=配置的类型=&lt;br /&gt;
SourceMod的配置文件一般处于下面两个文件夹中：&lt;br /&gt;
*&amp;lt;tt&amp;gt;cfg/sourcemod&amp;lt;/tt&amp;gt; - 这里的都是.cfg文件，里面都是控制台变量和指令，这些文件完全可以在服务器控制台中通过exec指令直接运行。&lt;br /&gt;
*&amp;lt;tt&amp;gt;addons/sourcemod/configs&amp;lt;/tt&amp;gt; - 这里的文件内容都是有着特殊或高级格式的，请谨慎编辑，他们不可以在控制台直接运行。&lt;br /&gt;
&lt;br /&gt;
=管理员相关=&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admins_simple.ini&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admins.cfg&amp;lt;/tt&amp;gt;，请参阅[[Adding_Admins_%28SourceMod%29|添加管理员]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_groups.cfg&amp;lt;/tt&amp;gt;请参阅[[Adding_Groups_%28SourceMod%29|添加组]]。&lt;br /&gt;
*关于&amp;lt;tt&amp;gt;admin_overrides.cfg&amp;lt;/tt&amp;gt;请参阅[[Overriding_Command_Access_%28SourceMod%29|覆写命令权限]]。&lt;br /&gt;
&lt;br /&gt;
文件&amp;lt;tt&amp;gt;admin_levels.cfg&amp;lt;/tt&amp;gt;不应该修改。&lt;br /&gt;
&lt;br /&gt;
=管理员菜单=&lt;br /&gt;
关于&amp;lt;tt&amp;gt;adminmenu_cfgs.txt&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;adminmenu_sorting.txt&amp;lt;/tt&amp;gt;的修改，请参阅[[Admin_Menu_Configuration_%28SourceMod%29|管理员菜单配置]]。&lt;br /&gt;
&lt;br /&gt;
=核心配置=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/core.cfg&amp;lt;/tt&amp;gt;的内容是一些几乎不需要修改的配置，在这里你可以修改跟下面内容相关的配置：&lt;br /&gt;
*日志&lt;br /&gt;
*服务器语言&lt;br /&gt;
*聊天触发器表现&lt;br /&gt;
*管理员密码&lt;br /&gt;
*菜单声音&lt;br /&gt;
&lt;br /&gt;
=Cvar和sourcemod.cfg=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;cfg/sourcemod/sourcemod.cfg&amp;lt;/tt&amp;gt;里包含了SourceMod默认带的所有cvar。每个cvar都有注释：&lt;br /&gt;
*简短文档介绍&lt;br /&gt;
*默认值，以防忘记&lt;br /&gt;
*提供此cvar的插件，如果是Core本身，则不会显示&lt;br /&gt;
&lt;br /&gt;
更多详细信息，请参考实际文件。&lt;br /&gt;
&lt;br /&gt;
有许多SourceMod相关的cvar，并不在&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;中。如果插件编写时依照了正确的规则，SourceMod会为每个插件自动生成一个符合上面规则的配置文件。例如，一个叫&amp;lt;tt&amp;gt;hat.smx&amp;lt;/tt&amp;gt;的插件，那么就会生成文件&amp;lt;tt&amp;gt;cfg/sourcemod/plugin.hat.cfg&amp;lt;/tt&amp;gt;，它的内容可能是这样的：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This file was auto-generated by SourceMod (v1.0.0.986)&lt;br /&gt;
// ConVars for plugin &amp;quot;hat.smx&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// MySQL database&lt;br /&gt;
// -&lt;br /&gt;
// Default: &amp;quot;&amp;quot;&lt;br /&gt;
mysqlk_database &amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
每次更变地图时，在&amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;之后，SourceMod会执行&amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;和其他插件的配置文件。 is ran.&lt;br /&gt;
&lt;br /&gt;
=数据库=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/databases.cfg&amp;lt;/tt&amp;gt;允许你创建数据库配置，插件通过这些配置建立数据库连接。更多关于编辑此文件，请参阅[[SQL_Admins_%28SourceMod%29#Configuration/zh|Configuring SQL]]。&lt;br /&gt;
&lt;br /&gt;
=地图列表=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/maplists.cfg&amp;lt;/tt&amp;gt;是用来配置SourceMod构建地图列表的。每一小节都是一个可以被插件使用的列表。如果插件请求了一个不存在的列表，它会指向&amp;lt;tt&amp;gt;default&amp;lt;/tt&amp;gt;小节，然后找到cvar&amp;lt;tt&amp;gt;mapcyclefile&amp;lt;/tt&amp;gt;所指定的文件，并最终指向&amp;lt;tt&amp;gt;maps&amp;lt;/tt&amp;gt;文件夹中的地图。&lt;br /&gt;
&lt;br /&gt;
通过调整这个文件的配置，你可以把所有的地图列表整合到一个中，或者分开以用于不同的需求。&lt;br /&gt;
&lt;br /&gt;
这个文件的头部有更多详细的文档。&lt;br /&gt;
&lt;br /&gt;
=插件加载=&lt;br /&gt;
文件&amp;lt;tt&amp;gt;addons/sourcemod/configs/plugin_settings.cfg&amp;lt;/tt&amp;gt;有一个简单的目的：“永久”把特定插件加入到debug模式，一般来说，其他设置不需要修改。&lt;br /&gt;
&lt;br /&gt;
通过这个文件，你可以禁止某些插件的运行。例如，SourceBans需要禁用&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;，你可以让Core禁止&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt;的加载。&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Upgrading_SourceMod/zh&amp;diff=10838</id>
		<title>Upgrading SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Upgrading_SourceMod/zh&amp;diff=10838"/>
		<updated>2019-09-12T02:45:56Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Upgrading SourceMod}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
升级SourceMod，除了少数情况下，都保证是会向下兼容的，要完成升级，你应当：&lt;br /&gt;
&lt;br /&gt;
*确保你的服务器（译注：指服务器程序）完全关闭，原因是有的文件处于使用状态时是无法修改的。&lt;br /&gt;
*如果你将要升级到一个稳定版本（非每日构建版），请查看一下发布日志，里面可能存在特定指示。&lt;br /&gt;
*更新这个文件夹下的所有的&amp;lt;tt&amp;gt;.so&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;.dll&amp;lt;/tt&amp;gt;二进制文件：&lt;br /&gt;
**&amp;lt;tt&amp;gt;sourcemod/bin&amp;lt;/tt&amp;gt;&lt;br /&gt;
**&amp;lt;tt&amp;gt;sourcemod/extensions&amp;lt;/tt&amp;gt;&lt;br /&gt;
*更新&amp;lt;tt&amp;gt;sourcemod/gamedata&amp;lt;/tt&amp;gt;这个文件夹下所有的&amp;lt;tt&amp;gt;.txt&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
*更新&amp;lt;tt&amp;gt;sourcemod/translations&amp;lt;/tt&amp;gt;这个文件夹下所有的&amp;lt;tt&amp;gt;.txt&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
*更新&amp;lt;tt&amp;gt;sourcemod/plugins&amp;lt;/tt&amp;gt;这个文件夹下所有的默认二进制&amp;lt;tt&amp;gt;.smx&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
*多看发布日志里是否某些文件有了新功能，如果有，你可以试试进行对配置文件进行微调，你或许需要[http://www.scootersoftware.com/ Beyond Compare]或者[http://winmerge.org/ WinMerge]这样的软件来进行比较。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 如果有插件因SourceMod升级而无法工作，上传bug报告文件，开发组会进行评估。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 升级&amp;lt;tt&amp;gt;gamedata&amp;lt;/tt&amp;gt;下所有的文件是极度重要的，因为这些文件指定了SourceMod与配置文件的交互方式。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 从SourceMod1.2.2起，SourceMod默认不再使用“Auto”文件夹，但第三方扩展可能依旧会使用。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 如果你更新了SourceMod内置的基础插件，那么你也必须更新对应的翻译文件（反之亦然），如果你真的不想更新基础插件，那么你至少要更新&amp;lt;tt&amp;gt;core.phrases.txt&amp;lt;/tt&amp;gt;这个文件。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 如果你是一个插件开发者，你只需要更新.inc文件。而如果你更新了.inc文件，你同时也需要更新编译器(Windows下的pcomp.exe，Linux下的spcomp)。&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Upgrading_SourceMod&amp;diff=10837</id>
		<title>Upgrading SourceMod</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Upgrading_SourceMod&amp;diff=10837"/>
		<updated>2019-09-12T02:45:42Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Upgrading SourceMod}}&lt;br /&gt;
__FORCETOC__&lt;br /&gt;
When upgrading SourceMod, backwards compatibility is guaranteed except in rare cases.  To completely upgrade, you should:&lt;br /&gt;
&lt;br /&gt;
*Ensure your game server is completely stopped and not running as you cannot update some files while they are still in use.&lt;br /&gt;
*If you are upgrading to a stable release (not a nightly build), check its release notes for any specific instructions.&lt;br /&gt;
*Upgrade all &amp;lt;tt&amp;gt;.so&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;.dll&amp;lt;/tt&amp;gt; binaries in the following folders:&lt;br /&gt;
**&amp;lt;tt&amp;gt;sourcemod/bin&amp;lt;/tt&amp;gt;&lt;br /&gt;
**&amp;lt;tt&amp;gt;sourcemod/extensions&amp;lt;/tt&amp;gt;&lt;br /&gt;
*Upgrade all of the &amp;lt;tt&amp;gt;.txt&amp;lt;/tt&amp;gt; files in the &amp;lt;tt&amp;gt;sourcemod/gamedata&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
*Upgrade all of the &amp;lt;tt&amp;gt;.txt&amp;lt;/tt&amp;gt; files in the &amp;lt;tt&amp;gt;sourcemod/translations&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
*Upgrade all of the default &amp;lt;tt&amp;gt;.smx&amp;lt;/tt&amp;gt; binaries in the &amp;lt;tt&amp;gt;sourcemod/plugins&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
*Read the release notes to see if there were any configuration files that got new features.  If so, you should look and see if you want to tweak those new settings.  You may want to use a program such as [http://www.scootersoftware.com/ Beyond Compare] or [http://winmerge.org/ WinMerge].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Note:''' If a plugin stops working due to a SourceMod upgrade, file a bug report and the development team will assess the situation.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' It is extremely important to update all &amp;lt;tt&amp;gt;gamedata&amp;lt;/tt&amp;gt; files, as those specify how SourceMod interacts with mod-specific properties.  &lt;br /&gt;
&lt;br /&gt;
'''Note:''' As of SourceMod 1.2.2, the &amp;quot;auto&amp;quot; folders for SourceMod extensions are no longer used by default. Third party extensions, however, may still use them.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' If you update your base SourceMod plugins, you must update your translation files (and vice versa).  If you absolutely don't want to update your SourceMod base plugins, you should at least update the &amp;lt;tt&amp;gt;core.phrases.txt&amp;lt;/tt&amp;gt; translation file.&lt;br /&gt;
&lt;br /&gt;
'''Note:''' You only need to update the .inc files if you are a plugin developer.  If you do update .inc files, you must update the compiler (spcomp.exe for Windows, spcomp for Linux) as well.&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Upgrading_SourceMod/zh&amp;diff=10836</id>
		<title>Upgrading SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Upgrading_SourceMod/zh&amp;diff=10836"/>
		<updated>2019-09-12T02:42:01Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: Created page with &amp;quot;升级SourceMod，除了少数情况下，都保证是会向下兼容的，要完成升级，你应当：  *确保你的服务器（译注：指服务器程序）完全关闭...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;升级SourceMod，除了少数情况下，都保证是会向下兼容的，要完成升级，你应当：&lt;br /&gt;
&lt;br /&gt;
*确保你的服务器（译注：指服务器程序）完全关闭，原因是有的文件处于使用状态时是无法修改的。&lt;br /&gt;
*如果你将要升级到一个稳定版本（非每日构建版），请查看一下发布日志，里面可能存在特定指示。&lt;br /&gt;
*更新这个文件夹下的所有的&amp;lt;tt&amp;gt;.so&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;.dll&amp;lt;/tt&amp;gt;二进制文件：&lt;br /&gt;
**&amp;lt;tt&amp;gt;sourcemod/bin&amp;lt;/tt&amp;gt;&lt;br /&gt;
**&amp;lt;tt&amp;gt;sourcemod/extensions&amp;lt;/tt&amp;gt;&lt;br /&gt;
*更新&amp;lt;tt&amp;gt;sourcemod/gamedata&amp;lt;/tt&amp;gt;这个文件夹下所有的&amp;lt;tt&amp;gt;.txt&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
*更新&amp;lt;tt&amp;gt;sourcemod/translations&amp;lt;/tt&amp;gt;这个文件夹下所有的&amp;lt;tt&amp;gt;.txt&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
*更新&amp;lt;tt&amp;gt;sourcemod/plugins&amp;lt;/tt&amp;gt;这个文件夹下所有的默认二进制&amp;lt;tt&amp;gt;.smx&amp;lt;/tt&amp;gt;文件。&lt;br /&gt;
*多看发布日志里是否某些文件有了新功能，如果有，你可以试试进行对配置文件进行微调，你或许需要[http://www.scootersoftware.com/ Beyond Compare]或者[http://winmerge.org/ WinMerge]这样的软件来进行比较。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 如果有插件因SourceMod升级而无法工作，上传bug报告文件，开发组会进行评估。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 升级&amp;lt;tt&amp;gt;gamedata&amp;lt;/tt&amp;gt;下所有的文件是极度重要的，因为这些文件指定了SourceMod与配置文件的交互方式。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 从SourceMod1.2.2起，SourceMod默认不再使用“Auto”文件夹，但第三方扩展可能依旧会使用。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 如果你更新了SourceMod内置的基础插件，那么你也必须更新对应的翻译文件（反之亦然），如果你真的不想更新基础插件，那么你至少要更新&amp;lt;tt&amp;gt;core.phrases.txt&amp;lt;/tt&amp;gt;这个文件。&lt;br /&gt;
&lt;br /&gt;
'''Note:''' 如果你是一个插件开发者，你只需要更新.inc文件。而如果你更新了.inc文件，你同时也需要更新编译器(Windows下的pcomp.exe，Linux下的spcomp)。&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)/zh&amp;diff=10835</id>
		<title>War Mode (SourceMod)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)/zh&amp;diff=10835"/>
		<updated>2019-09-11T09:48:19Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|War Mode (SourceMod)}}&lt;br /&gt;
SourceMod并不内置比赛模式。相应的，它是通过配置来锁定SourceMod只加载少量插件。这时，比赛可以在没有SourceMod插件干涉的情况下完成，结束后，SourceMod还是可以“解锁”的。在这个模式下你依旧持有管理员权限。&lt;br /&gt;
&lt;br /&gt;
当然，在比赛模式下，插件的可用与否是非常主观的，你完全可以对SourceMod配置进行个性调整，来保留你想要的功能。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=安全的插件=&lt;br /&gt;
在比赛模式中下面几个插件是绝对安全的，他们是：&lt;br /&gt;
*&amp;lt;tt&amp;gt;adminmenu.smx&amp;lt;/tt&amp;gt; - 提供&amp;lt;tt&amp;gt;sm_admin&amp;lt;/tt&amp;gt;指令。需要注意的是，admin菜单内容都是其他插件添加的，因此如果对应插件没有加载，那么admin菜单里就不会出现相应选项。&lt;br /&gt;
*&amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;admin-sql-threaded.smx&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admin-sql-prefetch.smx&amp;lt;/tt&amp;gt; - 这三个插件是用来加载管理员的。&lt;br /&gt;
*&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt; - 禁人/解禁相关功能。[[Admin_Commands_%28SourceMod%29#Ban/Unban Commands|点我]]查看指令列表。&lt;br /&gt;
*&amp;lt;tt&amp;gt;basecommands.smx&amp;lt;/tt&amp;gt; - 通用、不太过分的功能。[[Admin_Commands_%28SourceMod%29#Basic Commands|点我]]查看指令列表。&lt;br /&gt;
&lt;br /&gt;
=比赛模式的配置=&lt;br /&gt;
比赛模式的实现，非常简单，我们内置了两个模板配置文件，&amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_on.cfg&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_off.cfg&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
第一个文件&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;做了如下三件事：&lt;br /&gt;
*卸载所有插件&lt;br /&gt;
*重载“安全”的插件 &lt;br /&gt;
*锁住，不让其他插件进行加载&lt;br /&gt;
&lt;br /&gt;
第二个文件&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;，移除插件的锁，然后重新加载所有的插件（跟服务器启动时一样的操作）&lt;br /&gt;
&lt;br /&gt;
你可以通过&amp;lt;tt&amp;gt;sm_execcfg&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;rcon exec&amp;lt;/tt&amp;gt;或是admin菜单来运行这些配置文件。当然，由于仅是配置文件，所以你可以随意修改内容或者是文件名称，模板文件放在默认目录下，或者如下所示&lt;br /&gt;
&lt;br /&gt;
=模板文件=&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// 本文件会卸载全部插件&lt;br /&gt;
// 重新加载“安全”的部分&lt;br /&gt;
// 然后再禁止其他插件的加载&lt;br /&gt;
sm plugins unload_all&lt;br /&gt;
sm plugins load basebans.smx&lt;br /&gt;
sm plugins load basecommands.smx&lt;br /&gt;
sm plugins load admin-flatfile.smx&lt;br /&gt;
sm plugins load adminhelp.smx&lt;br /&gt;
sm plugins load adminmenu.smx&lt;br /&gt;
sm plugins load_lock&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// 本文件回解除插件的锁定&lt;br /&gt;
// 并刷新加载所有的插件&lt;br /&gt;
sm plugins load_unlock&lt;br /&gt;
sm plugins refresh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
友情翻译[[User:MisakaSora|MisakaSora]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)/zh&amp;diff=10834</id>
		<title>War Mode (SourceMod)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)/zh&amp;diff=10834"/>
		<updated>2019-09-11T09:46:00Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|War Mode (SourceMod)}}&lt;br /&gt;
SourceMod并不内置比赛模式。相应的，它是通过配置来锁定SourceMod只加载少量插件。这时，比赛可以在没有SourceMod插件干涉的情况下完成，结束后，SourceMod还是可以“解锁”的。在这个模式下你依旧持有管理员权限。&lt;br /&gt;
&lt;br /&gt;
当然，在比赛模式下，插件的可用与否是非常主观的，你完全可以对SourceMod配置进行个性调整，来保留你想要的功能。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=安全的插件=&lt;br /&gt;
在比赛模式中下面几个插件是绝对安全的，他们是：&lt;br /&gt;
*&amp;lt;tt&amp;gt;adminmenu.smx&amp;lt;/tt&amp;gt; - 提供&amp;lt;tt&amp;gt;sm_admin&amp;lt;/tt&amp;gt;指令。需要注意的是，admin菜单内容都是其他插件添加的，因此如果对应插件没有加载，那么admin菜单里就不会出现相应选项。&lt;br /&gt;
*&amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;admin-sql-threaded.smx&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admin-sql-prefetch.smx&amp;lt;/tt&amp;gt; - 这三个插件是用来加载管理员的。&lt;br /&gt;
*&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt; - 禁人/解禁相关功能。[[Admin_Commands_%28SourceMod%29#Ban/Unban Commands|点我]]查看指令列表。&lt;br /&gt;
*&amp;lt;tt&amp;gt;basecommands.smx&amp;lt;/tt&amp;gt; - 通用、不太过分的功能。[[Admin_Commands_%28SourceMod%29#Basic Commands|点我]]查看指令列表。&lt;br /&gt;
&lt;br /&gt;
=比赛模式的配置=&lt;br /&gt;
比赛模式的实现，非常简单，我们内置了两个模板配置文件，&amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_on.cfg&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_off.cfg&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
第一个文件&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;做了如下三件事：&lt;br /&gt;
*卸载所有插件&lt;br /&gt;
*重载“安全”的插件 &lt;br /&gt;
*锁住，不让其他插件进行加载&lt;br /&gt;
&lt;br /&gt;
第二个文件&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;，移除插件的锁，然后重新加载所有的插件（跟服务器启动时一样的操作）&lt;br /&gt;
&lt;br /&gt;
你可以通过&amp;lt;tt&amp;gt;sm_execcfg&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;rcon exec&amp;lt;/tt&amp;gt;或是admin菜单来运行这些配置文件。当然，由于仅是配置文件，所以你可以随意修改内容或者是文件名称，模板文件放在默认目录下，或者如下所示&lt;br /&gt;
&lt;br /&gt;
=模板文件=&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//本文件卸载全部插件，重新加载“安全”的部分，然后禁止其他插件的加载This file unloads all plugins, re-loads a few &amp;quot;safe&amp;quot; ones, and then prevents &lt;br /&gt;
sm plugins unload_all&lt;br /&gt;
sm plugins load basebans.smx&lt;br /&gt;
sm plugins load basecommands.smx&lt;br /&gt;
sm plugins load admin-flatfile.smx&lt;br /&gt;
sm plugins load adminhelp.smx&lt;br /&gt;
sm plugins load adminmenu.smx&lt;br /&gt;
sm plugins load_lock&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//本文件解除插件的锁定，并刷新所有的插件&lt;br /&gt;
sm plugins load_unlock&lt;br /&gt;
sm plugins refresh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[User:MisakaSora|MisakaSora]] ([[User talk:MisakaSora|联系]])&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)&amp;diff=10833</id>
		<title>War Mode (SourceMod)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)&amp;diff=10833"/>
		<updated>2019-09-11T09:43:05Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|War Mode (SourceMod)}}&lt;br /&gt;
SourceMod does not have a &amp;quot;war mode&amp;quot; built-in.  Instead, it has options that let you lock-down SourceMod to only keep a few plugins loaded.  Matches can be completed without SourceMod interfering, and once done, SourceMod can be &amp;quot;unlocked&amp;quot; again.  This way you can still retain administrative control.&lt;br /&gt;
&lt;br /&gt;
Of course, it is subjective as to which plugins are usable in war mode.  You can tweak SourceMod to achieve the functionality you wish to retain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Safe Plugins=&lt;br /&gt;
There are a few plugins that are absolutely safe to use for matches.  They are:&lt;br /&gt;
*&amp;lt;tt&amp;gt;adminhelp.smx&amp;lt;/tt&amp;gt; - Provides the &amp;lt;tt&amp;gt;sm_help&amp;lt;/tt&amp;gt; command.&lt;br /&gt;
*&amp;lt;tt&amp;gt;adminmenu.smx&amp;lt;/tt&amp;gt; - Provides the &amp;lt;tt&amp;gt;sm_admin&amp;lt;/tt&amp;gt; command.  Note that menu items are only added from other plugins, and thus enabling the menu plugin will not add abusive admin commands unless the plugin implementing those commands is also loaded.&lt;br /&gt;
*&amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;admin-sql-threaded.smx&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;admin-sql-prefetch.smx&amp;lt;/tt&amp;gt; - These three plugins simply load administrators.&lt;br /&gt;
*&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt; - Ban/unban functionality.  [[Admin_Commands_%28SourceMod%29#Ban/Unban Commands|Click here]] for command listing.&lt;br /&gt;
*&amp;lt;tt&amp;gt;basecommands.smx&amp;lt;/tt&amp;gt; - General, non-abusive admin commands.  [[Admin_Commands_%28SourceMod%29#Basic Commands|Click here]] for command listing.&lt;br /&gt;
&lt;br /&gt;
=War Mode Configs=&lt;br /&gt;
Implementing warmode is very simple.  Two sample configs are provided as &amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_on.cfg&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_off.cfg&amp;lt;/tt&amp;gt;.  &lt;br /&gt;
&lt;br /&gt;
The first file, &amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;, does three things:&lt;br /&gt;
*Unloads all plugins.&lt;br /&gt;
*Re-loads the &amp;quot;safe&amp;quot; plugins.&lt;br /&gt;
*Locks any more plugins from being loaded.&lt;br /&gt;
&lt;br /&gt;
The second file, &amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;, removes the plugin loading lock, and then reloads all plugins from the plugins folder (as would happen on server start).&lt;br /&gt;
&lt;br /&gt;
You can simply use &amp;lt;tt&amp;gt;sm_execcfg&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;rcon exec&amp;lt;/tt&amp;gt;, or the admin menu to run these configs.  Of course, you can also change or rename them.  Samples are provided in the default package, and also reproduced below.&lt;br /&gt;
&lt;br /&gt;
=Sample Files=&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//This file unloads all plugins, re-loads a few &amp;quot;safe&amp;quot; ones, and then prevents &lt;br /&gt;
//any more plugins from being loaded.&lt;br /&gt;
sm plugins unload_all&lt;br /&gt;
sm plugins load basebans.smx&lt;br /&gt;
sm plugins load basecommands.smx&lt;br /&gt;
sm plugins load admin-flatfile.smx&lt;br /&gt;
sm plugins load adminhelp.smx&lt;br /&gt;
sm plugins load adminmenu.smx&lt;br /&gt;
sm plugins load_lock&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//This file re-enables a server from &amp;quot;war mode&amp;quot; by unlocking plugin loading &lt;br /&gt;
//and refreshing the plugins list.&lt;br /&gt;
sm plugins load_unlock&lt;br /&gt;
sm plugins refresh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)/zh&amp;diff=10832</id>
		<title>War Mode (SourceMod)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)/zh&amp;diff=10832"/>
		<updated>2019-09-11T09:42:25Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|War Mode (SourceMod)}}&lt;br /&gt;
SourceMod并不内置比赛模式。相应的，它是通过配置来锁定SourceMod只加载少量插件。这时，比赛可以在没有SourceMod插件干涉的情况下完成，结束后，SourceMod还是可以“解锁”的。在这个模式下你依旧持有管理员权限。&lt;br /&gt;
&lt;br /&gt;
当然，在比赛模式下，插件的可用与否是非常主观的，你完全可以对SourceMod配置进行个性调整，来保留你想要的功能。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=安全的插件=&lt;br /&gt;
在比赛模式中下面几个插件是绝对安全的，他们是：&lt;br /&gt;
*&amp;lt;tt&amp;gt;adminmenu.smx&amp;lt;/tt&amp;gt; - 提供&amp;lt;tt&amp;gt;sm_admin&amp;lt;/tt&amp;gt;指令。需要注意的是，admin菜单内容都是其他插件添加的，因此如果对应插件没有加载，那么admin菜单里就不会出现相应选项。&lt;br /&gt;
*&amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;admin-sql-threaded.smx&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admin-sql-prefetch.smx&amp;lt;/tt&amp;gt; - 这三个插件是用来加载管理员的。&lt;br /&gt;
*&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt; - 禁人/解禁相关功能。[[Admin_Commands_%28SourceMod%29#Ban/Unban Commands|点我]]查看指令列表。&lt;br /&gt;
*&amp;lt;tt&amp;gt;basecommands.smx&amp;lt;/tt&amp;gt; - 通用、不太过分的功能。[[Admin_Commands_%28SourceMod%29#Basic Commands|点我]]查看指令列表。&lt;br /&gt;
&lt;br /&gt;
=比赛模式的配置=&lt;br /&gt;
比赛模式的实现，非常简单，我们内置了两个模板配置文件，&amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_on.cfg&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_off.cfg&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
第一个文件&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;做了如下三件事：&lt;br /&gt;
*卸载所有插件&lt;br /&gt;
*重载“安全”的插件 &lt;br /&gt;
*锁住，不让其他插件进行加载&lt;br /&gt;
&lt;br /&gt;
第二个文件&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;，移除插件的锁，然后重新加载所有的插件（跟服务器启动时一样的操作）&lt;br /&gt;
&lt;br /&gt;
你可以通过&amp;lt;tt&amp;gt;sm_execcfg&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;rcon exec&amp;lt;/tt&amp;gt;或是admin菜单来运行这些配置文件。当然，由于仅是配置文件，所以你可以随意修改内容或者是文件名称，模板文件放在默认目录下，或者如下所示&lt;br /&gt;
&lt;br /&gt;
=模板文件=&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//本文件卸载全部插件，重新加载“安全”的部分，然后禁止其他插件的加载This file unloads all plugins, re-loads a few &amp;quot;safe&amp;quot; ones, and then prevents &lt;br /&gt;
sm plugins unload_all&lt;br /&gt;
sm plugins load basebans.smx&lt;br /&gt;
sm plugins load basecommands.smx&lt;br /&gt;
sm plugins load admin-flatfile.smx&lt;br /&gt;
sm plugins load adminhelp.smx&lt;br /&gt;
sm plugins load adminmenu.smx&lt;br /&gt;
sm plugins load_lock&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//本文件解除插件的锁定，并刷新所有的插件&lt;br /&gt;
sm plugins load_unlock&lt;br /&gt;
sm plugins refresh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)/zh&amp;diff=10831</id>
		<title>War Mode (SourceMod)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=War_Mode_(SourceMod)/zh&amp;diff=10831"/>
		<updated>2019-09-11T09:41:39Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: Created page with &amp;quot;__FORCETOC__ {{Languages|Creating War Mode (SourceMod)}} SourceMod并不内置比赛模式。相应的，它是通过配置来锁定SourceMod只加载少量插件。这时，...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|Creating War Mode (SourceMod)}}&lt;br /&gt;
SourceMod并不内置比赛模式。相应的，它是通过配置来锁定SourceMod只加载少量插件。这时，比赛可以在没有SourceMod插件干涉的情况下完成，结束后，SourceMod还是可以“解锁”的。在这个模式下你依旧持有管理员权限。&lt;br /&gt;
&lt;br /&gt;
当然，在比赛模式下，插件的可用与否是非常主观的，你完全可以对SourceMod配置进行个性调整，来保留你想要的功能。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=安全的插件=&lt;br /&gt;
在比赛模式中下面几个插件是绝对安全的，他们是：&lt;br /&gt;
*&amp;lt;tt&amp;gt;adminmenu.smx&amp;lt;/tt&amp;gt; - 提供&amp;lt;tt&amp;gt;sm_admin&amp;lt;/tt&amp;gt;指令。需要注意的是，admin菜单内容都是其他插件添加的，因此如果对应插件没有加载，那么admin菜单里就不会出现相应选项。&lt;br /&gt;
*&amp;lt;tt&amp;gt;admin-flatfile.smx&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;admin-sql-threaded.smx&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;admin-sql-prefetch.smx&amp;lt;/tt&amp;gt; - 这三个插件是用来加载管理员的。&lt;br /&gt;
*&amp;lt;tt&amp;gt;basebans.smx&amp;lt;/tt&amp;gt; - 禁人/解禁相关功能。[[Admin_Commands_%28SourceMod%29#Ban/Unban Commands|点我]]查看指令列表。&lt;br /&gt;
*&amp;lt;tt&amp;gt;basecommands.smx&amp;lt;/tt&amp;gt; - 通用、不太过分的功能。[[Admin_Commands_%28SourceMod%29#Basic Commands|点我]]查看指令列表。&lt;br /&gt;
&lt;br /&gt;
=比赛模式的配置=&lt;br /&gt;
比赛模式的实现，非常简单，我们内置了两个模板配置文件，&amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_on.cfg&amp;lt;/tt&amp;gt;和&amp;lt;tt&amp;gt;cfg/sourcemod/sm_warmode_off.cfg&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
第一个文件&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;做了如下三件事：&lt;br /&gt;
*卸载所有插件&lt;br /&gt;
*重载“安全”的插件 &lt;br /&gt;
*锁住，不让其他插件进行加载&lt;br /&gt;
&lt;br /&gt;
第二个文件&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;，移除插件的锁，然后重新加载所有的插件（跟服务器启动时一样的操作）&lt;br /&gt;
&lt;br /&gt;
你可以通过&amp;lt;tt&amp;gt;sm_execcfg&amp;lt;/tt&amp;gt;，&amp;lt;tt&amp;gt;rcon exec&amp;lt;/tt&amp;gt;或是admin菜单来运行这些配置文件。当然，由于仅是配置文件，所以你可以随意修改内容或者是文件名称，模板文件放在默认目录下，或者如下所示&lt;br /&gt;
&lt;br /&gt;
=模板文件=&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_on.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//本文件卸载全部插件，重新加载“安全”的部分，然后禁止其他插件的加载This file unloads all plugins, re-loads a few &amp;quot;safe&amp;quot; ones, and then prevents &lt;br /&gt;
sm plugins unload_all&lt;br /&gt;
sm plugins load basebans.smx&lt;br /&gt;
sm plugins load basecommands.smx&lt;br /&gt;
sm plugins load admin-flatfile.smx&lt;br /&gt;
sm plugins load adminhelp.smx&lt;br /&gt;
sm plugins load adminmenu.smx&lt;br /&gt;
sm plugins load_lock&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;sm_warmode_off.cfg&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//本文件解除插件的锁定，并刷新所有的插件&lt;br /&gt;
sm plugins load_unlock&lt;br /&gt;
sm plugins refresh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10795</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10795"/>
		<updated>2019-08-30T09:52:29Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
安装SourceMod非常简单，几乎不需要添加什么配置修改。&lt;br /&gt;
=前提条件=&lt;br /&gt;
一个图形界面的网页浏览器，下载Metamod和SourceMod压缩包。&lt;br /&gt;
一个可以把文件拷贝到你专用服务主机上的工具。&lt;br /&gt;
&lt;br /&gt;
SourceMod需要1.9.0或更高的[[Metamod:Source]]（建议使用最新版本）。[http://www.metamodsource.net/ 点我]访问Metamod:Source主页。手动安装SourceMM的教程可以在[[Installing Metamod:Source|这]]找到。&lt;br /&gt;
&lt;br /&gt;
SourceMod几乎可以运行在所有起源引擎的游戏/模组上。&lt;br /&gt;
&lt;br /&gt;
Valve在SourceMod的不同版本间，有时会在游戏中有不兼容SourceMod的更新。当出现这种情况时，你可能需要安装Metamod和SourceMod的快照版本，可以在[[Required Versions (SourceMod)|需要的版本]]页面进行查看自己是否需要。&lt;br /&gt;
&lt;br /&gt;
=上传/安装=&lt;br /&gt;
==本地服务器==&lt;br /&gt;
本地安装[http://www.sourcemod.net/downloads.php SourceMod]，仅需要把&amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows)或&amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux)文件解压到游戏目录下，比如Counter-Strike(反恐精英)的&amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt;目录下、Day of Defeat(胜利之日)的&amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt;目录下等等。&lt;br /&gt;
&lt;br /&gt;
==远程服务器==&lt;br /&gt;
远程安装SourceMod，首先在你本地电脑上解压&amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows)或&amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux)压缩包，解压后你会看到&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;文件夹。&lt;br /&gt;
&lt;br /&gt;
先定位到你的游戏文件夹，比如Counter-Strike(反恐精英)的&amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt;、Day of Defeat(胜利之日)的&amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt;，在这个文件夹下，应该有一个addons文件夹（如果没有，很可能是你没有安装Metamod:Source），然后使用[https://encrypted.google.com/#q=FTP FTP]这样的工具，从你本地的&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;文件夹，把整个内容上传到远程的&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;文件夹下。结束后你的远程&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;文件夹下应该有&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt;文件夹。&lt;br /&gt;
&lt;br /&gt;
如果在这些步骤上有点问题，你需要去了解一下FTP和服务器管理。不过，你可以找服务器供应者寻求帮助，一些服务器供应者可能会有服务器的图形化管理界面。&lt;br /&gt;
&lt;br /&gt;
另外，如果你直接把tar,gz文件拷贝到srcds所在的文件夹，在cstrike子目录直接执行下面代码：&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=检查安装=&lt;br /&gt;
你的文件夹布局应该是这样的：&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - 你的游戏模组目录&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
当SourceMod上传好/拷贝好后，配置好Metamod:Source，然后完全重启游戏服务器（只是程序，不是电脑重启）。如果是本地的，直接关闭程序然后重启就好。如果是远程服务器，你可能需要寻求服务器供应者的帮助。不过，由于大部分服务器供应者会自动重启服务器，所以通过[[rcon]]直接输入quit指令也是安全的。&lt;br /&gt;
&lt;br /&gt;
首先，在你的[[服务器控制台]](不是客户端控制台)，输入：&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果插件安装成功，你会看到下面的输出内容：&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你接下来就可以使用SourceMod的控制台指令了：&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
一个例子：&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
最后，假定你已经设置好管理员（如果没有，参照[[Adding Admins (SourceMod)|这个页面]]），你可以加入游戏后测试游戏菜单，只需要在客户端控制台输入：&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
你应该会看到一个有一些选项的菜单。&lt;br /&gt;
&lt;br /&gt;
=问题解决=&lt;br /&gt;
如果安装失败，你可能是下面四种情况之一。&lt;br /&gt;
&lt;br /&gt;
==Metamod报错NOFILE或FAILED==&lt;br /&gt;
如果“meta list”指令的返回结果是这样的：&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
大多数情况，这要么是文件定位错误，要么就是文件无法加载。想查看更多详情，使用下面的指令（不需要对正确的插件进行查询）：&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod显示无插件==&lt;br /&gt;
如果“meta list”指令的返回结果是这样的：&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
有下面几种原因：&lt;br /&gt;
&lt;br /&gt;
# 最常见的原因是addons/metamod目录下找不到sourcemod.vdf文件，确保sourcemod.vdf在这个目录下。&lt;br /&gt;
# 如果sourcemod.vdf文件存在，确保使用了正确版本的Sourcemod（zip文件用于windows，tar.gz用于linux）&lt;br /&gt;
&lt;br /&gt;
==Metamod无返回==&lt;br /&gt;
如果“meta list”根本没有返回值，说明Metamod:Source没有正确安装。[[Installing Metamod:Source|这个页面]]可以教你如何解决这个问题。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10794</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10794"/>
		<updated>2019-08-26T09:57:52Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
安装SourceMod非常简单，几乎不需要添加什么配置修改。&lt;br /&gt;
=前提条件=&lt;br /&gt;
一个图形界面的网页浏览器，下载Metamod和SourceMod压缩包。&lt;br /&gt;
一个可以把文件拷贝到你专用服务主机上的工具。&lt;br /&gt;
&lt;br /&gt;
SourceMod需要1.9.0或更高的[[Metamod:Source]]（建议使用最新版本）。[http://www.metamodsource.net/ 点我]访问Metamod:Source主页。手动安装SourceMM的教程可以在[[Installing Metamod:Source|这]]找到。&lt;br /&gt;
&lt;br /&gt;
SourceMod几乎可以运行在所有起源引擎的游戏/模组上。&lt;br /&gt;
&lt;br /&gt;
Valve在SourceMod的不同版本间，有时会在游戏中有不兼容SourceMod的更新。当出现这种情况时，你可能需要安装Metamod和SourceMod的快照版本，可以在[[Required Versions (SourceMod)|需要的版本]]页面进行查看自己是否需要。&lt;br /&gt;
&lt;br /&gt;
=上传/安装=&lt;br /&gt;
==本地服务器==&lt;br /&gt;
本地安装[http://www.sourcemod.net/downloads.php SourceMod]，仅需要把&amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows)或&amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux)文件解压到游戏目录下，比如Counter-Strike(反恐精英)的&amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt;目录下、Day of Defeat(胜利之日)的&amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt;目录下等等。&lt;br /&gt;
&lt;br /&gt;
==远程服务器==&lt;br /&gt;
远程安装SourceMod，首先在你本地电脑上解压&amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows)或&amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux)压缩包，解压后你会看到&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;文件夹。&lt;br /&gt;
&lt;br /&gt;
先定位到你的游戏文件夹，比如Counter-Strike(反恐精英)的&amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt;、Day of Defeat(胜利之日)的&amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt;，在这个文件夹下，应该有一个addons文件夹（如果没有，很可能是你没有安装Metamod:Source），然后使用[https://encrypted.google.com/#q=FTP FTP]这样的工具，从你本地的&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;文件夹，把整个内容上传到远程的&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;文件夹下。结束后你的远程&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;文件夹下应该有&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt;文件夹。&lt;br /&gt;
&lt;br /&gt;
如果在这些步骤上有点问题，你需要去了解一下FTP和服务器管理。不过，你可以找服务器供应者寻求帮助，一些服务器供应者可能会有服务器的图形化管理界面。&lt;br /&gt;
&lt;br /&gt;
另外，如果你直接把tar,gz文件拷贝到srcds所在的文件夹，在cstrike子目录直接执行下面代码：&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=检查安装=&lt;br /&gt;
你的文件夹布局应该是这样的：&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - 你的游戏模组目录&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
当SourceMod上传好/拷贝好后，配置好Metamod:Source，然后完全重启游戏服务器（只是程序，不是电脑重启）。如果是本地的，直接关闭程序然后重启就好。如果是远程服务器，你可能需要寻求服务器供应者的帮助。不过，由于大部分服务器供应者会自动重启服务器，所以通过[[rcon]]直接输入quit指令也是安全的。&lt;br /&gt;
&lt;br /&gt;
首先，在你的[[服务器控制台]](不是客户端控制台)，输入：&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
如果插件安装成功，你会看到下面的输出内容：&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你接下来就可以使用SourceMod的控制台指令了：&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
一个例子：&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
最后，假定你已经设置好管理员（如果没有，参照[[Adding Admins (SourceMod)|这个页面]]），你可以加入游戏后测试游戏菜单，只需要在客户端控制台输入：&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
你应该会看到一个有一些选项的菜单。&lt;br /&gt;
&lt;br /&gt;
=问题解决=&lt;br /&gt;
如果安装失败了，你大致会看到下面四种症状中的一种。&lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10793</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10793"/>
		<updated>2019-08-26T06:31:59Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
安装SourceMod非常简单，几乎不需要添加什么配置修改。&lt;br /&gt;
=前提条件=&lt;br /&gt;
一个图形界面的网页浏览器，下载Metamod和SourceMod压缩包。&lt;br /&gt;
一个可以把文件拷贝到你专用服务主机上的工具。&lt;br /&gt;
&lt;br /&gt;
SourceMod需要1.9.0或更高的[[Metamod:Source]]（建议使用最新版本）。[http://www.metamodsource.net/ 点我]访问Metamod:Source主页。手动安装SourceMM的教程可以在[[Installing Metamod:Source|这]]找到。&lt;br /&gt;
&lt;br /&gt;
SourceMod几乎可以运行在所有起源引擎的游戏/模组上。&lt;br /&gt;
&lt;br /&gt;
Valve在SourceMod的不同版本间，有时会在游戏中有不兼容SourceMod的更新。当出现这种情况时，你可能需要安装Metamod和SourceMod的快照版本，可以在[[Required Versions (SourceMod)|需要的版本]]页面进行查看自己是否需要。&lt;br /&gt;
&lt;br /&gt;
=上传/安装=&lt;br /&gt;
==本地服务器==&lt;br /&gt;
本地安装[http://www.sourcemod.net/downloads.php SourceMod]，仅需要把&amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows)或&amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux)文件解压到游戏目录下，比如Counter-Strike(反恐精英)的&amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt;目录下、Day of Defeat(胜利之日)的&amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt;目录下等等。&lt;br /&gt;
&lt;br /&gt;
==Remote Server==&lt;br /&gt;
To install SourceMod remotely, first extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your local computer (for example, your Desktop).  You will see an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  &lt;br /&gt;
&lt;br /&gt;
Using a tool such as [https://encrypted.google.com/#q=FTP FTP], locate your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat:Source, et cetera).  Underneath this folder, you should have an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder (if not, Metamod:Source is probably not installed).  From your local &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder, upload the entire contents to your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  When done, your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder should have a &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
If you have trouble with these steps, you need to get acquainted with FTP and server management.  However, you can also ask your server provider for help.  Some providers also have web interfaces for managing your server.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if you copied the tar.gz to your srcds directory, execute the following from the cstrike sub directory:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Checking the Install=&lt;br /&gt;
Your folder layout should look like:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - Your mod's folder&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
Once SourceMod is uploaded/copied and configured with Metamod:Source, restart your server completely.  If it is local, shut it down and restart it.  If it is remote, you may need to ask your server provider for help.  However, it is often safe to issue a &amp;quot;quit&amp;quot; command via [[rcon]], since most providers will automatically restart your server.&lt;br /&gt;
&lt;br /&gt;
First, in your [[server console]] (not client console), type:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the install worked, you will see something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should then be able to use the SourceMod root console command, which can be invoked with simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lastly, assuming you have already setup your administration user (if not, see [[Adding Admins (SourceMod)|this page]]), you can test the in game menu by joining the server, and in the client console type the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see a menu popup with all you options.&lt;br /&gt;
&lt;br /&gt;
=Troubleshooting=&lt;br /&gt;
If the install failed, you will generally see one of four symptoms.  &lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10792</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10792"/>
		<updated>2019-08-26T06:30:40Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
安装SourceMod非常简单，几乎不需要添加什么配置修改。&lt;br /&gt;
=前提条件=&lt;br /&gt;
一个图形界面的网页浏览器，下载Metamod和SourceMod压缩包。&lt;br /&gt;
一个可以把文件拷贝到你专用服务主机上的工具。&lt;br /&gt;
&lt;br /&gt;
SourceMod需要1.9.0或更高的[[Metamod:Source]]（建议使用最新版本）。[http://www.metamodsource.net/ 点我]访问Metamod:Source主页。手动安装SourceMM的教程可以在[[Installing Metamod:Source|这]]找到。&lt;br /&gt;
&lt;br /&gt;
SourceMod几乎可以运行在所有起源引擎的游戏/模组上。&lt;br /&gt;
&lt;br /&gt;
Valve在SourceMod的不同版本间，有时会在游戏中有不兼容SourceMod的更新。当出现这种情况时，你可能需要安装Metamod和SourceMod的快照版本，可以在[[Required Versions (SourceMod)|需要的版本]]页面进行查看自己是否需要。&lt;br /&gt;
&lt;br /&gt;
=上传/安装=&lt;br /&gt;
==本地服务器==&amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt;&lt;br /&gt;
本地安装[http://www.sourcemod.net/downloads.php SourceMod]，仅需要把&amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux)文件解压到游戏目录下，比如Counter-Strike(反恐精英)的&amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt;目录下、Day of Defeat(胜利之日)的&amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt;目录下等等。&lt;br /&gt;
&lt;br /&gt;
==Remote Server==&lt;br /&gt;
To install SourceMod remotely, first extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your local computer (for example, your Desktop).  You will see an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  &lt;br /&gt;
&lt;br /&gt;
Using a tool such as [https://encrypted.google.com/#q=FTP FTP], locate your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat:Source, et cetera).  Underneath this folder, you should have an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder (if not, Metamod:Source is probably not installed).  From your local &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder, upload the entire contents to your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  When done, your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder should have a &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
If you have trouble with these steps, you need to get acquainted with FTP and server management.  However, you can also ask your server provider for help.  Some providers also have web interfaces for managing your server.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if you copied the tar.gz to your srcds directory, execute the following from the cstrike sub directory:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Checking the Install=&lt;br /&gt;
Your folder layout should look like:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - Your mod's folder&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
Once SourceMod is uploaded/copied and configured with Metamod:Source, restart your server completely.  If it is local, shut it down and restart it.  If it is remote, you may need to ask your server provider for help.  However, it is often safe to issue a &amp;quot;quit&amp;quot; command via [[rcon]], since most providers will automatically restart your server.&lt;br /&gt;
&lt;br /&gt;
First, in your [[server console]] (not client console), type:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the install worked, you will see something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should then be able to use the SourceMod root console command, which can be invoked with simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lastly, assuming you have already setup your administration user (if not, see [[Adding Admins (SourceMod)|this page]]), you can test the in game menu by joining the server, and in the client console type the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see a menu popup with all you options.&lt;br /&gt;
&lt;br /&gt;
=Troubleshooting=&lt;br /&gt;
If the install failed, you will generally see one of four symptoms.  &lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10791</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10791"/>
		<updated>2019-08-26T06:20:55Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
安装SourceMod非常简单，几乎不需要添加什么配置修改。&lt;br /&gt;
=前提条件=&lt;br /&gt;
一个图形界面的网页浏览器，下载Metamod和SourceMod压缩包。&lt;br /&gt;
一个可以把文件拷贝到你专用服务主机上的工具。&lt;br /&gt;
&lt;br /&gt;
SourceMod需要1.9.0或更高的[[Metamod:Source]]（建议使用最新版本）。[http://www.metamodsource.net/ 点我]访问Metamod:Source主页。手动安装SourceMM的教程可以在[[Installing Metamod:Source|这]]找到。&lt;br /&gt;
&lt;br /&gt;
SourceMod几乎可以运行在所有起源引擎的游戏/模组上。&lt;br /&gt;
&lt;br /&gt;
Valve在SourceMod的不同版本间，有时会在游戏中有不兼容SourceMod的更新。当出现这种情况时，你需要安装Metamod和SourceMoD的快照版本，你可以在[[Required Versions (SourceMod)|需要特殊版本]]页面进行查看自己是否需要。&lt;br /&gt;
&lt;br /&gt;
=Uploading/Installing=&lt;br /&gt;
==Local Server==&lt;br /&gt;
To install [http://www.sourcemod.net/downloads.php SourceMod] locally, simply extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat, et cetera).&lt;br /&gt;
&lt;br /&gt;
==Remote Server==&lt;br /&gt;
To install SourceMod remotely, first extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your local computer (for example, your Desktop).  You will see an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  &lt;br /&gt;
&lt;br /&gt;
Using a tool such as [https://encrypted.google.com/#q=FTP FTP], locate your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat:Source, et cetera).  Underneath this folder, you should have an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder (if not, Metamod:Source is probably not installed).  From your local &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder, upload the entire contents to your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  When done, your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder should have a &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
If you have trouble with these steps, you need to get acquainted with FTP and server management.  However, you can also ask your server provider for help.  Some providers also have web interfaces for managing your server.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if you copied the tar.gz to your srcds directory, execute the following from the cstrike sub directory:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Checking the Install=&lt;br /&gt;
Your folder layout should look like:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - Your mod's folder&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
Once SourceMod is uploaded/copied and configured with Metamod:Source, restart your server completely.  If it is local, shut it down and restart it.  If it is remote, you may need to ask your server provider for help.  However, it is often safe to issue a &amp;quot;quit&amp;quot; command via [[rcon]], since most providers will automatically restart your server.&lt;br /&gt;
&lt;br /&gt;
First, in your [[server console]] (not client console), type:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the install worked, you will see something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should then be able to use the SourceMod root console command, which can be invoked with simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lastly, assuming you have already setup your administration user (if not, see [[Adding Admins (SourceMod)|this page]]), you can test the in game menu by joining the server, and in the client console type the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see a menu popup with all you options.&lt;br /&gt;
&lt;br /&gt;
=Troubleshooting=&lt;br /&gt;
If the install failed, you will generally see one of four symptoms.  &lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10790</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10790"/>
		<updated>2019-08-26T06:06:24Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
Installing SourceMod is very simple, and it can be added with almost no configuration changes.&lt;br /&gt;
&lt;br /&gt;
=Prerequisites=&lt;br /&gt;
A GUI Web Browser to retrieve Metamod and SourceMod compressed archives.&lt;br /&gt;
A tool to copy archive to your dedicated server host.&lt;br /&gt;
&lt;br /&gt;
SourceMod requires [[Metamod:Source]] 1.9.0 or higher (it is recommended that the latest version is used). [http://www.metamodsource.net/ Click here] to visit the Metamod:Source homepage. Instructions to install SourceMM manually can be found [[Installing Metamod:Source|here]].&lt;br /&gt;
&lt;br /&gt;
SourceMod will run on almost any mod built using the Source SDK.&lt;br /&gt;
&lt;br /&gt;
Valve sometimes makes changes in their games that break SourceMod between releases.  When this happens, you may need to install snapshot versions of Metamod and SourceMod.  You can see if this is required on the [[Required Versions (SourceMod)|Required Versions]] page.&lt;br /&gt;
&lt;br /&gt;
=Uploading/Installing=&lt;br /&gt;
==Local Server==&lt;br /&gt;
To install [http://www.sourcemod.net/downloads.php SourceMod] locally, simply extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat, et cetera).&lt;br /&gt;
&lt;br /&gt;
==Remote Server==&lt;br /&gt;
To install SourceMod remotely, first extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your local computer (for example, your Desktop).  You will see an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  &lt;br /&gt;
&lt;br /&gt;
Using a tool such as [https://encrypted.google.com/#q=FTP FTP], locate your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat:Source, et cetera).  Underneath this folder, you should have an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder (if not, Metamod:Source is probably not installed).  From your local &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder, upload the entire contents to your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  When done, your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder should have a &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
If you have trouble with these steps, you need to get acquainted with FTP and server management.  However, you can also ask your server provider for help.  Some providers also have web interfaces for managing your server.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if you copied the tar.gz to your srcds directory, execute the following from the cstrike sub directory:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Checking the Install=&lt;br /&gt;
Your folder layout should look like:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - Your mod's folder&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
Once SourceMod is uploaded/copied and configured with Metamod:Source, restart your server completely.  If it is local, shut it down and restart it.  If it is remote, you may need to ask your server provider for help.  However, it is often safe to issue a &amp;quot;quit&amp;quot; command via [[rcon]], since most providers will automatically restart your server.&lt;br /&gt;
&lt;br /&gt;
First, in your [[server console]] (not client console), type:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the install worked, you will see something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should then be able to use the SourceMod root console command, which can be invoked with simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lastly, assuming you have already setup your administration user (if not, see [[Adding Admins (SourceMod)|this page]]), you can test the in game menu by joining the server, and in the client console type the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see a menu popup with all you options.&lt;br /&gt;
&lt;br /&gt;
=Troubleshooting=&lt;br /&gt;
If the install failed, you will generally see one of four symptoms.  &lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10789</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10789"/>
		<updated>2019-08-26T06:05:52Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
Installing SourceMod is very simple, and it can be added with almost no configuration changes.&lt;br /&gt;
&lt;br /&gt;
=Prerequisites=&lt;br /&gt;
A GUI Web Browser to retrieve Metamod and SourceMod compressed archives.&lt;br /&gt;
A tool to copy archive to your dedicated server host.&lt;br /&gt;
&lt;br /&gt;
SourceMod requires [[Metamod:Source]] 1.9.0 or higher (it is recommended that the latest version is used). [http://www.metamodsource.net/ Click here] to visit the Metamod:Source homepage. Instructions to install SourceMM manually can be found [[Installing Metamod:Source|here]].&lt;br /&gt;
&lt;br /&gt;
SourceMod will run on almost any mod built using the Source SDK.&lt;br /&gt;
&lt;br /&gt;
Valve sometimes makes changes in their games that break SourceMod between releases.  When this happens, you may need to install snapshot versions of Metamod and SourceMod.  You can see if this is required on the [[Required Versions (SourceMod)|Required Versions]] page.&lt;br /&gt;
&lt;br /&gt;
=Uploading/Installing=&lt;br /&gt;
==Local Server==&lt;br /&gt;
To install [http://www.sourcemod.net/downloads.php SourceMod] locally, simply extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat, et cetera).&lt;br /&gt;
&lt;br /&gt;
==Remote Server==&lt;br /&gt;
To install SourceMod remotely, first extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your local computer (for example, your Desktop).  You will see an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  &lt;br /&gt;
&lt;br /&gt;
Using a tool such as [https://encrypted.google.com/#q=FTP FTP], locate your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat:Source, et cetera).  Underneath this folder, you should have an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder (if not, Metamod:Source is probably not installed).  From your local &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder, upload the entire contents to your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  When done, your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder should have a &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
If you have trouble with these steps, you need to get acquainted with FTP and server management.  However, you can also ask your server provider for help.  Some providers also have web interfaces for managing your server.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if you copied the tar.gz to your srcds directory, execute the following from the cstrike sub directory:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Checking the Install=&lt;br /&gt;
Your folder layout should look like:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - Your mod's folder&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
Once SourceMod is uploaded/copied and configured with Metamod:Source, restart your server completely.  If it is local, shut it down and restart it.  If it is remote, you may need to ask your server provider for help.  However, it is often safe to issue a &amp;quot;quit&amp;quot; command via [[rcon]], since most providers will automatically restart your server.&lt;br /&gt;
&lt;br /&gt;
First, in your [[server console]] (not client console), type:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the install worked, you will see something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should then be able to use the SourceMod root console command, which can be invoked with simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lastly, assuming you have already setup your administration user (if not, see [[Adding Admins (SourceMod)|this page]]), you can test the in game menu by joining the server, and in the client console type the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see a menu popup with all you options.&lt;br /&gt;
&lt;br /&gt;
=Troubleshooting=&lt;br /&gt;
If the install failed, you will generally see one of four symptoms.  &lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10788</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10788"/>
		<updated>2019-08-26T06:05:32Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|Istalling_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
Installing SourceMod is very simple, and it can be added with almost no configuration changes.&lt;br /&gt;
&lt;br /&gt;
=Prerequisites=&lt;br /&gt;
A GUI Web Browser to retrieve Metamod and SourceMod compressed archives.&lt;br /&gt;
A tool to copy archive to your dedicated server host.&lt;br /&gt;
&lt;br /&gt;
SourceMod requires [[Metamod:Source]] 1.9.0 or higher (it is recommended that the latest version is used). [http://www.metamodsource.net/ Click here] to visit the Metamod:Source homepage. Instructions to install SourceMM manually can be found [[Installing Metamod:Source|here]].&lt;br /&gt;
&lt;br /&gt;
SourceMod will run on almost any mod built using the Source SDK.&lt;br /&gt;
&lt;br /&gt;
Valve sometimes makes changes in their games that break SourceMod between releases.  When this happens, you may need to install snapshot versions of Metamod and SourceMod.  You can see if this is required on the [[Required Versions (SourceMod)|Required Versions]] page.&lt;br /&gt;
&lt;br /&gt;
=Uploading/Installing=&lt;br /&gt;
==Local Server==&lt;br /&gt;
To install [http://www.sourcemod.net/downloads.php SourceMod] locally, simply extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat, et cetera).&lt;br /&gt;
&lt;br /&gt;
==Remote Server==&lt;br /&gt;
To install SourceMod remotely, first extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your local computer (for example, your Desktop).  You will see an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  &lt;br /&gt;
&lt;br /&gt;
Using a tool such as [https://encrypted.google.com/#q=FTP FTP], locate your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat:Source, et cetera).  Underneath this folder, you should have an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder (if not, Metamod:Source is probably not installed).  From your local &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder, upload the entire contents to your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  When done, your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder should have a &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
If you have trouble with these steps, you need to get acquainted with FTP and server management.  However, you can also ask your server provider for help.  Some providers also have web interfaces for managing your server.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if you copied the tar.gz to your srcds directory, execute the following from the cstrike sub directory:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Checking the Install=&lt;br /&gt;
Your folder layout should look like:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - Your mod's folder&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
Once SourceMod is uploaded/copied and configured with Metamod:Source, restart your server completely.  If it is local, shut it down and restart it.  If it is remote, you may need to ask your server provider for help.  However, it is often safe to issue a &amp;quot;quit&amp;quot; command via [[rcon]], since most providers will automatically restart your server.&lt;br /&gt;
&lt;br /&gt;
First, in your [[server console]] (not client console), type:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the install worked, you will see something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should then be able to use the SourceMod root console command, which can be invoked with simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lastly, assuming you have already setup your administration user (if not, see [[Adding Admins (SourceMod)|this page]]), you can test the in game menu by joining the server, and in the client console type the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see a menu popup with all you options.&lt;br /&gt;
&lt;br /&gt;
=Troubleshooting=&lt;br /&gt;
If the install failed, you will generally see one of four symptoms.  &lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10787</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10787"/>
		<updated>2019-08-26T06:04:12Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
{{Languages|Istalling_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
Installing SourceMod is very simple, and it can be added with almost no configuration changes.&lt;br /&gt;
&lt;br /&gt;
=Prerequisites=&lt;br /&gt;
A GUI Web Browser to retrieve Metamod and SourceMod compressed archives.&lt;br /&gt;
A tool to copy archive to your dedicated server host.&lt;br /&gt;
&lt;br /&gt;
SourceMod requires [[Metamod:Source]] 1.9.0 or higher (it is recommended that the latest version is used). [http://www.metamodsource.net/ Click here] to visit the Metamod:Source homepage. Instructions to install SourceMM manually can be found [[Installing Metamod:Source|here]].&lt;br /&gt;
&lt;br /&gt;
SourceMod will run on almost any mod built using the Source SDK.&lt;br /&gt;
&lt;br /&gt;
Valve sometimes makes changes in their games that break SourceMod between releases.  When this happens, you may need to install snapshot versions of Metamod and SourceMod.  You can see if this is required on the [[Required Versions (SourceMod)|Required Versions]] page.&lt;br /&gt;
&lt;br /&gt;
=Uploading/Installing=&lt;br /&gt;
==Local Server==&lt;br /&gt;
To install [http://www.sourcemod.net/downloads.php SourceMod] locally, simply extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat, et cetera).&lt;br /&gt;
&lt;br /&gt;
==Remote Server==&lt;br /&gt;
To install SourceMod remotely, first extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your local computer (for example, your Desktop).  You will see an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  &lt;br /&gt;
&lt;br /&gt;
Using a tool such as [https://encrypted.google.com/#q=FTP FTP], locate your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat:Source, et cetera).  Underneath this folder, you should have an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder (if not, Metamod:Source is probably not installed).  From your local &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder, upload the entire contents to your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  When done, your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder should have a &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
If you have trouble with these steps, you need to get acquainted with FTP and server management.  However, you can also ask your server provider for help.  Some providers also have web interfaces for managing your server.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if you copied the tar.gz to your srcds directory, execute the following from the cstrike sub directory:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Checking the Install=&lt;br /&gt;
Your folder layout should look like:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - Your mod's folder&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
Once SourceMod is uploaded/copied and configured with Metamod:Source, restart your server completely.  If it is local, shut it down and restart it.  If it is remote, you may need to ask your server provider for help.  However, it is often safe to issue a &amp;quot;quit&amp;quot; command via [[rcon]], since most providers will automatically restart your server.&lt;br /&gt;
&lt;br /&gt;
First, in your [[server console]] (not client console), type:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the install worked, you will see something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should then be able to use the SourceMod root console command, which can be invoked with simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lastly, assuming you have already setup your administration user (if not, see [[Adding Admins (SourceMod)|this page]]), you can test the in game menu by joining the server, and in the client console type the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see a menu popup with all you options.&lt;br /&gt;
&lt;br /&gt;
=Troubleshooting=&lt;br /&gt;
If the install failed, you will generally see one of four symptoms.  &lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10786</id>
		<title>Installing SourceMod/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Installing_SourceMod/zh&amp;diff=10786"/>
		<updated>2019-08-26T06:03:46Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: Created page with &amp;quot;{{Languages|Installing_SourceMod}}  Installing SourceMod is very simple, and it can be added with almost no configuration changes.  =Prerequisites= A GUI Web Browser to retrie...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Installing_SourceMod}}&lt;br /&gt;
&lt;br /&gt;
Installing SourceMod is very simple, and it can be added with almost no configuration changes.&lt;br /&gt;
&lt;br /&gt;
=Prerequisites=&lt;br /&gt;
A GUI Web Browser to retrieve Metamod and SourceMod compressed archives.&lt;br /&gt;
A tool to copy archive to your dedicated server host.&lt;br /&gt;
&lt;br /&gt;
SourceMod requires [[Metamod:Source]] 1.9.0 or higher (it is recommended that the latest version is used). [http://www.metamodsource.net/ Click here] to visit the Metamod:Source homepage. Instructions to install SourceMM manually can be found [[Installing Metamod:Source|here]].&lt;br /&gt;
&lt;br /&gt;
SourceMod will run on almost any mod built using the Source SDK.&lt;br /&gt;
&lt;br /&gt;
Valve sometimes makes changes in their games that break SourceMod between releases.  When this happens, you may need to install snapshot versions of Metamod and SourceMod.  You can see if this is required on the [[Required Versions (SourceMod)|Required Versions]] page.&lt;br /&gt;
&lt;br /&gt;
=Uploading/Installing=&lt;br /&gt;
==Local Server==&lt;br /&gt;
To install [http://www.sourcemod.net/downloads.php SourceMod] locally, simply extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat, et cetera).&lt;br /&gt;
&lt;br /&gt;
==Remote Server==&lt;br /&gt;
To install SourceMod remotely, first extract the &amp;lt;tt&amp;gt;.zip&amp;lt;/tt&amp;gt; (Windows) or &amp;lt;tt&amp;gt;.tar.gz&amp;lt;/tt&amp;gt; (Linux) package to your local computer (for example, your Desktop).  You will see an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  &lt;br /&gt;
&lt;br /&gt;
Using a tool such as [https://encrypted.google.com/#q=FTP FTP], locate your mod folder (i.e. &amp;lt;tt&amp;gt;cstrike&amp;lt;/tt&amp;gt; for Counter-Strike:Source, &amp;lt;tt&amp;gt;dod&amp;lt;/tt&amp;gt; for Day of Defeat:Source, et cetera).  Underneath this folder, you should have an &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder (if not, Metamod:Source is probably not installed).  From your local &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder, upload the entire contents to your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder.  When done, your remote &amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt; folder should have a &amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
If you have trouble with these steps, you need to get acquainted with FTP and server management.  However, you can also ask your server provider for help.  Some providers also have web interfaces for managing your server.&lt;br /&gt;
&lt;br /&gt;
Alternatively, if you copied the tar.gz to your srcds directory, execute the following from the cstrike sub directory:&lt;br /&gt;
tar -xzf ../sourcemod-1.1.0.tar.gz&lt;br /&gt;
&lt;br /&gt;
=Checking the Install=&lt;br /&gt;
Your folder layout should look like:&lt;br /&gt;
*&amp;lt;tt&amp;gt;[mod]&amp;lt;/tt&amp;gt; - Your mod's folder&lt;br /&gt;
**&amp;lt;tt&amp;gt;addons&amp;lt;/tt&amp;gt;&lt;br /&gt;
***&amp;lt;tt&amp;gt;metamod&amp;lt;/tt&amp;gt; - Metamod:Source&lt;br /&gt;
***&amp;lt;tt&amp;gt;sourcemod&amp;lt;/tt&amp;gt; - SourceMod&lt;br /&gt;
&lt;br /&gt;
Once SourceMod is uploaded/copied and configured with Metamod:Source, restart your server completely.  If it is local, shut it down and restart it.  If it is remote, you may need to ask your server provider for help.  However, it is often safe to issue a &amp;quot;quit&amp;quot; command via [[rcon]], since most providers will automatically restart your server.&lt;br /&gt;
&lt;br /&gt;
First, in your [[server console]] (not client console), type:&lt;br /&gt;
&amp;lt;pre&amp;gt;meta list&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the install worked, you will see something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
    [01] SourceMod (1.1.0.2489) by AlliedModders LLC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should then be able to use the SourceMod root console command, which can be invoked with simply:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;] sm version&lt;br /&gt;
 SourceMod Version Information:&lt;br /&gt;
    SourceMod Version: 1.1.0.2489&lt;br /&gt;
    SourcePawn Engine: SourcePawn 1.1, jit-x86 (build 1.1.0-svn)&lt;br /&gt;
    SourcePawn API: v1 = 4, v2 = 2&lt;br /&gt;
    Compiled on: Sep  5 2008 02:02:12&lt;br /&gt;
    http://www.sourcemod.net/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lastly, assuming you have already setup your administration user (if not, see [[Adding Admins (SourceMod)|this page]]), you can test the in game menu by joining the server, and in the client console type the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_admin&amp;lt;/pre&amp;gt;&lt;br /&gt;
You should see a menu popup with all you options.&lt;br /&gt;
&lt;br /&gt;
=Troubleshooting=&lt;br /&gt;
If the install failed, you will generally see one of four symptoms.  &lt;br /&gt;
&lt;br /&gt;
==Metamod reports NOFILE or FAILED==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 1 plugin:&lt;br /&gt;
  [01] &amp;lt;NOFILE&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Most likely, either the files are not located in the correct place, or the file could not be loaded.  For more information, use the following command (except use the correct list number):&lt;br /&gt;
&amp;lt;pre&amp;gt;meta info 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Metamod lists no plugins==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; replies with something like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;] meta list&lt;br /&gt;
Listing 0 plugins:&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are several causes of this error.&lt;br /&gt;
&lt;br /&gt;
# The most common cause is that sourcemod.vdf can't be located in the addons/metamod folder.  Verify that sourcemod.vdf is present in this folder.&lt;br /&gt;
# If sourcemod.vdf is present, make sure you are using the correct build of Sourcemod (zip = windows, tar.gz = linux).&lt;br /&gt;
&lt;br /&gt;
==Metamod says nothing==&lt;br /&gt;
If &amp;quot;meta list&amp;quot; has no reply at all, Metamod:Source is not properly installed. [[Installing Metamod:Source|This wiki page]] may provide you with clues on how to solve this problem.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Documentation]]&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10776</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10776"/>
		<updated>2019-07-11T09:50:34Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* Setting up plugin info */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Introduction to SourceMod Plugins}}&lt;br /&gt;
本指南将向你介绍如何编写[[SourceMod]]的插件。如果你还不熟悉SourcePawn语言的话，建议你先去看看[[Introduction to SourcePawn]]。&lt;br /&gt;
&lt;br /&gt;
关于编译插件，请看[[Compiling SourceMod Plugins]]。你可以使用[https://forums.alliedmods.net/showthread.php?t=259917 SPEdit]，[https://www.crimsoneditor.com/ Crimson Editor]，[http://www.pspad.com/ PSPad]，[http://www.ultraedit.com/ UltraEdit]，[https://notepad-plus-plus.org/ Notepad++]，[https://www.textpad.com/ TextPad]，[http://sourceforge.net/projects/pawnstudio/ Pawn Studio]，[https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn]等编辑器来编写插件，或者你也可以使用其他你喜欢的文本编辑器。（译注：推荐SPEdit）&lt;br /&gt;
&lt;br /&gt;
=从头开始=&lt;br /&gt;
打开你最喜欢的文本编辑器，然后新建一个空白文件，建好之后，你就可以开始写代码了，但是，实际上你现在是没法使用SourceMod的功能的，因为此时编译器还不认识它们。这是设计成这样的，以便可以在SourceMod以外的地方使用SourcePawn。但是，我们现在要写的是SourceMod的插件，还是先启用SourceMod的功能吧。这一步是通过include指令来实现的，它告诉编译器将另一个文件中的代码“粘贴”到你的文件中。&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
这都是怎么运作的呢？首先，请注意我们用尖括号把文件名包起来了。尖括号会让编译器从默认的目录里查找文件。默认的目录是'''scripting/include'''，你可以现在打开这个目录，有很多的inc文件在里面。这些是sourceMod包含的文件，描述了sourceMod插件可用的各种功能、标记和其他功能。它们都是明文保存的，欢迎阅读。不过，你会发现里面其实没有多少代码，很显然是不够实现SourceMod全部的伟大功能的。那么它们在哪呢？它们是在用C++实现的SourceMod内核中实现的，并被编译成二进制文件，最终存放在'''bin'''目录中。所以，SourcePawn的代码和SM核心是没法链接在一起的，如果编译器不知道后者存在的话。SourceMod的include文件都是专门写的，所以当它们说函数的实现在''其它地方''，编译器可以理解，并生成特殊代码表明这个函数是外部调用的。每当SourceMod加载你的插件，它会检查代码对应比特并替换成它的内部方法，这叫做[http://en.wikipedia.org/wiki/Dynamic_linking 动态链接（dynamic linking）]。&lt;br /&gt;
&lt;br /&gt;
=设置插件信息=&lt;br /&gt;
既然我们终于可以使用SourceMod的功能了，是时候设置通过指令&amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt;显示的插件信息了。不会有人喜欢没名字的插件的。我们可以看看'''sourcemod.inc'''文件来了解（如何设置插件信息），以及信息声明时要用的格式。在SM的include文件寻找你不知道的信息总是有用的。这是官方的[http://docs.sourcemod.net/api/ API文档]，但是它有点过时，并且只有SM的核心文件的API，如果你使用了第三方扩展或者插件，你就需要学习一下'''inc'''文件。现在，我们打开There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10775</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10775"/>
		<updated>2019-07-11T09:31:18Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 从头开始 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Introduction to SourceMod Plugins}}&lt;br /&gt;
本指南将向你介绍如何编写[[SourceMod]]的插件。如果你还不熟悉SourcePawn语言的话，建议你先去看看[[Introduction to SourcePawn]]。&lt;br /&gt;
&lt;br /&gt;
关于编译插件，请看[[Compiling SourceMod Plugins]]。你可以使用[https://forums.alliedmods.net/showthread.php?t=259917 SPEdit]，[https://www.crimsoneditor.com/ Crimson Editor]，[http://www.pspad.com/ PSPad]，[http://www.ultraedit.com/ UltraEdit]，[https://notepad-plus-plus.org/ Notepad++]，[https://www.textpad.com/ TextPad]，[http://sourceforge.net/projects/pawnstudio/ Pawn Studio]，[https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn]等编辑器来编写插件，或者你也可以使用其他你喜欢的文本编辑器。（译注：推荐SPEdit）&lt;br /&gt;
&lt;br /&gt;
=从头开始=&lt;br /&gt;
打开你最喜欢的文本编辑器，然后新建一个空白文件，建好之后，你就可以开始写代码了，但是，实际上你现在是没法使用SourceMod的功能的，因为此时编译器还不认识它们。这是设计成这样的，以便可以在SourceMod以外的地方使用SourcePawn。但是，我们现在要写的是SourceMod的插件，还是先启用SourceMod的功能吧。这一步是通过include指令来实现的，它告诉编译器将另一个文件中的代码“粘贴”到你的文件中。&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
这都是怎么运作的呢？首先，请注意我们用尖括号把文件名包起来了。尖括号会让编译器从默认的目录里查找文件。默认的目录是'''scripting/include'''，你可以现在打开这个目录，有很多的inc文件在里面。这些是sourceMod包含的文件，描述了sourceMod插件可用的各种功能、标记和其他功能。它们都是明文保存的，欢迎阅读。不过，你会发现里面其实没有多少代码，很显然是不够实现SourceMod全部的伟大功能的。那么它们在哪呢？它们是在用C++实现的SourceMod内核中实现的，并被编译成二进制文件，最终存放在'''bin'''目录中。所以，SourcePawn的代码和SM核心是没法链接在一起的，如果编译器不知道后者存在的话。SourceMod的include文件都是专门写的，所以当它们说函数的实现在''其它地方''，编译器可以理解，并生成特殊代码表明这个函数是外部调用的。每当SourceMod加载你的插件，它会检查代码对应比特并替换成它的内部方法，这叫做[http://en.wikipedia.org/wiki/Dynamic_linking 动态链接（dynamic linking）]。&lt;br /&gt;
&lt;br /&gt;
=Setting up plugin info=&lt;br /&gt;
Now that we got access to SourceMod features, it is time to set up the information that will be displayed via &amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt; command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10774</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10774"/>
		<updated>2019-07-11T09:30:04Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 从头开始 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Introduction to SourceMod Plugins}}&lt;br /&gt;
本指南将向你介绍如何编写[[SourceMod]]的插件。如果你还不熟悉SourcePawn语言的话，建议你先去看看[[Introduction to SourcePawn]]。&lt;br /&gt;
&lt;br /&gt;
关于编译插件，请看[[Compiling SourceMod Plugins]]。你可以使用[https://forums.alliedmods.net/showthread.php?t=259917 SPEdit]，[https://www.crimsoneditor.com/ Crimson Editor]，[http://www.pspad.com/ PSPad]，[http://www.ultraedit.com/ UltraEdit]，[https://notepad-plus-plus.org/ Notepad++]，[https://www.textpad.com/ TextPad]，[http://sourceforge.net/projects/pawnstudio/ Pawn Studio]，[https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn]等编辑器来编写插件，或者你也可以使用其他你喜欢的文本编辑器。（译注：推荐SPEdit）&lt;br /&gt;
&lt;br /&gt;
=从头开始=&lt;br /&gt;
打开你最喜欢的文本编辑器，然后新建一个空白文件，建好之后，你就可以开始写代码了，但是，实际上你现在是没法使用SourceMod的功能的，因为此时编译器还不认识它们。这是设计成这样的，以便可以在SourceMod以外的地方使用SourcePawn。但是，我们现在要写的是SourceMod的插件，还是先启用SourceMod的功能吧。这一步是通过include指令来实现的，它告诉编译器将另一个文件中的代码“粘贴”到你的文件中。&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
这都是怎么运作的呢？首先，请注意我们用尖括号把文件名包起来了。尖括号会让编译器从默认的目录里查找文件。默认的目录是'''scripting/include'''，你可以现在打开这个目录，有很多的inc文件在里面。这些是sourceMod包含的文件，描述了sourceMod插件可用的各种功能、标记和其他功能。它们都是明文保存的，欢迎阅读。不过，你会发现里面其实没有多少代码，很显然是不够实现SourceMod全部的伟大功能的。那么它们在哪呢？它们是在用C++实现的SourceMod内核中实现的，并被编译成二进制文件，最终存放在'''bin'''目录中。所以，SourcePawn的代码和SM核心是没法链接在一起的，如果编译器不知道后者存在的话。SourceMod的include文件都是专门写的，所以当它们说函数的实现在''其它地方''，编译器可以理解，并生成特殊代码表明这个函数是外部调用的。每当SourceMod加载你的插件，它会检查代码对应比特并替换它的内部方法，这叫做[http://en.wikipedia.org/wiki/Dynamic_linking 动态链接（dynamic linking）]。&lt;br /&gt;
&lt;br /&gt;
=Setting up plugin info=&lt;br /&gt;
Now that we got access to SourceMod features, it is time to set up the information that will be displayed via &amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt; command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10773</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10773"/>
		<updated>2019-07-11T08:32:07Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 从头开始 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Introduction to SourceMod Plugins}}&lt;br /&gt;
本指南将向你介绍如何编写[[SourceMod]]的插件。如果你还不熟悉SourcePawn语言的话，建议你先去看看[[Introduction to SourcePawn]]。&lt;br /&gt;
&lt;br /&gt;
关于编译插件，请看[[Compiling SourceMod Plugins]]。你可以使用[https://forums.alliedmods.net/showthread.php?t=259917 SPEdit]，[https://www.crimsoneditor.com/ Crimson Editor]，[http://www.pspad.com/ PSPad]，[http://www.ultraedit.com/ UltraEdit]，[https://notepad-plus-plus.org/ Notepad++]，[https://www.textpad.com/ TextPad]，[http://sourceforge.net/projects/pawnstudio/ Pawn Studio]，[https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn]等编辑器来编写插件，或者你也可以使用其他你喜欢的文本编辑器。（译注：推荐SPEdit）&lt;br /&gt;
&lt;br /&gt;
=从头开始=&lt;br /&gt;
打开你最喜欢的文本编辑器，然后新建一个空白文件，建好之后，你就可以开始写代码了，但是，实际上你现在是没法使用SourceMod的功能的，因为此时编译器还不认识它们。这是设计成这样的，以便可以在SourceMod以外的地方使用SourcePawn。但是，我们现在要写的是SourceMod的插件，还是先启用SourceMod的功能吧。这一步是通过include指令来实现的，它告诉编译器将另一个文件中的代码“粘贴”到你的文件中。&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
这都是怎么运作的呢？首先，请注意我们用尖括号把文件名包起来了。尖括号会让编译器从默认的目录里查找文件。默认的目录是'''scripting/include'''，你可以现在打开这个目录，有很多的inc文件在里面。这些是sourceMod包含的文件，描述了sourceMod插件可用的各种功能、标记和其他功能。它们都是明文保存的，欢迎阅读。不过，你会发现里面其实没有多少代码，很显然是不够实现SourceMod全部的伟大功能的。那么它们在哪呢？它们是在用C++实现的SourceMod内核中实现的，并被编译成二进制文件，最终存放在'''bin'''目录中。所以，SourcePawn的代码和SM核心是没法链接在一起的，如果编译器不知道后者存在的。So how does your SourcePawn code and SM core link together if the compiler doesn't know about the existence of the latter? SourceMod include files are written specially, so they say that the implementation of functions is ''somewhere else''. The compiler understands that and generates a special code that says that this function call is going outside. When SourceMod loads your plugin, it inspects these bits of code and substitutes it's own internal functions instead. This is called [http://en.wikipedia.org/wiki/Dynamic_linking dynamic linking].&lt;br /&gt;
&lt;br /&gt;
=Setting up plugin info=&lt;br /&gt;
Now that we got access to SourceMod features, it is time to set up the information that will be displayed via &amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt; command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10772</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10772"/>
		<updated>2019-07-05T03:27:20Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 从头开始 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Introduction to SourceMod Plugins}}&lt;br /&gt;
本指南将向你介绍如何编写[[SourceMod]]的插件。如果你还不熟悉SourcePawn语言的话，建议你先去看看[[Introduction to SourcePawn]]。&lt;br /&gt;
&lt;br /&gt;
关于编译插件，请看[[Compiling SourceMod Plugins]]。你可以使用[https://forums.alliedmods.net/showthread.php?t=259917 SPEdit]，[https://www.crimsoneditor.com/ Crimson Editor]，[http://www.pspad.com/ PSPad]，[http://www.ultraedit.com/ UltraEdit]，[https://notepad-plus-plus.org/ Notepad++]，[https://www.textpad.com/ TextPad]，[http://sourceforge.net/projects/pawnstudio/ Pawn Studio]，[https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn]等编辑器来编写插件，或者你也可以使用其他你喜欢的文本编辑器。（译注：推荐SPEdit）&lt;br /&gt;
&lt;br /&gt;
=从头开始=&lt;br /&gt;
打开你最喜欢的文本编辑器，然后新建一个空白文件，建好之后，你就可以开始写代码了，但是，实际上你现在是没法使用SourceMod的功能的，因为此时编译器还不认识它们。这是故意设计成这样的，为了可以在SourceMod以外的地方使用SourcePawn。但是，现在要写SourceMod的插件，我们还是先启用SourceMod的功能吧。这是通过include指令来实现的，它告诉编译器将另一个文件中的代码“粘贴”到你的文件中。&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
这是怎么运作的呢？首先，请注意我们用尖括号把文件名包起来了。尖括号会让编译器从默认的目录里查找文件。默认的目录是'''scripting/include'''，你可以现在打开这个目录，有很多的文件在里面。How does this work? First of all, note that we enclosed file name into angle brackets. Angle brackets tell the compiler to look in the default include directory. By default, it is '''scripting/include'''. You can open it right now and see a lot of inc files there. Those are SourceMod include files that describe various functions, tags and other features available for SourceMod plugins. The files are plain-text and you are encouraged to read them. You will notice, however, that there's not much code in there, certainly not enough to implement all the great features of SourceMod, so where are they? They are implemented inside a SourceMod core which is written in C++ and is compiled into binary files which end up in '''bin''' directory. So how does your SourcePawn code and SM core link together if the compiler doesn't know about the existence of the latter? SourceMod include files are written specially, so they say that the implementation of functions is ''somewhere else''. The compiler understands that and generates a special code that says that this function call is going outside. When SourceMod loads your plugin, it inspects these bits of code and substitutes it's own internal functions instead. This is called [http://en.wikipedia.org/wiki/Dynamic_linking dynamic linking].&lt;br /&gt;
&lt;br /&gt;
=Setting up plugin info=&lt;br /&gt;
Now that we got access to SourceMod features, it is time to set up the information that will be displayed via &amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt; command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10771</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10771"/>
		<updated>2019-07-04T09:45:22Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 从头开始 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Introduction to SourceMod Plugins}}&lt;br /&gt;
本指南将向你介绍如何编写[[SourceMod]]的插件。如果你还不熟悉SourcePawn语言的话，建议你先去看看[[Introduction to SourcePawn]]。&lt;br /&gt;
&lt;br /&gt;
关于编译插件，请看[[Compiling SourceMod Plugins]]。你可以使用[https://forums.alliedmods.net/showthread.php?t=259917 SPEdit]，[https://www.crimsoneditor.com/ Crimson Editor]，[http://www.pspad.com/ PSPad]，[http://www.ultraedit.com/ UltraEdit]，[https://notepad-plus-plus.org/ Notepad++]，[https://www.textpad.com/ TextPad]，[http://sourceforge.net/projects/pawnstudio/ Pawn Studio]，[https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn]等编辑器来编写插件，或者你也可以使用其他你喜欢的文本编辑器。（译注：推荐SPEdit）&lt;br /&gt;
&lt;br /&gt;
=从头开始=&lt;br /&gt;
打开你最喜欢的文本编辑器，然后新建一个空白文件，建好之后，你就可以开始写代码了，但是，实际上你现在是没法使用SourceMod的功能的，因为此时编译器还不认识它们。这是故意设计成这样的，为了可以在SourceMod以外的地方使用SourcePawn。但是，现在要写SourceMod的插件，我们还是先启用SourceMod的功能吧。使用include指令可以实现，它告诉编译器将另一个文件中的代码“粘贴”到你的文件中。&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
这是怎么工作的呢？首先，请注意我们用尖括号把文件名包起来了。尖括号让编译器从默认的目录里查找文件。How does this work? First of all, note that we enclosed file name into angle brackets. Angle brackets tell the compiler to look in the default include directory. By default, it is '''scripting/include'''. You can open it right now and see a lot of inc files there. Those are SourceMod include files that describe various functions, tags and other features available for SourceMod plugins. The files are plain-text and you are encouraged to read them. You will notice, however, that there's not much code in there, certainly not enough to implement all the great features of SourceMod, so where are they? They are implemented inside a SourceMod core which is written in C++ and is compiled into binary files which end up in '''bin''' directory. So how does your SourcePawn code and SM core link together if the compiler doesn't know about the existence of the latter? SourceMod include files are written specially, so they say that the implementation of functions is ''somewhere else''. The compiler understands that and generates a special code that says that this function call is going outside. When SourceMod loads your plugin, it inspects these bits of code and substitutes it's own internal functions instead. This is called [http://en.wikipedia.org/wiki/Dynamic_linking dynamic linking].&lt;br /&gt;
&lt;br /&gt;
=Setting up plugin info=&lt;br /&gt;
Now that we got access to SourceMod features, it is time to set up the information that will be displayed via &amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt; command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10769</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10769"/>
		<updated>2019-07-04T03:30:36Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Introduction to SourceMod Plugins}}&lt;br /&gt;
本指南将向你介绍如何编写[[SourceMod]]的插件。如果你还不熟悉SourcePawn语言的话，建议你先去看看[[Introduction to SourcePawn]]。&lt;br /&gt;
&lt;br /&gt;
关于编译插件，请看[[Compiling SourceMod Plugins]]。你可以使用[https://forums.alliedmods.net/showthread.php?t=259917 SPEdit]，[https://www.crimsoneditor.com/ Crimson Editor]，[http://www.pspad.com/ PSPad]，[http://www.ultraedit.com/ UltraEdit]，[https://notepad-plus-plus.org/ Notepad++]，[https://www.textpad.com/ TextPad]，[http://sourceforge.net/projects/pawnstudio/ Pawn Studio]，[https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn]等编辑器来编写插件，或者你也可以使用其他你喜欢的文本编辑器。（译注：推荐SPEdit）&lt;br /&gt;
&lt;br /&gt;
=从头开始=&lt;br /&gt;
打开你最喜欢的文本编辑器，然后新建一个空白文件。建好后，你就可以开始写代码了，但是你实际上是没法使用SourceMod的功能的，因为编译器不认识它们。这是故意为之，这样就可以在SourceMod以外的地方使用SourcePawn了。不过，我们这里是在写SourceMod插件，我们还是先启用SourceMod的功能吧。这是使用include指令完成的。它告诉编译器将另一个文件中的代码“粘贴”到你的文件中。Open your favorite text editor and create a new empty file. When you have an empty file you can just start writing code using the core language, however, you will not be able to use any of SourceMod features because the compiler does not know about them. This is done deliberately so it is possible to use SourcePawn outside of SourceMod. But since we are writing a SourceMod plugin, it is a good idea to enable access to SourceMod features first. This is done using &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; directive. It tells the compiler to &amp;quot;paste&amp;quot; the code from another file into yours.&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
How does this work? First of all, note that we enclosed file name into angle brackets. Angle brackets tell the compiler to look in the default include directory. By default, it is '''scripting/include'''. You can open it right now and see a lot of inc files there. Those are SourceMod include files that describe various functions, tags and other features available for SourceMod plugins. The files are plain-text and you are encouraged to read them. You will notice, however, that there's not much code in there, certainly not enough to implement all the great features of SourceMod, so where are they? They are implemented inside a SourceMod core which is written in C++ and is compiled into binary files which end up in '''bin''' directory. So how does your SourcePawn code and SM core link together if the compiler doesn't know about the existence of the latter? SourceMod include files are written specially, so they say that the implementation of functions is ''somewhere else''. The compiler understands that and generates a special code that says that this function call is going outside. When SourceMod loads your plugin, it inspects these bits of code and substitutes it's own internal functions instead. This is called [http://en.wikipedia.org/wiki/Dynamic_linking dynamic linking].&lt;br /&gt;
&lt;br /&gt;
=Setting up plugin info=&lt;br /&gt;
Now that we got access to SourceMod features, it is time to set up the information that will be displayed via &amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt; command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10768</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10768"/>
		<updated>2019-07-04T03:08:46Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|Introduction to SourceMod Plugins}}&lt;br /&gt;
This guide will give you a basic introduction to writing a [[SourceMod]] plugin.  If you are not familiar with the SourcePawn language, it is recommended that you at least briefly read the [[Introduction to SourcePawn]] article.&lt;br /&gt;
&lt;br /&gt;
For information on compiling plugins, see [[Compiling SourceMod Plugins]]. You can use [https://forums.alliedmods.net/showthread.php?t=259917 SPEdit], [https://www.crimsoneditor.com/ Crimson Editor], [http://www.pspad.com/ PSPad], [http://www.ultraedit.com/ UltraEdit], [https://notepad-plus-plus.org/ Notepad++], [https://www.textpad.com/ TextPad], [http://sourceforge.net/projects/pawnstudio/ Pawn Studio], [https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn] or any other text editor you're comfortable with to write plugins.&lt;br /&gt;
&lt;br /&gt;
=Starting from scratch=&lt;br /&gt;
Open your favorite text editor and create a new empty file. When you have an empty file you can just start writing code using the core language, however, you will not be able to use any of SourceMod features because the compiler does not know about them. This is done deliberately so it is possible to use SourcePawn outside of SourceMod. But since we are writing a SourceMod plugin, it is a good idea to enable access to SourceMod features first. This is done using &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; directive. It tells the compiler to &amp;quot;paste&amp;quot; the code from another file into yours.&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
How does this work? First of all, note that we enclosed file name into angle brackets. Angle brackets tell the compiler to look in the default include directory. By default, it is '''scripting/include'''. You can open it right now and see a lot of inc files there. Those are SourceMod include files that describe various functions, tags and other features available for SourceMod plugins. The files are plain-text and you are encouraged to read them. You will notice, however, that there's not much code in there, certainly not enough to implement all the great features of SourceMod, so where are they? They are implemented inside a SourceMod core which is written in C++ and is compiled into binary files which end up in '''bin''' directory. So how does your SourcePawn code and SM core link together if the compiler doesn't know about the existence of the latter? SourceMod include files are written specially, so they say that the implementation of functions is ''somewhere else''. The compiler understands that and generates a special code that says that this function call is going outside. When SourceMod loads your plugin, it inspects these bits of code and substitutes it's own internal functions instead. This is called [http://en.wikipedia.org/wiki/Dynamic_linking dynamic linking].&lt;br /&gt;
&lt;br /&gt;
=Setting up plugin info=&lt;br /&gt;
Now that we got access to SourceMod features, it is time to set up the information that will be displayed via &amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt; command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10767</id>
		<title>Introduction to SourceMod Plugins/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Introduction_to_SourceMod_Plugins/zh&amp;diff=10767"/>
		<updated>2019-07-04T03:07:13Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: Created page with &amp;quot;{{Languages|SQL (SourceMod_Scripting)}} This guide will give you a basic introduction to writing a SourceMod plugin.  If you are not familiar with the SourcePawn language,...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
This guide will give you a basic introduction to writing a [[SourceMod]] plugin.  If you are not familiar with the SourcePawn language, it is recommended that you at least briefly read the [[Introduction to SourcePawn]] article.&lt;br /&gt;
&lt;br /&gt;
For information on compiling plugins, see [[Compiling SourceMod Plugins]]. You can use [https://forums.alliedmods.net/showthread.php?t=259917 SPEdit], [https://www.crimsoneditor.com/ Crimson Editor], [http://www.pspad.com/ PSPad], [http://www.ultraedit.com/ UltraEdit], [https://notepad-plus-plus.org/ Notepad++], [https://www.textpad.com/ TextPad], [http://sourceforge.net/projects/pawnstudio/ Pawn Studio], [https://forums.alliedmods.net/showthread.php?t=289127 BasicPawn] or any other text editor you're comfortable with to write plugins.&lt;br /&gt;
&lt;br /&gt;
=Starting from scratch=&lt;br /&gt;
Open your favorite text editor and create a new empty file. When you have an empty file you can just start writing code using the core language, however, you will not be able to use any of SourceMod features because the compiler does not know about them. This is done deliberately so it is possible to use SourcePawn outside of SourceMod. But since we are writing a SourceMod plugin, it is a good idea to enable access to SourceMod features first. This is done using &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; directive. It tells the compiler to &amp;quot;paste&amp;quot; the code from another file into yours.&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
How does this work? First of all, note that we enclosed file name into angle brackets. Angle brackets tell the compiler to look in the default include directory. By default, it is '''scripting/include'''. You can open it right now and see a lot of inc files there. Those are SourceMod include files that describe various functions, tags and other features available for SourceMod plugins. The files are plain-text and you are encouraged to read them. You will notice, however, that there's not much code in there, certainly not enough to implement all the great features of SourceMod, so where are they? They are implemented inside a SourceMod core which is written in C++ and is compiled into binary files which end up in '''bin''' directory. So how does your SourcePawn code and SM core link together if the compiler doesn't know about the existence of the latter? SourceMod include files are written specially, so they say that the implementation of functions is ''somewhere else''. The compiler understands that and generates a special code that says that this function call is going outside. When SourceMod loads your plugin, it inspects these bits of code and substitutes it's own internal functions instead. This is called [http://en.wikipedia.org/wiki/Dynamic_linking dynamic linking].&lt;br /&gt;
&lt;br /&gt;
=Setting up plugin info=&lt;br /&gt;
Now that we got access to SourceMod features, it is time to set up the information that will be displayed via &amp;lt;tt&amp;gt;sm plugins list&amp;lt;/tt&amp;gt; command. No one likes unnamed plugins. To do that we are going to look inside '''sourcemod.inc''' file and see the format that information should be declared. It's always helpful to look inside SM include files to find out information you don't know. There is also an [http://docs.sourcemod.net/api/ API documentation] but it can be outdated and it only has SM core files so if your plugin is going to use any third party extension or another plugin, you will have to study inc files. So, open '''sourcemod.inc''' and scroll down a bit until you see this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Plugin public information.&lt;br /&gt;
 */&lt;br /&gt;
struct Plugin&lt;br /&gt;
{&lt;br /&gt;
   public const char[] name;		/**&amp;lt; Plugin Name */&lt;br /&gt;
   public const char[] description;	/**&amp;lt; Plugin Description */&lt;br /&gt;
   public const char[] author;		/**&amp;lt; Plugin Author */&lt;br /&gt;
   public const char[] version;		/**&amp;lt; Plugin Version */&lt;br /&gt;
   public const char[] url;			/**&amp;lt; Plugin URL */&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
and this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Declare this as a struct in your plugin to expose its information.&lt;br /&gt;
 * Example:&lt;br /&gt;
 *&lt;br /&gt;
 * public Plugin myinfo =&lt;br /&gt;
 * {&lt;br /&gt;
 *    name = &amp;quot;My Plugin&amp;quot;,&lt;br /&gt;
 *    //etc&lt;br /&gt;
 * };&lt;br /&gt;
 */&lt;br /&gt;
public Plugin myinfo;&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It tells us that we need to create a global public variable &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; which must be of type &amp;lt;tt&amp;gt;Plugin&amp;lt;/tt&amp;gt; which is a struct with 5 fields which themselves are strings. It may sound complicated for a beginner but it's easy. Let's go ahead and create one:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt; keyword means that SourceMod will be able to directly access our variable. &amp;lt;tt&amp;gt;Plugin:&amp;lt;/tt&amp;gt; defines a type of our variable. &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; is, obviously, a name of our variable as required by SourceMod. You see that we initialize it right away. This is the preferable way to fill out plugin info.&lt;br /&gt;
&lt;br /&gt;
After that the full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Getting code to run=&lt;br /&gt;
We already include SourceMod features and filled up or plugin info. We now have a perfectly well-formed plugin which can be compiled and loaded by SourceMod. However, there is one problem - it does nothing. You might be tempted to just start writing a code after &amp;lt;tt&amp;gt;myinfo&amp;lt;/tt&amp;gt; declaration just to see that it will not compile. SourcePawn, unlike other scripting languages like Lua, does not allow a code to be outside of functions. After reading that, you may probably want to just define some function, name it &amp;lt;tt&amp;gt;main&amp;lt;/tt&amp;gt; probably, compile and load a plugin and see that your code never gets called. So how do we make SourceMod call our code? For this exact reason, we have forwards. Forwards are function prototypes declared by one party that can be implemented by another party as a [http://en.wikipedia.org/wiki/Callback_%28computer_programming%29 callback]. When a first party starts a forward call, all parties that have matching callbacks receive the call. SourceMod declares a plenty of interesting forwards that we can implement. As you can see, forwards are the only way to get our code executed, keep that in mind. So let's implement &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt; forward. As you may have guessed, it is called when our plugin starts. To do that, we'll have to look up the declaration of &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;. It is declared inside '''sourcemod.inc''', a file we are already familiar with, let's find it:&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Called when the plugin is fully initialized and all known external references &lt;br /&gt;
 * are resolved. This is only called once in the lifetime of the plugin, and is &lt;br /&gt;
 * paired with OnPluginEnd().&lt;br /&gt;
 *&lt;br /&gt;
 * If any run-time error is thrown during this callback, the plugin will be marked &lt;br /&gt;
 * as failed.&lt;br /&gt;
 *&lt;br /&gt;
 * It is not necessary to close any handles or remove hooks in this function.  &lt;br /&gt;
 * SourceMod guarantees that plugin shutdown automatically and correctly releases &lt;br /&gt;
 * all resources.&lt;br /&gt;
 *&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
forward void OnPluginStart();&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Empty parentheses tell us that no arguments are passed inside this forward, &amp;lt;tt&amp;gt;@noreturn&amp;lt;/tt&amp;gt; inside documentation tells us that we don't have to return anything, pretty simple forward. So how to write a correct callback for it? Firstly, our callback must have the same name, so it's &amp;lt;tt&amp;gt;OnPluginStart&amp;lt;/tt&amp;gt;, secondly, our callback should have the same number of arguments, none in this case, and lastly, SourceMod needs to be able to call our callback so it needs to be &amp;lt;tt&amp;gt;public&amp;lt;/tt&amp;gt;. So the implementation looks like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can write code inside curly braces and it will be executed when our plugin starts. Let's output &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; to server console. To do that we are going to use &amp;lt;tt&amp;gt;PrintToServer&amp;lt;/tt&amp;gt; function. It is declared inside '''console.inc''', however, we don't need to manually include '''console.inc''' because it is included automatically as part of '''sourcemod.inc'''.&lt;br /&gt;
&amp;lt;pawn&amp;gt;/**&lt;br /&gt;
 * Sends a message to the server console.&lt;br /&gt;
 *&lt;br /&gt;
 * @param format		Formatting rules.&lt;br /&gt;
 * @param ...			Variable number of format parameters.&lt;br /&gt;
 * @noreturn&lt;br /&gt;
 */&lt;br /&gt;
native int PrintToServer(const char[] format, any ...);&amp;lt;/pawn&amp;gt;&lt;br /&gt;
As you can see, this is a native function. It is implemented inside SM core. Judging by it's arguments, we can see that it is a [[Format_Class_Functions_%28SourceMod_Scripting%29|format class function]]. However, we don't need any formatting right now, so let's just pass &amp;lt;tt&amp;gt;&amp;quot;Hello world!&amp;quot;&amp;lt;/tt&amp;gt; string as an only argument:&lt;br /&gt;
&amp;lt;pawn&amp;gt;public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
That's it! The full code of your plugin should look like this:&lt;br /&gt;
&amp;lt;pawn&amp;gt;#include &amp;lt;sourcemod&amp;gt;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Hello world!&amp;quot;);&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
Compile and load your plugin on your server and see for yourself that the message is displayed in the server console.&lt;br /&gt;
&lt;br /&gt;
=Includes=&lt;br /&gt;
Pawn requires '''include files''', much like C requires header files.  Include files list all of the structures, functions, callbacks, and tags that are available.  There are three types of include files:&lt;br /&gt;
*'''Core''' - &amp;lt;tt&amp;gt;sourcemod.inc&amp;lt;/tt&amp;gt; and anything it includes.  These are all provided by SourceMod's Core.&lt;br /&gt;
*'''Extension''' - adds a dependency against a certain extension.&lt;br /&gt;
*'''Plugin''' - adds a dependency against a certain plugin.&lt;br /&gt;
&lt;br /&gt;
Include files are loaded using the &amp;lt;tt&amp;gt;#include&amp;lt;/tt&amp;gt; compiler directive.&lt;br /&gt;
&lt;br /&gt;
=Commands=&lt;br /&gt;
Our first example will be writing a simple admin command to slap a player.  We'll continue to extend this example with more features until we have a final, complete result.&lt;br /&gt;
&lt;br /&gt;
==Declaration==&lt;br /&gt;
First, let's look at what an admin command requires.  Admin commands are registered using the [https://sm.alliedmods.net/new-api/console/RegAdminCmd RegAdminCmd] function.  They require a '''name''', a '''callback function''', and '''default admin flags'''.  &lt;br /&gt;
&lt;br /&gt;
The callback function is what's invoked every time the command is used.  [https://sm.alliedmods.net/new-api/console/ConCmd Click here] to see its prototype.  Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we've successfully implemented a command -- though it doesn't do anything yet.  In fact, it will say &amp;quot;Unknown command&amp;quot; if you use it! This is because you're not returning Plugin_Handled in your callback. Since you haven't,  SourceMod believes you didn't want the Source Engine to know the command was registered, and it handles it so. The reason SourceMod expects your function to return Plugin_Handled is because of the Action tag you put in your function's prototype. The Action tag specifies that Command_MySlap must return one of four things. See the [https://sm.alliedmods.net/new-api/core/Action Action] enumeration in the sourcemod API to learn more about these return types and when to use them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the command will report no error, but it still won't do anything. This is because returning &amp;quot;Plugin_Handled&amp;quot; in a command callback will prevent the engine from processing the command. The engine will never even see that the command was run. This is what you will want to do if you are registering a completely new command through SourceMod.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
Let's decide what the command will look like.  Let's have it act like the default &amp;lt;tt&amp;gt;sm_slap&amp;lt;/tt&amp;gt; command:&lt;br /&gt;
&amp;lt;pre&amp;gt;sm_myslap &amp;lt;name|#userid&amp;gt; [damage]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To implement this, we'll need a few steps:&lt;br /&gt;
*Get the input from the console.  For this we use [https://sm.alliedmods.net/new-api/console/GetCmdArg GetCmdArg()].&lt;br /&gt;
*Find a matching player.  For this we use [https://sm.alliedmods.net/new-api/helpers/FindTarget FindTarget()].&lt;br /&gt;
*Slap them.  For this we use [https://sm.alliedmods.net/new-api/sdktools_functions/SlapPlayer SlapPlayer()], which requires including &amp;lt;tt&amp;gt;sdktools&amp;lt;/tt&amp;gt;, an extension bundled with SourceMod.&lt;br /&gt;
*Respond to the admin.  For this we use [https://sm.alliedmods.net/new-api/console/ReplyToCommand ReplyToCommand()].&lt;br /&gt;
&lt;br /&gt;
Full example:&lt;br /&gt;
&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;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	&lt;br /&gt;
	/* By default, we set damage = 0 */&lt;br /&gt;
	int damage = 0;&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, we set damage to&lt;br /&gt;
	 * what the user specified. If a damage isn't specified&lt;br /&gt;
	 * then it will stay zero. */&lt;br /&gt;
	if (args &amp;gt;= 2)&lt;br /&gt;
	{&lt;br /&gt;
		GetCmdArg(2, arg2, sizeof(arg2));&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* Try and find a matching player */&lt;br /&gt;
	int target = FindTarget(client, arg1);&lt;br /&gt;
	if (target == -1)&lt;br /&gt;
	{&lt;br /&gt;
		/* FindTarget() automatically replies with the &lt;br /&gt;
		 * failure reason and returns -1 so we know not &lt;br /&gt;
		 * to continue&lt;br /&gt;
		 */&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
	ReplyToCommand(client, &amp;quot;[SM] You slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more information on what %s and %d are, see [[Format Class Functions (SourceMod Scripting)|Format Class Functions]].  Note that you never need to unregister or remove your admin command.  When a plugin is unloaded, SourceMod cleans it up for you.&lt;br /&gt;
&lt;br /&gt;
=ConVars=&lt;br /&gt;
ConVars, also known as cvars, are global console variables in the Source engine.  They can have integer, float, or string values.  ConVar accessing is done through [[Handles (SourceMod Scripting)|Handles]].  Since ConVars are global, you do not need to close ConVar Handles (in fact, you cannot).&lt;br /&gt;
&lt;br /&gt;
The handy feature of ConVars is that they are easy for users to configure.  They can be placed in any .cfg file, such as &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;.  To make this easier, SourceMod has an [https://sm.alliedmods.net/new-api/sourcemod/AutoExecConfig AutoExecConfig()] function.  This function will automatically build a default .cfg file containing all of your cvars, annotated with comments, for users.  It is highly recommended that you call this if you have customizable ConVars.&lt;br /&gt;
&lt;br /&gt;
Let's extend your example from earlier with a new ConVar.  Our ConVar will be &amp;lt;tt&amp;gt;sm_myslap_damage&amp;lt;/tt&amp;gt; and will specify the default damage someone is slapped for if no damage is specified.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pawn&amp;gt;ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* The rest remains unchanged! */&lt;br /&gt;
&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Showing Activity, Logging=&lt;br /&gt;
Almost all admin commands should log their activity, and some admin commands should show their activity to in-game clients.  This can be done via the [https://sm.alliedmods.net/new-api/logging/LogAction LogAction()] and [https://sm.alliedmods.net/new-api/console/ShowActivity2 ShowActivity2()] functions.  The exact functionality of ShowActivity2() is determined by the &amp;lt;tt&amp;gt;sm_show_activity&amp;lt;/tt&amp;gt; cvar.&lt;br /&gt;
&lt;br /&gt;
For example, let's rewrite the last few lines of our slap command:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
	SlapPlayer(target, damage);&lt;br /&gt;
&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&lt;br /&gt;
	&lt;br /&gt;
	GetClientName(target, name, sizeof(name));&lt;br /&gt;
&lt;br /&gt;
	ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, name, damage);&lt;br /&gt;
	LogAction(client, target, &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target, damage);&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;
=Multiple Targets=&lt;br /&gt;
To fully complete our slap demonstration, let's make it support multiple targets.  SourceMod's [[Admin_Commands_%28SourceMod%29#How_to_Target|targeting system]] is quite advanced, so using it may seem complicated at first.  &lt;br /&gt;
&lt;br /&gt;
The function we use is [https://sm.alliedmods.net/new-api/commandfilters/ProcessTargetString ProcessTargetString()].  It takes in input from the console and returns a list of matching clients.  It also returns a noun that will identify either a single client or describe a list of clients.  The idea is that each client is then processed, but the activity shown to all players is only processed once.  This reduces screen spam.&lt;br /&gt;
&lt;br /&gt;
This method of target processing is used for almost every admin command in SourceMod, and in fact, FindTarget() is just a simplified version.&lt;br /&gt;
&lt;br /&gt;
Full, final 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;
ConVar sm_myslap_damage = null;&lt;br /&gt;
&lt;br /&gt;
public Plugin myinfo =&lt;br /&gt;
{&lt;br /&gt;
	name = &amp;quot;My First Plugin&amp;quot;,&lt;br /&gt;
	author = &amp;quot;Me&amp;quot;,&lt;br /&gt;
	description = &amp;quot;My first plugin ever&amp;quot;,&lt;br /&gt;
	version = &amp;quot;1.0.0.0&amp;quot;,&lt;br /&gt;
	url = &amp;quot;http://www.sourcemod.net/&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
	LoadTranslations(&amp;quot;common.phrases&amp;quot;);&lt;br /&gt;
	RegAdminCmd(&amp;quot;sm_myslap&amp;quot;, Command_MySlap, ADMFLAG_SLAY);&lt;br /&gt;
&lt;br /&gt;
	sm_myslap_damage = CreateConVar(&amp;quot;sm_myslap_damage&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;Default slap damage&amp;quot;);&lt;br /&gt;
	AutoExecConfig(true, &amp;quot;plugin_myslap&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public Action Command_MySlap(int client, int args)&lt;br /&gt;
{&lt;br /&gt;
	char arg1[32], arg2[32];&lt;br /&gt;
	int damage = GetConVarInt(sm_myslap_damage);&lt;br /&gt;
&lt;br /&gt;
	/* Get the first argument */&lt;br /&gt;
	GetCmdArg(1, arg1, sizeof(arg1));&lt;br /&gt;
&lt;br /&gt;
	/* If there are 2 or more arguments, and the second argument fetch &lt;br /&gt;
	 * is successful, convert it to an integer.&lt;br /&gt;
	 */&lt;br /&gt;
	if (args &amp;gt;= 2 &amp;amp;&amp;amp; GetCmdArg(2, arg2, sizeof(arg2)))&lt;br /&gt;
	{&lt;br /&gt;
		damage = StringToInt(arg2);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/**&lt;br /&gt;
	 * target_name - stores the noun identifying the target(s)&lt;br /&gt;
	 * target_list - array to store clients&lt;br /&gt;
	 * target_count - variable to store number of clients&lt;br /&gt;
	 * tn_is_ml - stores whether the noun must be translated&lt;br /&gt;
	 */&lt;br /&gt;
	char target_name[MAX_TARGET_LENGTH];&lt;br /&gt;
	int target_list[MAXPLAYERS], target_count;&lt;br /&gt;
	bool tn_is_ml;&lt;br /&gt;
&lt;br /&gt;
	if ((target_count = ProcessTargetString(&lt;br /&gt;
			arg1,&lt;br /&gt;
			client,&lt;br /&gt;
			target_list,&lt;br /&gt;
			MAXPLAYERS,&lt;br /&gt;
			COMMAND_FILTER_ALIVE, /* Only allow alive players */&lt;br /&gt;
			target_name,&lt;br /&gt;
			sizeof(target_name),&lt;br /&gt;
			tn_is_ml)) &amp;lt;= 0)&lt;br /&gt;
	{&lt;br /&gt;
		/* This function replies to the admin with a failure message */&lt;br /&gt;
		ReplyToTargetError(client, target_count);&lt;br /&gt;
		return Plugin_Handled;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	for (int i = 0; i &amp;lt; target_count; i++)&lt;br /&gt;
	{&lt;br /&gt;
		SlapPlayer(target_list[i], damage);&lt;br /&gt;
		LogAction(client, target_list[i], &amp;quot;\&amp;quot;%L\&amp;quot; slapped \&amp;quot;%L\&amp;quot; (damage %d)&amp;quot;, client, target_list[i], damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (tn_is_ml)&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %t for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		ShowActivity2(client, &amp;quot;[SM] &amp;quot;, &amp;quot;Slapped %s for %d damage!&amp;quot;, target_name, damage);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return Plugin_Handled;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Events=&lt;br /&gt;
Events are informational notification messages passed between objects in the server.  Many are also passed from the server to the client.  They are defined in .res files under the &amp;lt;tt&amp;gt;hl2/resource&amp;lt;/tt&amp;gt; folder and &amp;lt;tt&amp;gt;resource&amp;lt;/tt&amp;gt; folders of specific mods.  For a basic listing, see [[Game Events (Source)|Source Game Events]].&lt;br /&gt;
&lt;br /&gt;
It is important to note a few concepts about events:&lt;br /&gt;
*They are almost always informational.  That is, blocking &amp;lt;tt&amp;gt;player_death&amp;lt;/tt&amp;gt; will not stop a player from dying.  It may block a HUD or console message or something else minor.&lt;br /&gt;
*They almost always use userids instead of client indexes.&lt;br /&gt;
*Just because it is in a resource file does not mean it is ever called, or works the way you expect it to.  Mods are notorious for not properly documenting their event functionality.&lt;br /&gt;
&lt;br /&gt;
An example of finding when a player dies:&lt;br /&gt;
&amp;lt;pawn&amp;gt;&lt;br /&gt;
public void OnPluginStart()&lt;br /&gt;
{&lt;br /&gt;
   HookEvent(&amp;quot;player_death&amp;quot;, Event_PlayerDeath);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)&lt;br /&gt;
{&lt;br /&gt;
   int victim_id = event.GetInt(&amp;quot;userid&amp;quot;);&lt;br /&gt;
   int attacker_id = event.GetInt(&amp;quot;attacker&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   int victim = GetClientOfUserId(victim_id);&lt;br /&gt;
   int attacker = GetClientOfUserId(attacker_id);&lt;br /&gt;
&lt;br /&gt;
   /* CODE */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Callback Orders and Pairing=&lt;br /&gt;
SourceMod has a number of builtin callbacks about the state of the server and plugin.  Some of these are paired in special ways which can confuse users.&lt;br /&gt;
&lt;br /&gt;
==Pairing==&lt;br /&gt;
'''Pairing''' is SourceMod terminology.  Examples of it are:&lt;br /&gt;
*OnMapEnd() cannot be called without an OnMapStart(), and if OnMapStart() is called, it cannot be called again without an OnMapEnd().&lt;br /&gt;
*OnClientConnected(N) for a given client N will only be called once until an OnClientDisconnected(N) for the same client N is called (which is guaranteed to happen).&lt;br /&gt;
&lt;br /&gt;
There is a formal definition of SourceMod's pairing.  For two functions X and Y, both with input A, the following conditions hold:&lt;br /&gt;
*If X is invoked with input A, it cannot be invoked again with the same input unless Y is called with input A.&lt;br /&gt;
*If X is invoked with input A, it is guaranteed that Y will, at some point, be called with input A.&lt;br /&gt;
*Y cannot be invoked with any input A unless X was called first with input A.&lt;br /&gt;
*The relationship is described as, &amp;quot;X is paired with Y,&amp;quot; and &amp;quot;Y is paired to X.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==General Callbacks==&lt;br /&gt;
These callbacks are listed in the order they are called, in the lifetime of a plugin and the server.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/AskPluginLoad2 AskPluginLoad2()] - Called once, immediately after the plugin is loaded from the disk.  This function can be used to stop a plugin from loading and return a custom error message; return APLRes_Failure and use strcopy on to replace the error string.  All CreateNative and RegPluginLibrary calls should be done here.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginStart OnPluginStart()] - Called once, after the plugin has been fully initialized and can proceed to load.  Any run-time errors in this function will cause the plugin to fail to load.  '''This is paired with OnPluginEnd()'''.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnAllPluginsLoaded OnAllPluginsLoaded()] - Called once, after all non-late loaded plugins have called OnPluginStart.  &lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapStart OnMapStart()] - Called every time the map loads.  If the plugin is loaded late, and the map has already started, this function is called anyway after load, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnConfigsExecuted OnConfigsExecuted()] - Called once per map-change after  &amp;lt;tt&amp;gt;servercfgfile&amp;lt;/tt&amp;gt; (usually &amp;lt;tt&amp;gt;server.cfg&amp;lt;/tt&amp;gt;), &amp;lt;tt&amp;gt;sourcemod.cfg&amp;lt;/tt&amp;gt;, and all plugin config files have finished executing.  If a plugin is loaded after this has happened, the callback is called anyway, in order to preserve pairing.  '''This function is paired with OnMapEnd().'''&lt;br /&gt;
*At this point, most game callbacks can occur, such as events and callbacks involving clients (or other things, like OnGameFrame).&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnMapEnd OnMapEnd()] - Called when the map is about to end.  At this point, all clients are disconnected, but &amp;lt;tt&amp;gt;TIMER_NO_MAPCHANGE&amp;lt;/tt&amp;gt; timers are not yet destroyed.  '''This function is paired to OnMapStart().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/sourcemod/OnPluginEnd OnPluginEnd()] - Called once, immediately before the plugin is unloaded.  '''This function is paired to OnPluginStart().'''&lt;br /&gt;
&lt;br /&gt;
==Client Callbacks==&lt;br /&gt;
These callbacks are listed in no specific order, however, their documentation holds for both fake and real clients.&lt;br /&gt;
&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnect OnClientConnect()] - Called when a player initiates a connection.  You can block a player from connecting by returning Plugin_Stop and setting rejectmsg to an error message.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientConnected OnClientConnected()] - Called after a player connects. Signifies that the player is in-game and IsClientConnected() will return true. '''This is paired with OnClientDisconnect() for successful connections only.'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientAuthorized OnClientAuthorized()] - Called when a player gets a Steam ID.  It is important to note that this may never be called.  It may occur any time in between OnClientConnected and OnClientPreAdminCheck/OnClientDisconnect.  Do not rely on it unless you are writing something that needs Steam IDs, and even then you should use OnClientPostAdminCheck().&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPutInServer OnClientPutInServer()] - Signifies that the player is in-game and IsClientInGame() will return true.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientPostAdminCheck OnClientPostAdminCheck()] - Called after the player is '''both authorized and in-game'''.  This is the best callback for checking administrative access after connect.&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect OnClientDisconnect()] - Called when a player's disconnection starts.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
*[https://sm.alliedmods.net/new-api/clients/OnClientDisconnect_Post OnClientDisconnect_Post()] - Called when a player's disconnection ends.  '''This is paired to OnClientConnected().'''&lt;br /&gt;
&lt;br /&gt;
=Frequently Asked Questions=&lt;br /&gt;
==Are plugins reloaded every mapchange?==&lt;br /&gt;
Plugins, by default, are not reloaded on mapchange unless their timestamp changes.  This is a feature so plugin authors have more flexibility with the state of their plugins.  &lt;br /&gt;
&lt;br /&gt;
==Do I need to call CloseHandle in OnPluginEnd?==&lt;br /&gt;
No.  SourceMod automatically closes your Handles when your plugin is unloaded, in order to prevent memory errors.&lt;br /&gt;
&lt;br /&gt;
==Do I need to #include every individual .inc?==&lt;br /&gt;
No.  &amp;lt;tt&amp;gt;#include &amp;lt;sourcemod&amp;gt;&amp;lt;/tt&amp;gt; will give you 95% of the .incs.  Similarly, &amp;lt;tt&amp;gt;#include &amp;lt;sdktools&amp;gt;&amp;lt;/tt&amp;gt; includes everything starting with &amp;lt;sdktools&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Why don't some events fire?==&lt;br /&gt;
There is no guarantee that events will fire.  The event listing is not a specification, it is a list of the events that a game is capable of firing.  Whether the game actually fires them is up to Valve or the developer.&lt;br /&gt;
&lt;br /&gt;
==Do I need to CloseHandle timers?==&lt;br /&gt;
No.  In fact, doing so may cause errors.  Timers naturally die on their own unless they are infinite timers, in which case you can use KillTimer() or die gracefully by returning &amp;lt;tt&amp;gt;Plugin_Stop&amp;lt;/tt&amp;gt; in the callback.&lt;br /&gt;
&lt;br /&gt;
==Are clients disconnected on mapchange?==&lt;br /&gt;
All clients are fully disconnected before the map changes.  They are all reconnected after the next map starts.&lt;br /&gt;
&lt;br /&gt;
If you only want to detect when a client initially connects or leaves your server, hook the [[Generic Source Server Events#player_connect|player_connect]] or [[Generic Source Server Events#player_disconnect|player_disconnect]] events respectively.&lt;br /&gt;
&lt;br /&gt;
==Why am I getting &amp;quot;function prototypes do not match&amp;quot; errors?==&lt;br /&gt;
When you see this error, you'll most likely find that the issue comes from any callback functions referenced in the line(s) that are causing the error.&lt;br /&gt;
&lt;br /&gt;
When you call a function that takes another function as a callback, the callback function must be declared with the correct number of parameter and return types.&lt;br /&gt;
&lt;br /&gt;
For example, [https://sm.alliedmods.net/new-api/console/RegConsoleCmd &amp;lt;tt&amp;gt;RegConsoleCommand&amp;lt;/tt&amp;gt;] must be called with a callback that has with the exact arguments and return type as specified by the [https://sm.alliedmods.net/new-api/console/ConCmd &amp;lt;tt&amp;gt;ConCmd&amp;lt;/tt&amp;gt;] definition.&lt;br /&gt;
&lt;br /&gt;
=Further Reading=&lt;br /&gt;
For further reading, see the &amp;quot;Scripting&amp;quot; section at the [http://docs.sourcemod.net/ SourceMod Documentation], as well as [https://wiki.alliedmods.net/Scripting_FAQ_(SourceMod) Yak's FAQs on Scripting].&lt;br /&gt;
&lt;br /&gt;
[[Category:SourceMod Scripting]]&lt;br /&gt;
&lt;br /&gt;
{{LanguageSwitch}}&lt;/div&gt;</summary>
		<author><name>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10766</id>
		<title>SQL (SourceMod Scripting)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10766"/>
		<updated>2019-07-03T09:15:56Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 查询 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。&lt;br /&gt;
&lt;br /&gt;
SourceMod的SQL层的正式称呼是''DBI''，全写是'''D'''ata'''b'''ase '''I'''nterface，中文称呼为数据库接口，这个接口是通用SQL方法的一般抽象。要连接到特定数据库（譬如MySQL和sqLite），则必须加载对应的SourceMod DBI驱动。当前SourceMod已内置有MySQL和SQLite的驱动。&lt;br /&gt;
&lt;br /&gt;
SourceMod会自动按需（当然前提是有）检测并加载驱动。所有的数据库相关内置函数可以在&amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;中找到，对应的C++ API则在&amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;中。&lt;br /&gt;
&lt;br /&gt;
=连接数据库=&lt;br /&gt;
当前共有两种方式连接数据库。第一种是通过命名配置文件，命名配置是在&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;中列出的预设配置。如果使用了数据库，那么SourceMod要求你至少提供一个名为&amp;quot;default&amp;quot;的配置。&lt;br /&gt;
&lt;br /&gt;
下面是配置SQL的一个例子：&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;
请使用&amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt;或&amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;来实例化基于命名配置的数据库连接。&lt;br /&gt;
&lt;br /&gt;
另一种方式是使用&amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt;，并通过传递包含这些参数的键值对句柄对象手动指定所有连接参数。&lt;br /&gt;
&lt;br /&gt;
下面是一个典型的连接数据库的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete db;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=查询语句=&lt;br /&gt;
&lt;br /&gt;
==无返回结果类==&lt;br /&gt;
最简单的查询就是那些不返回结果的查询了，例如，CREATE，DROP，UPDATE，INSERT和DELETE。对于这些查询语句，推荐使用&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;。它的名字里带快，并不意味着它执行起来更快，实际上，它只是让我们在编写代码上更快。下面是使用例子，代码中给定的&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;是一个有效的数据库句柄：&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;
	char 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;
==有返回结果类==&lt;br /&gt;
如果一个查询返回了结果集，并且必须处理它，那么你就必须使用&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;了。与&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;不同的是，这个函数会返回一个必须关闭的句柄。&lt;br /&gt;
&lt;br /&gt;
一个会返回结果的查询例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* 在这里进行结果的处理！&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* 释放句柄对象 */&lt;br /&gt;
	delete query;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=预编译语句=&lt;br /&gt;
预编译语句是另一种查询的方法。预编译语句背后的实质是，你构造一个查询的“模板”，在这之后可以任意次地复用。预编译语句有以下的优势：&lt;br /&gt;
*如果使用预编译语句，那么数据库就可以更好地缓存查询&lt;br /&gt;
*你不需要每次使用时重新构造查询语句&lt;br /&gt;
*你不需要每次使用时分配新的查询结构体&lt;br /&gt;
*输入总是安全的（下面会讲到）&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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* 创建足够的空间来保证我们的查询字符串能被正确引用  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[buffer_len];&lt;br /&gt;
&lt;br /&gt;
	/* 要求SQL驱动程序确保我们的字符串被安全引用 */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len);&lt;br /&gt;
&lt;br /&gt;
	/* 生成查询语句串 */&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;
	/* 执行查询语句 */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == null)&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* 获取一些信息&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	delete hQuery;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
让我们来看看使用预编译语句的版本：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* 检查一下我们是不是已经创建好了语句 */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;
	 * 在这处理信息&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
重要区别：&lt;br /&gt;
*输入字符串（&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;）不需要被反引号（引号）包起来。数据库引擎会自动处理类型安全和插入检查。&lt;br /&gt;
*参数标记&amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;周围不需要引号，即使它接受了一个字符串。&lt;br /&gt;
*我们仅仅只需要创建语句句柄一次，在那之后，于该数据库连接的整个生命周期它都将存在。&lt;br /&gt;
&lt;br /&gt;
=处理查询结果=&lt;br /&gt;
对于普通查询和预编译语句查询，它们处理结果的方式相同。 相关的重要函数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - 返回查询结果的行数。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - 拉取下一行，如果有的话。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - 从当前行拉取特定字段的数据。&lt;br /&gt;
&lt;br /&gt;
我们假设有如下结构的一个简单表：（译注：这是一段建表语句）&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;
下面的示例包含的代码将打印出与某个年龄段匹配的所有用户。常规查询和预编译语句各有一个例子。&lt;br /&gt;
&amp;lt;pawn&amp;gt;void PrintResults(Handle query)&lt;br /&gt;
{&lt;br /&gt;
	/* 即便结果只有一行，你也应该先使用SQL_FetchRow() */&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
注意，这些示例中都没有关闭预编译语句句柄。因为这些示例假定有一个全局的数据库实例，只有在卸载插件时才关闭它。对于那些自己维护临时数据库连接的插件，就必须释放预编译语句句柄，否则数据库连接将永远不会关闭。&lt;br /&gt;
&lt;br /&gt;
=多线程=&lt;br /&gt;
&amp;lt;b&amp;gt;译注：下面的内容比较复杂，请选择性观看。&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SourceMod支持多线程SQL查询。这意味着，数据库操作可以在游戏主线程之外的线程中完成。如果你使用远程的数据库服务器或者需要一个网络连接，查询可能会导致明显延迟，所以如果你的查询发生在游戏进行期间，那么支持线程通常是一个好主意。&lt;br /&gt;
&lt;br /&gt;
多线程查询是''异步的''。这意味着，他们会在回调函数里触发并返回结果。尽管回调函数最终一定能触发，但是你并不能让它在指定时刻触发。某些驱动可能不支持多线程，如果是这种情况，一个运行时错误会被抛出。如果线程器不能被启动或者被禁用，SourceMod会以回调函数的形式在主线程里执行查询。&lt;br /&gt;
&lt;br /&gt;
==操作==&lt;br /&gt;
所有被线程化的操作（除了数据库连接）都使用一样的回调&amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;来接受结果，参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - 数据库句柄对象的拷贝。如果db句柄找不到或者是无效的，会把 &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; 传过去。&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - 结果对象，失败时为null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - 包含错误内容的字符串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - 通过SQL操作传递的自定义数据。&lt;br /&gt;
&lt;br /&gt;
多线程支持下面的操作：&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库连接&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库查询&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*''注意: 预编译语句目前不支持多线程。''&lt;br /&gt;
&lt;br /&gt;
数据库操作的连续调用是安全的。&lt;br /&gt;
&lt;br /&gt;
===连接===&lt;br /&gt;
没必要为了线程化的查询而使用线程化的数据据连接。但是，使用线程化的数据库连接就不会因为建立连接时的延时造成服务器卡顿。&lt;br /&gt;
线程化的数据库连接使用这个回调： &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
回调的参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: 数据库连接的句柄对象，如果无法连接，将会返回&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 未使用&lt;br /&gt;
&lt;br /&gt;
例子: &lt;br /&gt;
&amp;lt;pawn&amp;gt;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===查询===&lt;br /&gt;
只要驱动支持，那么线程化的查询可以用任何的数据库句柄来执行。在线程中，第一个结果集的所有查询结果都会被检索。如果你的查询会返回不止一个结果集（比如，在MySQL中用&amp;lt;tt&amp;gt;CALL&amp;lt;/tt&amp;gt;调用特定函数），那么这个时候线程器的行为是未定义的。&amp;lt;b&amp;gt;请注意，如果你想在同一个连接上同时执行线程和非线程化的查询，你应当先阅读下面有关“线程锁”的小节&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
查询操作使用如下的回调参数：&lt;br /&gt;
*&amp;lt;tt&amp;gt;owner&amp;lt;/tt&amp;gt;: 一个指向进行查询的数据库的对象。这个对象与原本传入的对象不同，但是，使用&amp;lt;tt&amp;gt;SQL_IsSameConnection&amp;lt;/tt&amp;gt;方法对二者进行比较将会返回真值。这个对象可以被复制，但是不能被手动关闭（它将会自动关闭）。在出现严重错误时（例如，驱动被卸载），它将会是null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;hndl&amp;lt;/tt&amp;gt;: 一个指向查询的对象，它可以被复制，但是不能被手动关闭（将会被自动关闭）。如果有错误，这个对象将会是&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有、&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 通过&amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;tt&amp;gt;传入的、可选的、用户自定义数据。&lt;br /&gt;
&lt;br /&gt;
一个接着上面代码的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&lt;br /&gt;
&lt;br /&gt;
	/* 确保玩家不会在线程运行的时候断开连接 */&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 (results == null)&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 (results.RowCount == 0) {&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;
==锁==&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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;
===警告===&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;
==优先级==&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;
==简介==&lt;br /&gt;
[http://www.sqlite.org/ SQLite]是一个基于本地文件的数据库引擎。SourceMod内置了对应DBI。SQLite和MySql是不同的数据库，所以有些Mysql的查询语句在SQLite中将不会生效。在配置文件中它对应的数据库类型填&amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
==使用==&lt;br /&gt;
大部分的连接参数是可以忽略的，因为SQLite是本地数据库。唯一需要的连接参数是&amp;lt;tt&amp;gt;database&amp;lt;/tt&amp;gt;，这个指定了对应数据库的文件名称。如果数据库不存在，对应数据库会按需创建，相应文件存储在&amp;lt;tt&amp;gt;addons/sourcemod/data/sqlite&amp;lt;/tt&amp;gt;目录下，并自动设置文件扩展名为“.sql3”。&lt;br /&gt;
&lt;br /&gt;
另外，你可以通过数据库名称来指定子文件夹。例如，“cssdm/player”对应的文件会是&amp;lt;tt&amp;gt;addons/sourcemod/data/sqlite/cssdm/players.sql3&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
SQLite支持线程层，并且和MySQL遵循同样的规则。（包括锁和共享连接）&lt;br /&gt;
&lt;br /&gt;
==外部链接==&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>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10765</id>
		<title>SQL (SourceMod Scripting)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10765"/>
		<updated>2019-07-03T09:02:18Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 使用 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。&lt;br /&gt;
&lt;br /&gt;
SourceMod的SQL层的正式称呼是''DBI''，全写是'''D'''ata'''b'''ase '''I'''nterface，中文称呼为数据库接口，这个接口是通用SQL方法的一般抽象。要连接到特定数据库（譬如MySQL和sqLite），则必须加载对应的SourceMod DBI驱动。当前SourceMod已内置有MySQL和SQLite的驱动。&lt;br /&gt;
&lt;br /&gt;
SourceMod会自动按需（当然前提是有）检测并加载驱动。所有的数据库相关内置函数可以在&amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;中找到，对应的C++ API则在&amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;中。&lt;br /&gt;
&lt;br /&gt;
=连接数据库=&lt;br /&gt;
当前共有两种方式连接数据库。第一种是通过命名配置文件，命名配置是在&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;中列出的预设配置。如果使用了数据库，那么SourceMod要求你至少提供一个名为&amp;quot;default&amp;quot;的配置。&lt;br /&gt;
&lt;br /&gt;
下面是配置SQL的一个例子：&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;
请使用&amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt;或&amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;来实例化基于命名配置的数据库连接。&lt;br /&gt;
&lt;br /&gt;
另一种方式是使用&amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt;，并通过传递包含这些参数的键值对句柄对象手动指定所有连接参数。&lt;br /&gt;
&lt;br /&gt;
下面是一个典型的连接数据库的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete db;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=查询语句=&lt;br /&gt;
&lt;br /&gt;
==无返回结果类==&lt;br /&gt;
最简单的查询就是那些不返回结果的查询了，例如，CREATE，DROP，UPDATE，INSERT和DELETE。对于这些查询语句，推荐使用&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;。它的名字里带快，并不意味着它执行起来更快，实际上，它只是让我们在编写代码上更快。下面是使用例子，代码中给定的&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;是一个有效的数据库句柄：&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;
	char 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;
==有返回结果类==&lt;br /&gt;
如果一个查询返回了结果集，并且必须处理它，那么你就必须使用&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;了。与&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;不同的是，这个函数会返回一个必须关闭的句柄。&lt;br /&gt;
&lt;br /&gt;
一个会返回结果的查询例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* 在这里进行结果的处理！&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* 释放句柄对象 */&lt;br /&gt;
	delete query;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=预编译语句=&lt;br /&gt;
预编译语句是另一种查询的方法。预编译语句背后的实质是，你构造一个查询的“模板”，在这之后可以任意次地复用。预编译语句有以下的优势：&lt;br /&gt;
*如果使用预编译语句，那么数据库就可以更好地缓存查询&lt;br /&gt;
*你不需要每次使用时重新构造查询语句&lt;br /&gt;
*你不需要每次使用时分配新的查询结构体&lt;br /&gt;
*输入总是安全的（下面会讲到）&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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* 创建足够的空间来保证我们的查询字符串能被正确引用  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[buffer_len];&lt;br /&gt;
&lt;br /&gt;
	/* 要求SQL驱动程序确保我们的字符串被安全引用 */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len);&lt;br /&gt;
&lt;br /&gt;
	/* 生成查询语句串 */&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;
	/* 执行查询语句 */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == null)&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* 获取一些信息&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	delete hQuery;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
让我们来看看使用预编译语句的版本：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* 检查一下我们是不是已经创建好了语句 */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;
	 * 在这处理信息&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
重要区别：&lt;br /&gt;
*输入字符串（&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;）不需要被反引号（引号）包起来。数据库引擎会自动处理类型安全和插入检查。&lt;br /&gt;
*参数标记&amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;周围不需要引号，即使它接受了一个字符串。&lt;br /&gt;
*我们仅仅只需要创建语句句柄一次，在那之后，于该数据库连接的整个生命周期它都将存在。&lt;br /&gt;
&lt;br /&gt;
=处理查询结果=&lt;br /&gt;
对于普通查询和预编译语句查询，它们处理结果的方式相同。 相关的重要函数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - 返回查询结果的行数。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - 拉取下一行，如果有的话。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - 从当前行拉取特定字段的数据。&lt;br /&gt;
&lt;br /&gt;
我们假设有如下结构的一个简单表：（译注：这是一段建表语句）&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;
下面的示例包含的代码将打印出与某个年龄段匹配的所有用户。常规查询和预编译语句各有一个例子。&lt;br /&gt;
&amp;lt;pawn&amp;gt;void PrintResults(Handle query)&lt;br /&gt;
{&lt;br /&gt;
	/* 即便结果只有一行，你也应该先使用SQL_FetchRow() */&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
注意，这些示例中都没有关闭预编译语句句柄。因为这些示例假定有一个全局的数据库实例，只有在卸载插件时才关闭它。对于那些自己维护临时数据库连接的插件，就必须释放预编译语句句柄，否则数据库连接将永远不会关闭。&lt;br /&gt;
&lt;br /&gt;
=多线程=&lt;br /&gt;
&amp;lt;b&amp;gt;译注：下面的内容比较复杂，请选择性观看。&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SourceMod支持多线程SQL查询。这意味着，数据库操作可以在游戏主线程之外的线程中完成。如果你使用远程的数据库服务器或者需要一个网络连接，查询可能会导致明显延迟，所以如果你的查询发生在游戏进行期间，那么支持线程通常是一个好主意。&lt;br /&gt;
&lt;br /&gt;
多线程查询是''异步的''。这意味着，他们会在回调函数里触发并返回结果。尽管回调函数最终一定能触发，但是你并不能让它在指定时刻触发。某些驱动可能不支持多线程，如果是这种情况，一个运行时错误会被抛出。如果线程器不能被启动或者被禁用，SourceMod会以回调函数的形式在主线程里执行查询。&lt;br /&gt;
&lt;br /&gt;
==操作==&lt;br /&gt;
所有被线程化的操作（除了数据库连接）都使用一样的回调&amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;来接受结果，参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - 数据库句柄对象的拷贝。如果db句柄找不到或者是无效的，会把 &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; 传过去。&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - 结果对象，失败时为null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - 包含错误内容的字符串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - 通过SQL操作传递的自定义数据。&lt;br /&gt;
&lt;br /&gt;
多线程支持下面的操作：&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库连接&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库查询&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*''注意: 预编译语句目前不支持多线程。''&lt;br /&gt;
&lt;br /&gt;
数据库操作的连续调用是安全的。&lt;br /&gt;
&lt;br /&gt;
===连接===&lt;br /&gt;
没必要为了线程化的查询而使用线程化的数据据连接。但是，使用线程化的数据库连接就不会因为建立连接时的延时造成服务器卡顿。&lt;br /&gt;
线程化的数据库连接使用这个回调： &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
回调的参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: 数据库连接的句柄对象，如果无法连接，将会返回&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 未使用&lt;br /&gt;
&lt;br /&gt;
例子: &lt;br /&gt;
&amp;lt;pawn&amp;gt;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===查询===&lt;br /&gt;
只要驱动支持，那么线程化的查询可以用任何的数据库句柄来执行。在线程中，第一个结果集的所有查询结果都会被检索。如果你的查询会返回不止一个结果集（比如，在MySQL中用&amp;lt;tt&amp;gt;CALL&amp;lt;/tt&amp;gt;调用特定函数），那么这个时候线程器的行为是未定义的。&amp;lt;b&amp;gt;请注意，如果你想在同一个连接上同时执行线程和非线程化的查询，你应当先阅读下面有关“线程锁”的小节&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
查询操作使用如下的回调参数：&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;null&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;null&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;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&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 (results == null)&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 (results.RowCount == 0) {&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;
==锁==&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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;
===警告===&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;
==优先级==&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;
==简介==&lt;br /&gt;
[http://www.sqlite.org/ SQLite]是一个基于本地文件的数据库引擎。SourceMod内置了对应DBI。SQLite和MySql是不同的数据库，所以有些Mysql的查询语句在SQLite中将不会生效。在配置文件中它对应的数据库类型填&amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
==使用==&lt;br /&gt;
大部分的连接参数是可以忽略的，因为SQLite是本地数据库。唯一需要的连接参数是&amp;lt;tt&amp;gt;database&amp;lt;/tt&amp;gt;，这个指定了对应数据库的文件名称。如果数据库不存在，对应数据库会按需创建，相应文件存储在&amp;lt;tt&amp;gt;addons/sourcemod/data/sqlite&amp;lt;/tt&amp;gt;目录下，并自动设置文件扩展名为“.sql3”。&lt;br /&gt;
&lt;br /&gt;
另外，你可以通过数据库名称来指定子文件夹。例如，“cssdm/player”对应的文件会是&amp;lt;tt&amp;gt;addons/sourcemod/data/sqlite/cssdm/players.sql3&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
SQLite支持线程层，并且和MySQL遵循同样的规则。（包括锁和共享连接）&lt;br /&gt;
&lt;br /&gt;
==外部链接==&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>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10764</id>
		<title>SQL (SourceMod Scripting)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10764"/>
		<updated>2019-07-03T08:50:00Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: /* 简介 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。&lt;br /&gt;
&lt;br /&gt;
SourceMod的SQL层的正式称呼是''DBI''，全写是'''D'''ata'''b'''ase '''I'''nterface，中文称呼为数据库接口，这个接口是通用SQL方法的一般抽象。要连接到特定数据库（譬如MySQL和sqLite），则必须加载对应的SourceMod DBI驱动。当前SourceMod已内置有MySQL和SQLite的驱动。&lt;br /&gt;
&lt;br /&gt;
SourceMod会自动按需（当然前提是有）检测并加载驱动。所有的数据库相关内置函数可以在&amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;中找到，对应的C++ API则在&amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;中。&lt;br /&gt;
&lt;br /&gt;
=连接数据库=&lt;br /&gt;
当前共有两种方式连接数据库。第一种是通过命名配置文件，命名配置是在&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;中列出的预设配置。如果使用了数据库，那么SourceMod要求你至少提供一个名为&amp;quot;default&amp;quot;的配置。&lt;br /&gt;
&lt;br /&gt;
下面是配置SQL的一个例子：&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;
请使用&amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt;或&amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;来实例化基于命名配置的数据库连接。&lt;br /&gt;
&lt;br /&gt;
另一种方式是使用&amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt;，并通过传递包含这些参数的键值对句柄对象手动指定所有连接参数。&lt;br /&gt;
&lt;br /&gt;
下面是一个典型的连接数据库的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete db;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=查询语句=&lt;br /&gt;
&lt;br /&gt;
==无返回结果类==&lt;br /&gt;
最简单的查询就是那些不返回结果的查询了，例如，CREATE，DROP，UPDATE，INSERT和DELETE。对于这些查询语句，推荐使用&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;。它的名字里带快，并不意味着它执行起来更快，实际上，它只是让我们在编写代码上更快。下面是使用例子，代码中给定的&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;是一个有效的数据库句柄：&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;
	char 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;
==有返回结果类==&lt;br /&gt;
如果一个查询返回了结果集，并且必须处理它，那么你就必须使用&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;了。与&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;不同的是，这个函数会返回一个必须关闭的句柄。&lt;br /&gt;
&lt;br /&gt;
一个会返回结果的查询例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* 在这里进行结果的处理！&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* 释放句柄对象 */&lt;br /&gt;
	delete query;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=预编译语句=&lt;br /&gt;
预编译语句是另一种查询的方法。预编译语句背后的实质是，你构造一个查询的“模板”，在这之后可以任意次地复用。预编译语句有以下的优势：&lt;br /&gt;
*如果使用预编译语句，那么数据库就可以更好地缓存查询&lt;br /&gt;
*你不需要每次使用时重新构造查询语句&lt;br /&gt;
*你不需要每次使用时分配新的查询结构体&lt;br /&gt;
*输入总是安全的（下面会讲到）&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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* 创建足够的空间来保证我们的查询字符串能被正确引用  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[buffer_len];&lt;br /&gt;
&lt;br /&gt;
	/* 要求SQL驱动程序确保我们的字符串被安全引用 */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len);&lt;br /&gt;
&lt;br /&gt;
	/* 生成查询语句串 */&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;
	/* 执行查询语句 */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == null)&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* 获取一些信息&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	delete hQuery;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
让我们来看看使用预编译语句的版本：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* 检查一下我们是不是已经创建好了语句 */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;
	 * 在这处理信息&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
重要区别：&lt;br /&gt;
*输入字符串（&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;）不需要被反引号（引号）包起来。数据库引擎会自动处理类型安全和插入检查。&lt;br /&gt;
*参数标记&amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;周围不需要引号，即使它接受了一个字符串。&lt;br /&gt;
*我们仅仅只需要创建语句句柄一次，在那之后，于该数据库连接的整个生命周期它都将存在。&lt;br /&gt;
&lt;br /&gt;
=处理查询结果=&lt;br /&gt;
对于普通查询和预编译语句查询，它们处理结果的方式相同。 相关的重要函数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - 返回查询结果的行数。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - 拉取下一行，如果有的话。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - 从当前行拉取特定字段的数据。&lt;br /&gt;
&lt;br /&gt;
我们假设有如下结构的一个简单表：（译注：这是一段建表语句）&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;
下面的示例包含的代码将打印出与某个年龄段匹配的所有用户。常规查询和预编译语句各有一个例子。&lt;br /&gt;
&amp;lt;pawn&amp;gt;void PrintResults(Handle query)&lt;br /&gt;
{&lt;br /&gt;
	/* 即便结果只有一行，你也应该先使用SQL_FetchRow() */&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
注意，这些示例中都没有关闭预编译语句句柄。因为这些示例假定有一个全局的数据库实例，只有在卸载插件时才关闭它。对于那些自己维护临时数据库连接的插件，就必须释放预编译语句句柄，否则数据库连接将永远不会关闭。&lt;br /&gt;
&lt;br /&gt;
=多线程=&lt;br /&gt;
&amp;lt;b&amp;gt;译注：下面的内容比较复杂，请选择性观看。&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SourceMod支持多线程SQL查询。这意味着，数据库操作可以在游戏主线程之外的线程中完成。如果你使用远程的数据库服务器或者需要一个网络连接，查询可能会导致明显延迟，所以如果你的查询发生在游戏进行期间，那么支持线程通常是一个好主意。&lt;br /&gt;
&lt;br /&gt;
多线程查询是''异步的''。这意味着，他们会在回调函数里触发并返回结果。尽管回调函数最终一定能触发，但是你并不能让它在指定时刻触发。某些驱动可能不支持多线程，如果是这种情况，一个运行时错误会被抛出。如果线程器不能被启动或者被禁用，SourceMod会以回调函数的形式在主线程里执行查询。&lt;br /&gt;
&lt;br /&gt;
==操作==&lt;br /&gt;
所有被线程化的操作（除了数据库连接）都使用一样的回调&amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;来接受结果，参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - 数据库句柄对象的拷贝。如果db句柄找不到或者是无效的，会把 &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; 传过去。&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - 结果对象，失败时为null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - 包含错误内容的字符串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - 通过SQL操作传递的自定义数据。&lt;br /&gt;
&lt;br /&gt;
多线程支持下面的操作：&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库连接&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库查询&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*''注意: 预编译语句目前不支持多线程。''&lt;br /&gt;
&lt;br /&gt;
数据库操作的连续调用是安全的。&lt;br /&gt;
&lt;br /&gt;
===连接===&lt;br /&gt;
没必要为了线程化的查询而使用线程化的数据据连接。但是，使用线程化的数据库连接就不会因为建立连接时的延时造成服务器卡顿。&lt;br /&gt;
线程化的数据库连接使用这个回调： &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
回调的参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: 数据库连接的句柄对象，如果无法连接，将会返回&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 未使用&lt;br /&gt;
&lt;br /&gt;
例子: &lt;br /&gt;
&amp;lt;pawn&amp;gt;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===查询===&lt;br /&gt;
只要驱动支持，那么线程化的查询可以用任何的数据库句柄来执行。在线程中，第一个结果集的所有查询结果都会被检索。如果你的查询会返回不止一个结果集（比如，在MySQL中用&amp;lt;tt&amp;gt;CALL&amp;lt;/tt&amp;gt;调用特定函数），那么这个时候线程器的行为是未定义的。&amp;lt;b&amp;gt;请注意，如果你想在同一个连接上同时执行线程和非线程化的查询，你应当先阅读下面有关“线程锁”的小节&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
查询操作使用如下的回调参数：&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;null&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;null&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;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&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 (results == null)&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 (results.RowCount == 0) {&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;
==锁==&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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;
===警告===&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;
==优先级==&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;
==简介==&lt;br /&gt;
[http://www.sqlite.org/ SQLite]是一个基于本地文件的数据库引擎。SourceMod内置了对应DBI。SQLite和MySql是不同的数据库，所以有些Mysql的查询语句在SQLite中将不会生效。在配置文件中它对应的数据库类型填&amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
==使用==&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;
==外部链接==&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>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10763</id>
		<title>SQL (SourceMod Scripting)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10763"/>
		<updated>2019-07-01T09:53:27Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。&lt;br /&gt;
&lt;br /&gt;
SourceMod的SQL层的正式称呼是''DBI''，全写是'''D'''ata'''b'''ase '''I'''nterface，中文称呼为数据库接口，这个接口是通用SQL方法的一般抽象。要连接到特定数据库（譬如MySQL和sqLite），则必须加载对应的SourceMod DBI驱动。当前SourceMod已内置有MySQL和SQLite的驱动。&lt;br /&gt;
&lt;br /&gt;
SourceMod会自动按需（当然前提是有）检测并加载驱动。所有的数据库相关内置函数可以在&amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;中找到，对应的C++ API则在&amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;中。&lt;br /&gt;
&lt;br /&gt;
=连接数据库=&lt;br /&gt;
当前共有两种方式连接数据库。第一种是通过命名配置文件，命名配置是在&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;中列出的预设配置。如果使用了数据库，那么SourceMod要求你至少提供一个名为&amp;quot;default&amp;quot;的配置。&lt;br /&gt;
&lt;br /&gt;
下面是配置SQL的一个例子：&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;
请使用&amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt;或&amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;来实例化基于命名配置的数据库连接。&lt;br /&gt;
&lt;br /&gt;
另一种方式是使用&amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt;，并通过传递包含这些参数的键值对句柄对象手动指定所有连接参数。&lt;br /&gt;
&lt;br /&gt;
下面是一个典型的连接数据库的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete db;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=查询语句=&lt;br /&gt;
&lt;br /&gt;
==无返回结果类==&lt;br /&gt;
最简单的查询就是那些不返回结果的查询了，例如，CREATE，DROP，UPDATE，INSERT和DELETE。对于这些查询语句，推荐使用&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;。它的名字里带快，并不意味着它执行起来更快，实际上，它只是让我们在编写代码上更快。下面是使用例子，代码中给定的&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;是一个有效的数据库句柄：&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;
	char 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;
==有返回结果类==&lt;br /&gt;
如果一个查询返回了结果集，并且必须处理它，那么你就必须使用&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;了。与&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;不同的是，这个函数会返回一个必须关闭的句柄。&lt;br /&gt;
&lt;br /&gt;
一个会返回结果的查询例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* 在这里进行结果的处理！&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* 释放句柄对象 */&lt;br /&gt;
	delete query;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=预编译语句=&lt;br /&gt;
预编译语句是另一种查询的方法。预编译语句背后的实质是，你构造一个查询的“模板”，在这之后可以任意次地复用。预编译语句有以下的优势：&lt;br /&gt;
*如果使用预编译语句，那么数据库就可以更好地缓存查询&lt;br /&gt;
*你不需要每次使用时重新构造查询语句&lt;br /&gt;
*你不需要每次使用时分配新的查询结构体&lt;br /&gt;
*输入总是安全的（下面会讲到）&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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* 创建足够的空间来保证我们的查询字符串能被正确引用  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[buffer_len];&lt;br /&gt;
&lt;br /&gt;
	/* 要求SQL驱动程序确保我们的字符串被安全引用 */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len);&lt;br /&gt;
&lt;br /&gt;
	/* 生成查询语句串 */&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;
	/* 执行查询语句 */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == null)&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* 获取一些信息&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	delete hQuery;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
让我们来看看使用预编译语句的版本：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* 检查一下我们是不是已经创建好了语句 */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;
	 * 在这处理信息&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
重要区别：&lt;br /&gt;
*输入字符串（&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;）不需要被反引号（引号）包起来。数据库引擎会自动处理类型安全和插入检查。&lt;br /&gt;
*参数标记&amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;周围不需要引号，即使它接受了一个字符串。&lt;br /&gt;
*我们仅仅只需要创建语句句柄一次，在那之后，于该数据库连接的整个生命周期它都将存在。&lt;br /&gt;
&lt;br /&gt;
=处理查询结果=&lt;br /&gt;
对于普通查询和预编译语句查询，它们处理结果的方式相同。 相关的重要函数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - 返回查询结果的行数。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - 拉取下一行，如果有的话。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - 从当前行拉取特定字段的数据。&lt;br /&gt;
&lt;br /&gt;
我们假设有如下结构的一个简单表：（译注：这是一段建表语句）&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;
下面的示例包含的代码将打印出与某个年龄段匹配的所有用户。常规查询和预编译语句各有一个例子。&lt;br /&gt;
&amp;lt;pawn&amp;gt;void PrintResults(Handle query)&lt;br /&gt;
{&lt;br /&gt;
	/* 即便结果只有一行，你也应该先使用SQL_FetchRow() */&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
注意，这些示例中都没有关闭预编译语句句柄。因为这些示例假定有一个全局的数据库实例，只有在卸载插件时才关闭它。对于那些自己维护临时数据库连接的插件，就必须释放预编译语句句柄，否则数据库连接将永远不会关闭。&lt;br /&gt;
&lt;br /&gt;
=多线程=&lt;br /&gt;
&amp;lt;b&amp;gt;译注：下面的内容比较复杂，请选择性观看。&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SourceMod支持多线程SQL查询。这意味着，数据库操作可以在游戏主线程之外的线程中完成。如果你使用远程的数据库服务器或者需要一个网络连接，查询可能会导致明显延迟，所以如果你的查询发生在游戏进行期间，那么支持线程通常是一个好主意。&lt;br /&gt;
&lt;br /&gt;
多线程查询是''异步的''。这意味着，他们会在回调函数里触发并返回结果。尽管回调函数最终一定能触发，但是你并不能让它在指定时刻触发。某些驱动可能不支持多线程，如果是这种情况，一个运行时错误会被抛出。如果线程器不能被启动或者被禁用，SourceMod会以回调函数的形式在主线程里执行查询。&lt;br /&gt;
&lt;br /&gt;
==操作==&lt;br /&gt;
所有被线程化的操作（除了数据库连接）都使用一样的回调&amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;来接受结果，参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - 数据库句柄对象的拷贝。如果db句柄找不到或者是无效的，会把 &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; 传过去。&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - 结果对象，失败时为null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - 包含错误内容的字符串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - 通过SQL操作传递的自定义数据。&lt;br /&gt;
&lt;br /&gt;
多线程支持下面的操作：&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库连接&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库查询&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*''注意: 预编译语句目前不支持多线程。''&lt;br /&gt;
&lt;br /&gt;
数据库操作的连续调用是安全的。&lt;br /&gt;
&lt;br /&gt;
===连接===&lt;br /&gt;
没必要为了线程化的查询而使用线程化的数据据连接。但是，使用线程化的数据库连接就不会因为建立连接时的延时造成服务器卡顿。&lt;br /&gt;
线程化的数据库连接使用这个回调： &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
回调的参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: 数据库连接的句柄对象，如果无法连接，将会返回&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 未使用&lt;br /&gt;
&lt;br /&gt;
例子: &lt;br /&gt;
&amp;lt;pawn&amp;gt;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===查询===&lt;br /&gt;
只要驱动支持，那么线程化的查询可以用任何的数据库句柄来执行。在线程中，第一个结果集的所有查询结果都会被检索。如果你的查询会返回不止一个结果集（比如，在MySQL中用&amp;lt;tt&amp;gt;CALL&amp;lt;/tt&amp;gt;调用特定函数），那么这个时候线程器的行为是未定义的。&amp;lt;b&amp;gt;请注意，如果你想在同一个连接上同时执行线程和非线程化的查询，你应当先阅读下面有关“线程锁”的小节&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
查询操作使用如下的回调参数：&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;null&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;null&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;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&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 (results == null)&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 (results.RowCount == 0) {&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;
==锁==&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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;
===警告===&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;
==优先级==&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;
==简介==&lt;br /&gt;
[http://www.sqlite.org/ SQLite]是一个基于本地文件的数据库引擎，SourceMod给它内置了DBI。SQLite和MySql是不同的，所以有些Mysql的查询语句在SQLite中不生效，在配置文件中的数据库类型填&amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
==使用==&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;
==外部链接==&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>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)&amp;diff=10762</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=10762"/>
		<updated>2019-07-01T08:54:01Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&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;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete 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;
	char 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;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* Process results here!&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* Free the Handle */&lt;br /&gt;
	delete 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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* Create enough space to make sure our string is quoted properly  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[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)) == null)&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;
	delete 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;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* Check if we haven't already created the statement */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;void 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;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
=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 (except connecting) use the same callback for result notification: &amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;.  The parameters are:&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - The cloned database handle.  If the db handle was not found or was invalidated, &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; is passed.&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - Result object, or null on failure.&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;
Connecting however uses a different callback: &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The following parameters are used for the threaded connection callback:&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: Handle to the database connection, or &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; if it could not be found.&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;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&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;null&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;null&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;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&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 (results == null)&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 (results.RowCount == 0) {&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10761</id>
		<title>SQL (SourceMod Scripting)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10761"/>
		<updated>2019-06-30T18:46:23Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。&lt;br /&gt;
&lt;br /&gt;
SourceMod的SQL层的正式称呼是''DBI''，全写是'''D'''ata'''b'''ase '''I'''nterface，中文称呼为数据库接口，这个接口是通用SQL方法的一般抽象。要连接到特定数据库（譬如MySQL和sqLite），则必须加载对应的SourceMod DBI驱动。当前SourceMod已内置有MySQL和SQLite的驱动。&lt;br /&gt;
&lt;br /&gt;
SourceMod会自动按需（当然前提是有）检测并加载驱动。所有的数据库相关内置函数可以在&amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;中找到，对应的C++ API则在&amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;中。&lt;br /&gt;
&lt;br /&gt;
=连接数据库=&lt;br /&gt;
当前共有两种方式连接数据库。第一种是通过命名配置文件，命名配置是在&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;中列出的预设配置。如果使用了数据库，那么SourceMod要求你至少提供一个名为&amp;quot;default&amp;quot;的配置。&lt;br /&gt;
&lt;br /&gt;
下面是配置SQL的一个例子：&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;
请使用&amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt;或&amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;来实例化基于命名配置的数据库连接。&lt;br /&gt;
&lt;br /&gt;
另一种方式是使用&amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt;，并通过传递包含这些参数的键值对句柄对象手动指定所有连接参数。&lt;br /&gt;
&lt;br /&gt;
下面是一个典型的连接数据库的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete db;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=查询语句=&lt;br /&gt;
&lt;br /&gt;
==无返回结果类==&lt;br /&gt;
最简单的查询就是那些不返回结果的查询了，例如，CREATE，DROP，UPDATE，INSERT和DELETE。对于这些查询语句，推荐使用&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;。它的名字里带快，并不意味着它执行起来更快，实际上，它只是让我们在编写代码上更快。下面是使用例子，代码中给定的&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;是一个有效的数据库句柄：&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;
	char 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;
==有返回结果类==&lt;br /&gt;
如果一个查询返回了结果集，并且必须处理它，那么你就必须使用&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;了。与&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;不同的是，这个函数会返回一个必须关闭的句柄。&lt;br /&gt;
&lt;br /&gt;
一个会返回结果的查询例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* 在这里进行结果的处理！&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* 释放句柄对象 */&lt;br /&gt;
	delete query;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=预编译语句=&lt;br /&gt;
预编译语句是另一种查询的方法。预编译语句背后的实质是，你构造一个查询的“模板”，在这之后可以任意次地复用。预编译语句有以下的优势：&lt;br /&gt;
*如果使用预编译语句，那么数据库就可以更好地缓存查询&lt;br /&gt;
*你不需要每次使用时重新构造查询语句&lt;br /&gt;
*你不需要每次使用时分配新的查询结构体&lt;br /&gt;
*输入总是安全的（下面会讲到）&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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* 创建足够的空间来保证我们的查询字符串能被正确引用  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[buffer_len];&lt;br /&gt;
&lt;br /&gt;
	/* 要求SQL驱动程序确保我们的字符串被安全引用 */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len);&lt;br /&gt;
&lt;br /&gt;
	/* 生成查询语句串 */&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;
	/* 执行查询语句 */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == null)&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* 获取一些信息&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	delete hQuery;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
让我们来看看使用预编译语句的版本：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* 检查一下我们是不是已经创建好了语句 */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;
	 * 在这处理信息&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
重要区别：&lt;br /&gt;
*输入字符串（&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;）不需要被反引号（引号）包起来。数据库引擎会自动处理类型安全和插入检查。&lt;br /&gt;
*参数标记&amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;周围不需要引号，即使它接受了一个字符串。&lt;br /&gt;
*我们仅仅只需要创建语句句柄一次，在那之后，于该数据库连接的整个生命周期它都将存在。&lt;br /&gt;
&lt;br /&gt;
=处理查询结果=&lt;br /&gt;
对于普通查询和预编译语句查询，它们处理结果的方式相同。 相关的重要函数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - 返回查询结果的行数。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - 拉取下一行，如果有的话。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - 从当前行拉取特定字段的数据。&lt;br /&gt;
&lt;br /&gt;
我们假设有如下结构的一个简单表：（译注：这是一段建表语句）&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;
下面的示例包含的代码将打印出与某个年龄段匹配的所有用户。常规查询和预编译语句各有一个例子。&lt;br /&gt;
&amp;lt;pawn&amp;gt;void PrintResults(Handle query)&lt;br /&gt;
{&lt;br /&gt;
	/* 即便结果只有一行，你也应该先使用SQL_FetchRow() */&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
注意，这些示例中都没有关闭预编译语句句柄。因为这些示例假定有一个全局的数据库实例，只有在卸载插件时才关闭它。对于那些自己维护临时数据库连接的插件，就必须释放预编译语句句柄，否则数据库连接将永远不会关闭。&lt;br /&gt;
&lt;br /&gt;
=多线程=&lt;br /&gt;
&amp;lt;b&amp;gt;译注：下面的内容比较复杂，可以不用看&amp;lt;/b&amp;gt;&lt;br /&gt;
SourceMod支持多线程SQL查询。这意味着，数据库操作可以在游戏主线程之外的线程中完成。如果你使用远程的数据库服务器或者需要一个网络连接，查询可能会导致明显延迟，所以如果你的查询发生在游戏进行期间，那么支持线程通常是一个好主意。&lt;br /&gt;
&lt;br /&gt;
多线程查询是''异步的''。这意味着，他们会在回调函数里触发并返回结果。尽管回调函数最终一定能触发，但是你并不能让它在指定时刻触发。某些驱动可能不支持多线程，如果是这种情况，一个运行时错误会被抛出。如果线程器不能被启动或者被禁用，SourceMod会以回调函数的形式在主线程里执行查询。&lt;br /&gt;
&lt;br /&gt;
==操作==&lt;br /&gt;
所有被线程化的操作（除了数据库连接）都使用一样的回调&amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;来接受结果，参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - 数据库句柄对象的拷贝。如果db句柄找不到或者是无效的，会把 &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; 传过去。&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - 结果对象，失败时为null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - 包含错误内容的字符串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - 通过SQL操作传递的自定义数据。&lt;br /&gt;
&lt;br /&gt;
多线程支持下面的操作：&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库连接&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库查询&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*''注意: 预编译语句目前不支持多线程。''&lt;br /&gt;
&lt;br /&gt;
数据库操作的连续调用是安全的。&lt;br /&gt;
&lt;br /&gt;
===连接===&lt;br /&gt;
没必要为了线程化的查询而使用线程化的数据据连接。但是，使用线程化的数据库连接就不会因为建立连接时的延时造成服务器卡顿。&lt;br /&gt;
线程化的数据库连接使用这个回调： &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
回调的参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: 数据库连接的句柄对象，如果无法连接，将会返回&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 未使用&lt;br /&gt;
&lt;br /&gt;
例子: &lt;br /&gt;
&amp;lt;pawn&amp;gt;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===查询===&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;null&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;null&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;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&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 (results == null)&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 (results.RowCount == 0) {&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;
==锁==&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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;
===警告===&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;
==优先级==&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;
==简介==&lt;br /&gt;
[http://www.sqlite.org/ SQLite]是一个基于本地文件的数据库引擎，SourceMod给它内置了DBI。SQLite和MySql是不同的，所以有些Mysql的查询语句在SQLite中不生效，在配置文件中的数据库类型填&amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
==使用==&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;
==外部链接==&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>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Zh_cn:SQL_(SourceMod_Scripting)&amp;diff=10760</id>
		<title>Zh cn:SQL (SourceMod Scripting)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Zh_cn:SQL_(SourceMod_Scripting)&amp;diff=10760"/>
		<updated>2019-06-30T18:45:44Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__FORCETOC__&lt;br /&gt;
{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。&lt;br /&gt;
&lt;br /&gt;
SourceMod的SQL层的正式称呼是''DBI''，全写是'''D'''ata'''b'''ase '''I'''nterface，中文称呼为数据库接口，这个接口是通用SQL方法的一般抽象。要连接到特定数据库（譬如MySQL和sqLite），则必须加载对应的SourceMod DBI驱动。当前SourceMod已内置有MySQL和SQLite的驱动。&lt;br /&gt;
&lt;br /&gt;
SourceMod会自动按需（当然前提是有）检测并加载驱动。所有的数据库相关内置函数可以在&amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;中找到，对应的C++ API则在&amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;中。&lt;br /&gt;
&lt;br /&gt;
=连接数据库=&lt;br /&gt;
当前共有两种方式连接数据库。第一种是通过命名配置文件，命名配置是在&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;中列出的预设配置。如果使用了数据库，那么SourceMod要求你至少提供一个名为&amp;quot;default&amp;quot;的配置。&lt;br /&gt;
&lt;br /&gt;
下面是配置SQL的一个例子：&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;
请使用&amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt;或&amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;来实例化基于命名配置的数据库连接。&lt;br /&gt;
&lt;br /&gt;
另一种方式是使用&amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt;，并通过传递包含这些参数的键值对句柄对象手动指定所有连接参数。&lt;br /&gt;
&lt;br /&gt;
下面是一个典型的连接数据库的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete db;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=查询语句=&lt;br /&gt;
&lt;br /&gt;
==无返回结果类==&lt;br /&gt;
最简单的查询就是那些不返回结果的查询了，例如，CREATE，DROP，UPDATE，INSERT和DELETE。对于这些查询语句，推荐使用&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;。它的名字里带快，并不意味着它执行起来更快，实际上，它只是让我们在编写代码上更快。下面是使用例子，代码中给定的&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;是一个有效的数据库句柄：&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;
	char 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;
==有返回结果类==&lt;br /&gt;
如果一个查询返回了结果集，并且必须处理它，那么你就必须使用&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;了。与&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;不同的是，这个函数会返回一个必须关闭的句柄。&lt;br /&gt;
&lt;br /&gt;
一个会返回结果的查询例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* 在这里进行结果的处理！&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* 释放句柄对象 */&lt;br /&gt;
	delete query;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=预编译语句=&lt;br /&gt;
预编译语句是另一种查询的方法。预编译语句背后的实质是，你构造一个查询的“模板”，在这之后可以任意次地复用。预编译语句有以下的优势：&lt;br /&gt;
*如果使用预编译语句，那么数据库就可以更好地缓存查询&lt;br /&gt;
*你不需要每次使用时重新构造查询语句&lt;br /&gt;
*你不需要每次使用时分配新的查询结构体&lt;br /&gt;
*输入总是安全的（下面会讲到）&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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* 创建足够的空间来保证我们的查询字符串能被正确引用  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[buffer_len];&lt;br /&gt;
&lt;br /&gt;
	/* 要求SQL驱动程序确保我们的字符串被安全引用 */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len);&lt;br /&gt;
&lt;br /&gt;
	/* 生成查询语句串 */&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;
	/* 执行查询语句 */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == null)&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* 获取一些信息&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	delete hQuery;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
让我们来看看使用预编译语句的版本：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* 检查一下我们是不是已经创建好了语句 */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;
	 * 在这处理信息&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
重要区别：&lt;br /&gt;
*输入字符串（&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;）不需要被反引号（引号）包起来。数据库引擎会自动处理类型安全和插入检查。&lt;br /&gt;
*参数标记&amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;周围不需要引号，即使它接受了一个字符串。&lt;br /&gt;
*我们仅仅只需要创建语句句柄一次，在那之后，于该数据库连接的整个生命周期它都将存在。&lt;br /&gt;
&lt;br /&gt;
=处理查询结果=&lt;br /&gt;
对于普通查询和预编译语句查询，它们处理结果的方式相同。 相关的重要函数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - 返回查询结果的行数。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - 拉取下一行，如果有的话。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - 从当前行拉取特定字段的数据。&lt;br /&gt;
&lt;br /&gt;
我们假设有如下结构的一个简单表：（译注：这是一段建表语句）&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;
下面的示例包含的代码将打印出与某个年龄段匹配的所有用户。常规查询和预编译语句各有一个例子。&lt;br /&gt;
&amp;lt;pawn&amp;gt;void PrintResults(Handle query)&lt;br /&gt;
{&lt;br /&gt;
	/* 即便结果只有一行，你也应该先使用SQL_FetchRow() */&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
注意，这些示例中都没有关闭预编译语句句柄。因为这些示例假定有一个全局的数据库实例，只有在卸载插件时才关闭它。对于那些自己维护临时数据库连接的插件，就必须释放预编译语句句柄，否则数据库连接将永远不会关闭。&lt;br /&gt;
&lt;br /&gt;
=多线程=&lt;br /&gt;
&amp;lt;b&amp;gt;译注：下面的内容比较复杂，可以不用看&amp;lt;/b&amp;gt;&lt;br /&gt;
SourceMod支持多线程SQL查询。这意味着，数据库操作可以在游戏主线程之外的线程中完成。如果你使用远程的数据库服务器或者需要一个网络连接，查询可能会导致明显延迟，所以如果你的查询发生在游戏进行期间，那么支持线程通常是一个好主意。&lt;br /&gt;
&lt;br /&gt;
多线程查询是''异步的''。这意味着，他们会在回调函数里触发并返回结果。尽管回调函数最终一定能触发，但是你并不能让它在指定时刻触发。某些驱动可能不支持多线程，如果是这种情况，一个运行时错误会被抛出。如果线程器不能被启动或者被禁用，SourceMod会以回调函数的形式在主线程里执行查询。&lt;br /&gt;
&lt;br /&gt;
==操作==&lt;br /&gt;
所有被线程化的操作（除了数据库连接）都使用一样的回调&amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;来接受结果，参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - 数据库句柄对象的拷贝。如果db句柄找不到或者是无效的，会把 &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; 传过去。&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - 结果对象，失败时为null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - 包含错误内容的字符串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - 通过SQL操作传递的自定义数据。&lt;br /&gt;
&lt;br /&gt;
多线程支持下面的操作：&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库连接&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库查询&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*''注意: 预编译语句目前不支持多线程。''&lt;br /&gt;
&lt;br /&gt;
数据库操作的连续调用是安全的。&lt;br /&gt;
&lt;br /&gt;
===连接===&lt;br /&gt;
没必要为了线程化的查询而使用线程化的数据据连接。但是，使用线程化的数据库连接就不会因为建立连接时的延时造成服务器卡顿。&lt;br /&gt;
线程化的数据库连接使用这个回调： &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
回调的参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: 数据库连接的句柄对象，如果无法连接，将会返回&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 未使用&lt;br /&gt;
&lt;br /&gt;
例子: &lt;br /&gt;
&amp;lt;pawn&amp;gt;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===查询===&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;null&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;null&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;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&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 (results == null)&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 (results.RowCount == 0) {&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;
==锁==&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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;
===警告===&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;
==优先级==&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;
==简介==&lt;br /&gt;
[http://www.sqlite.org/ SQLite]是一个基于本地文件的数据库引擎，SourceMod给它内置了DBI。SQLite和MySql是不同的，所以有些Mysql的查询语句在SQLite中不生效，在配置文件中的数据库类型填&amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
==使用==&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;
==外部链接==&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>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=Zh_cn:SQL_(SourceMod_Scripting)&amp;diff=10759</id>
		<title>Zh cn:SQL (SourceMod Scripting)</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=Zh_cn:SQL_(SourceMod_Scripting)&amp;diff=10759"/>
		<updated>2019-06-30T18:44:21Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
{{nmbox&lt;br /&gt;
 | header = '''{{Languages/Title|{{SUBPAGENAME}}}}'''&lt;br /&gt;
 | text = &lt;br /&gt;
'''[[{{{1|:{{NAMESPACE}}:{{BASEPAGENAME}}}}}|English]]''' {{Languages/Lang|af|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ar|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ast|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|az|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bcc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|bg|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ca|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ce|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|cs|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|da|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|de|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|el|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|eo|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|es|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fa|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|fr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|gu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|he|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|hu|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|id|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|it|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ja|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ka|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ko|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ksh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|kw|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|mk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ml|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|mr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ms|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|nl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|no|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|oc|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pl|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|pt-br|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ro|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ru|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|si|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sq|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|sv|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|ta|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|th|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|tr|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|uk|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|vi|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|yue|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hans|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-hant|{{{1|}}}|&lt;br /&gt;
}}{{Languages/Lang|zh-tw|{{{1|}}}}}|&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。&lt;br /&gt;
&lt;br /&gt;
SourceMod的SQL层的正式称呼是''DBI''，全写是'''D'''ata'''b'''ase '''I'''nterface，中文称呼为数据库接口，这个接口是通用SQL方法的一般抽象。要连接到特定数据库（譬如MySQL和sqLite），则必须加载对应的SourceMod DBI驱动。当前SourceMod已内置有MySQL和SQLite的驱动。&lt;br /&gt;
&lt;br /&gt;
SourceMod会自动按需（当然前提是有）检测并加载驱动。所有的数据库相关内置函数可以在&amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;中找到，对应的C++ API则在&amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;中。&lt;br /&gt;
&lt;br /&gt;
=连接数据库=&lt;br /&gt;
当前共有两种方式连接数据库。第一种是通过命名配置文件，命名配置是在&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;中列出的预设配置。如果使用了数据库，那么SourceMod要求你至少提供一个名为&amp;quot;default&amp;quot;的配置。&lt;br /&gt;
&lt;br /&gt;
下面是配置SQL的一个例子：&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;
请使用&amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt;或&amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;来实例化基于命名配置的数据库连接。&lt;br /&gt;
&lt;br /&gt;
另一种方式是使用&amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt;，并通过传递包含这些参数的键值对句柄对象手动指定所有连接参数。&lt;br /&gt;
&lt;br /&gt;
下面是一个典型的连接数据库的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete db;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=查询语句=&lt;br /&gt;
&lt;br /&gt;
==无返回结果类==&lt;br /&gt;
最简单的查询就是那些不返回结果的查询了，例如，CREATE，DROP，UPDATE，INSERT和DELETE。对于这些查询语句，推荐使用&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;。它的名字里带快，并不意味着它执行起来更快，实际上，它只是让我们在编写代码上更快。下面是使用例子，代码中给定的&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;是一个有效的数据库句柄：&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;
	char 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;
==有返回结果类==&lt;br /&gt;
如果一个查询返回了结果集，并且必须处理它，那么你就必须使用&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;了。与&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;不同的是，这个函数会返回一个必须关闭的句柄。&lt;br /&gt;
&lt;br /&gt;
一个会返回结果的查询例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* 在这里进行结果的处理！&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* 释放句柄对象 */&lt;br /&gt;
	delete query;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=预编译语句=&lt;br /&gt;
预编译语句是另一种查询的方法。预编译语句背后的实质是，你构造一个查询的“模板”，在这之后可以任意次地复用。预编译语句有以下的优势：&lt;br /&gt;
*如果使用预编译语句，那么数据库就可以更好地缓存查询&lt;br /&gt;
*你不需要每次使用时重新构造查询语句&lt;br /&gt;
*你不需要每次使用时分配新的查询结构体&lt;br /&gt;
*输入总是安全的（下面会讲到）&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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* 创建足够的空间来保证我们的查询字符串能被正确引用  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[buffer_len];&lt;br /&gt;
&lt;br /&gt;
	/* 要求SQL驱动程序确保我们的字符串被安全引用 */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len);&lt;br /&gt;
&lt;br /&gt;
	/* 生成查询语句串 */&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;
	/* 执行查询语句 */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == null)&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* 获取一些信息&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	delete hQuery;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
让我们来看看使用预编译语句的版本：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* 检查一下我们是不是已经创建好了语句 */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;
	 * 在这处理信息&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
重要区别：&lt;br /&gt;
*输入字符串（&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;）不需要被反引号（引号）包起来。数据库引擎会自动处理类型安全和插入检查。&lt;br /&gt;
*参数标记&amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;周围不需要引号，即使它接受了一个字符串。&lt;br /&gt;
*我们仅仅只需要创建语句句柄一次，在那之后，于该数据库连接的整个生命周期它都将存在。&lt;br /&gt;
&lt;br /&gt;
=处理查询结果=&lt;br /&gt;
对于普通查询和预编译语句查询，它们处理结果的方式相同。 相关的重要函数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - 返回查询结果的行数。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - 拉取下一行，如果有的话。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - 从当前行拉取特定字段的数据。&lt;br /&gt;
&lt;br /&gt;
我们假设有如下结构的一个简单表：（译注：这是一段建表语句）&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;
下面的示例包含的代码将打印出与某个年龄段匹配的所有用户。常规查询和预编译语句各有一个例子。&lt;br /&gt;
&amp;lt;pawn&amp;gt;void PrintResults(Handle query)&lt;br /&gt;
{&lt;br /&gt;
	/* 即便结果只有一行，你也应该先使用SQL_FetchRow() */&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
注意，这些示例中都没有关闭预编译语句句柄。因为这些示例假定有一个全局的数据库实例，只有在卸载插件时才关闭它。对于那些自己维护临时数据库连接的插件，就必须释放预编译语句句柄，否则数据库连接将永远不会关闭。&lt;br /&gt;
&lt;br /&gt;
=多线程=&lt;br /&gt;
&amp;lt;b&amp;gt;译注：下面的内容比较复杂，可以不用看&amp;lt;/b&amp;gt;&lt;br /&gt;
SourceMod支持多线程SQL查询。这意味着，数据库操作可以在游戏主线程之外的线程中完成。如果你使用远程的数据库服务器或者需要一个网络连接，查询可能会导致明显延迟，所以如果你的查询发生在游戏进行期间，那么支持线程通常是一个好主意。&lt;br /&gt;
&lt;br /&gt;
多线程查询是''异步的''。这意味着，他们会在回调函数里触发并返回结果。尽管回调函数最终一定能触发，但是你并不能让它在指定时刻触发。某些驱动可能不支持多线程，如果是这种情况，一个运行时错误会被抛出。如果线程器不能被启动或者被禁用，SourceMod会以回调函数的形式在主线程里执行查询。&lt;br /&gt;
&lt;br /&gt;
==操作==&lt;br /&gt;
所有被线程化的操作（除了数据库连接）都使用一样的回调&amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;来接受结果，参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - 数据库句柄对象的拷贝。如果db句柄找不到或者是无效的，会把 &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; 传过去。&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - 结果对象，失败时为null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - 包含错误内容的字符串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - 通过SQL操作传递的自定义数据。&lt;br /&gt;
&lt;br /&gt;
多线程支持下面的操作：&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库连接&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库查询&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*''注意: 预编译语句目前不支持多线程。''&lt;br /&gt;
&lt;br /&gt;
数据库操作的连续调用是安全的。&lt;br /&gt;
&lt;br /&gt;
===连接===&lt;br /&gt;
没必要为了线程化的查询而使用线程化的数据据连接。但是，使用线程化的数据库连接就不会因为建立连接时的延时造成服务器卡顿。&lt;br /&gt;
线程化的数据库连接使用这个回调： &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
回调的参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: 数据库连接的句柄对象，如果无法连接，将会返回&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 未使用&lt;br /&gt;
&lt;br /&gt;
例子: &lt;br /&gt;
&amp;lt;pawn&amp;gt;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===查询===&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;null&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;null&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;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&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 (results == null)&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 (results.RowCount == 0) {&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;
==锁==&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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;
===警告===&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;
==优先级==&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;
==简介==&lt;br /&gt;
[http://www.sqlite.org/ SQLite]是一个基于本地文件的数据库引擎，SourceMod给它内置了DBI。SQLite和MySql是不同的，所以有些Mysql的查询语句在SQLite中不生效，在配置文件中的数据库类型填&amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
==使用==&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;
==外部链接==&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>MisakaSora</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10758</id>
		<title>SQL (SourceMod Scripting)/zh</title>
		<link rel="alternate" type="text/html" href="https://wiki.alliedmods.net/index.php?title=SQL_(SourceMod_Scripting)/zh&amp;diff=10758"/>
		<updated>2019-06-30T18:38:00Z</updated>

		<summary type="html">&lt;p&gt;MisakaSora: Created page with &amp;quot;{{Languages|SQL (SourceMod_Scripting)}} 本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。  Sour...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Languages|SQL (SourceMod_Scripting)}}&lt;br /&gt;
本文将介绍如何使用SourceMod的SQL特性。请注意，这并不是关于SQL或任何特定SQL实现的介绍或教程。&lt;br /&gt;
&lt;br /&gt;
SourceMod的SQL层的正式称呼是''DBI''，全写是'''D'''ata'''b'''ase '''I'''nterface，中文称呼为数据库接口，这个接口是通用SQL方法的一般抽象。要连接到特定数据库（譬如MySQL和sqLite），则必须加载对应的SourceMod DBI驱动。当前SourceMod已内置有MySQL和SQLite的驱动。&lt;br /&gt;
&lt;br /&gt;
SourceMod会自动按需（当然前提是有）检测并加载驱动。所有的数据库相关内置函数可以在&amp;lt;tt&amp;gt;scripting/include/dbi.inc&amp;lt;/tt&amp;gt;中找到，对应的C++ API则在&amp;lt;tt&amp;gt;public/IDBDriver.h&amp;lt;/tt&amp;gt;中。&lt;br /&gt;
&lt;br /&gt;
=连接数据库=&lt;br /&gt;
当前共有两种方式连接数据库。第一种是通过命名配置文件，命名配置是在&amp;lt;tt&amp;gt;configs/databases.cfg&amp;lt;/tt&amp;gt;中列出的预设配置。如果使用了数据库，那么SourceMod要求你至少提供一个名为&amp;quot;default&amp;quot;的配置。&lt;br /&gt;
&lt;br /&gt;
下面是配置SQL的一个例子：&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;
请使用&amp;lt;tt&amp;gt;SQL_Connect&amp;lt;/tt&amp;gt;或&amp;lt;tt&amp;gt;SQL_DefConnect&amp;lt;/tt&amp;gt;来实例化基于命名配置的数据库连接。&lt;br /&gt;
&lt;br /&gt;
另一种方式是使用&amp;lt;tt&amp;gt;SQL_ConnectCustom&amp;lt;/tt&amp;gt;，并通过传递包含这些参数的键值对句柄对象手动指定所有连接参数。&lt;br /&gt;
&lt;br /&gt;
下面是一个典型的连接数据库的例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;char error[255];&lt;br /&gt;
Database db = SQL_DefConnect(error, sizeof(error));&lt;br /&gt;
&lt;br /&gt;
if (db == null)&lt;br /&gt;
{&lt;br /&gt;
	PrintToServer(&amp;quot;Could not connect: %s&amp;quot;, error);&lt;br /&gt;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	delete db;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=查询语句=&lt;br /&gt;
&lt;br /&gt;
==无返回结果类==&lt;br /&gt;
最简单的查询就是那些不返回结果的查询了，例如，CREATE，DROP，UPDATE，INSERT和DELETE。对于这些查询语句，推荐使用&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;。它的名字里带快，并不意味着它执行起来更快，实际上，它只是让我们在编写代码上更快。下面是使用例子，代码中给定的&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;是一个有效的数据库句柄：&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;
	char 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;
==有返回结果类==&lt;br /&gt;
如果一个查询返回了结果集，并且必须处理它，那么你就必须使用&amp;lt;tt&amp;gt;SQL_Query()&amp;lt;/tt&amp;gt;了。与&amp;lt;tt&amp;gt;SQL_FastQuery()&amp;lt;/tt&amp;gt;不同的是，这个函数会返回一个必须关闭的句柄。&lt;br /&gt;
&lt;br /&gt;
一个会返回结果的查询例子：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBResultSet query = SQL_Query(db, &amp;quot;SELECT userid FROM vb_user WHERE username = 'BAILOPAN'&amp;quot;);&lt;br /&gt;
if (query == null)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
} &lt;br /&gt;
else &lt;br /&gt;
{&lt;br /&gt;
	/* 在这里进行结果的处理！&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* 释放句柄对象 */&lt;br /&gt;
	delete query;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=预编译语句=&lt;br /&gt;
预编译语句是另一种查询的方法。预编译语句背后的实质是，你构造一个查询的“模板”，在这之后可以任意次地复用。预编译语句有以下的优势：&lt;br /&gt;
*如果使用预编译语句，那么数据库就可以更好地缓存查询&lt;br /&gt;
*你不需要每次使用时重新构造查询语句&lt;br /&gt;
*你不需要每次使用时分配新的查询结构体&lt;br /&gt;
*输入总是安全的（下面会讲到）&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;int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	DBResultSet hQuery;&lt;br /&gt;
	char query[100];&lt;br /&gt;
&lt;br /&gt;
	/* 创建足够的空间来保证我们的查询字符串能被正确引用  */&lt;br /&gt;
	int buffer_len = strlen(name) * 2 + 1;&lt;br /&gt;
	char[] new_name = new char[buffer_len];&lt;br /&gt;
&lt;br /&gt;
	/* 要求SQL驱动程序确保我们的字符串被安全引用 */&lt;br /&gt;
	SQL_EscapeString(db, name, new_name, buffer_len);&lt;br /&gt;
&lt;br /&gt;
	/* 生成查询语句串 */&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;
	/* 执行查询语句 */&lt;br /&gt;
	if ((hQuery = SQL_Query(query)) == null)&lt;br /&gt;
	{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* 获取一些信息&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	delete hQuery;&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
让我们来看看使用预编译语句的版本：&lt;br /&gt;
&amp;lt;pawn&amp;gt;DBStatement hUserStmt = null;&lt;br /&gt;
int GetSomeInfo(Database db, const char[] name)&lt;br /&gt;
{&lt;br /&gt;
	/* 检查一下我们是不是已经创建好了语句 */&lt;br /&gt;
	if (hUserStmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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 == null)&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;
	 * 在这处理信息&lt;br /&gt;
	 */&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
重要区别：&lt;br /&gt;
*输入字符串（&amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt;）不需要被反引号（引号）包起来。数据库引擎会自动处理类型安全和插入检查。&lt;br /&gt;
*参数标记&amp;lt;tt&amp;gt;?&amp;lt;/tt&amp;gt;周围不需要引号，即使它接受了一个字符串。&lt;br /&gt;
*我们仅仅只需要创建语句句柄一次，在那之后，于该数据库连接的整个生命周期它都将存在。&lt;br /&gt;
&lt;br /&gt;
=处理查询结果=&lt;br /&gt;
对于普通查询和预编译语句查询，它们处理结果的方式相同。 相关的重要函数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_GetRowCount()&amp;lt;/tt&amp;gt; - 返回查询结果的行数。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_FetchRow()&amp;lt;/tt&amp;gt; - 拉取下一行，如果有的话。&lt;br /&gt;
*&amp;lt;tt&amp;gt;SQL_Fetch[Int|String|Float]()&amp;lt;/tt&amp;gt; - 从当前行拉取特定字段的数据。&lt;br /&gt;
&lt;br /&gt;
我们假设有如下结构的一个简单表：（译注：这是一段建表语句）&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;
下面的示例包含的代码将打印出与某个年龄段匹配的所有用户。常规查询和预编译语句各有一个例子。&lt;br /&gt;
&amp;lt;pawn&amp;gt;void PrintResults(Handle query)&lt;br /&gt;
{&lt;br /&gt;
	/* 即便结果只有一行，你也应该先使用SQL_FetchRow() */&lt;br /&gt;
	char name[MAX_NAME_LENGTH];&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char 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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete hQuery;&lt;br /&gt;
&lt;br /&gt;
	return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DBStatement hAgeStmt = null;&lt;br /&gt;
bool GetByAge_Statement(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	if (hAgeSmt == null)&lt;br /&gt;
	{&lt;br /&gt;
		char 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;
		     == null)&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;
注意，这些示例中都没有关闭预编译语句句柄。因为这些示例假定有一个全局的数据库实例，只有在卸载插件时才关闭它。对于那些自己维护临时数据库连接的插件，就必须释放预编译语句句柄，否则数据库连接将永远不会关闭。&lt;br /&gt;
&lt;br /&gt;
=多线程=&lt;br /&gt;
&amp;lt;b&amp;gt;译注：下面的内容比较复杂，可以不用看&amp;lt;/b&amp;gt;&lt;br /&gt;
SourceMod支持多线程SQL查询。这意味着，数据库操作可以在游戏主线程之外的线程中完成。如果你使用远程的数据库服务器或者需要一个网络连接，查询可能会导致明显延迟，所以如果你的查询发生在游戏进行期间，那么支持线程通常是一个好主意。&lt;br /&gt;
&lt;br /&gt;
多线程查询是''异步的''。这意味着，他们会在回调函数里触发并返回结果。尽管回调函数最终一定能触发，但是你并不能让它在指定时刻触发。某些驱动可能不支持多线程，如果是这种情况，一个运行时错误会被抛出。如果线程器不能被启动或者被禁用，SourceMod会以回调函数的形式在主线程里执行查询。&lt;br /&gt;
&lt;br /&gt;
==操作==&lt;br /&gt;
所有被线程化的操作（除了数据库连接）都使用一样的回调&amp;lt;tt&amp;gt;SQLQueryCallback&amp;lt;/tt&amp;gt;来接受结果，参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt; - 数据库句柄对象的拷贝。如果db句柄找不到或者是无效的，会把 &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; 传过去。&lt;br /&gt;
*&amp;lt;tt&amp;gt;results&amp;lt;/tt&amp;gt; - 结果对象，失败时为null。&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; - 包含错误内容的字符串。&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt; - 通过SQL操作传递的自定义数据。&lt;br /&gt;
&lt;br /&gt;
多线程支持下面的操作：&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库连接&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TConnect&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*&amp;lt;b&amp;gt;数据库查询&amp;lt;/b&amp;gt;，使用函数 &amp;lt;tt&amp;gt;SQL_TQuery()&amp;lt;/tt&amp;gt;。&lt;br /&gt;
*''注意: 预编译语句目前不支持多线程。''&lt;br /&gt;
&lt;br /&gt;
数据库操作的连续调用是安全的。&lt;br /&gt;
&lt;br /&gt;
===连接===&lt;br /&gt;
没必要为了线程化的查询而使用线程化的数据据连接。但是，使用线程化的数据库连接就不会因为建立连接时的延时造成服务器卡顿。&lt;br /&gt;
线程化的数据库连接使用这个回调： &amp;lt;tt&amp;gt;SQLConnectCallback&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
回调的参数如下：&lt;br /&gt;
*&amp;lt;tt&amp;gt;db&amp;lt;/tt&amp;gt;: 数据库连接的句柄对象，如果无法连接，将会返回&amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;&lt;br /&gt;
*&amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;: 错误的字符串，如果有&lt;br /&gt;
*&amp;lt;tt&amp;gt;data&amp;lt;/tt&amp;gt;: 未使用&lt;br /&gt;
&lt;br /&gt;
例子: &lt;br /&gt;
&amp;lt;pawn&amp;gt;Database hDatabase = null;&lt;br /&gt;
&lt;br /&gt;
void StartSQL()&lt;br /&gt;
{&lt;br /&gt;
	Database.Connect(GotDatabase);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void GotDatabase(Database db, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	if (db == null)&lt;br /&gt;
	{&lt;br /&gt;
		LogError(&amp;quot;Database failure: %s&amp;quot;, error);&lt;br /&gt;
	} &lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
		hDatabase = db;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pawn&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===查询===&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;null&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;null&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;void CheckSteamID(int userid, const char[] auth)&lt;br /&gt;
{&lt;br /&gt;
	char query[255];&lt;br /&gt;
	FormatEx(query, sizeof(query), &amp;quot;SELECT userid FROM users WHERE steamid = '%s'&amp;quot;, auth);&lt;br /&gt;
	hdatabase.Query(T_CheckSteamID, query, userid);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public void T_CheckSteamID(Database db, DBResultSet results, const char[] error, any data)&lt;br /&gt;
{&lt;br /&gt;
	int client = 0;&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 (results == null)&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 (results.RowCount == 0) {&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;
==锁==&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(Database db, int age)&lt;br /&gt;
{&lt;br /&gt;
	char query[100];&lt;br /&gt;
	FormatEx(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;
	DBResultSet hQuery = SQL_Query(db, query);&lt;br /&gt;
	if (hQuery == null)&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;
	delete 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;
===警告===&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;
==优先级==&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;
==简介==&lt;br /&gt;
[http://www.sqlite.org/ SQLite]是一个基于本地文件的数据库引擎，SourceMod给它内置了DBI。SQLite和MySql是不同的，所以有些Mysql的查询语句在SQLite中不生效，在配置文件中的数据库类型填&amp;lt;tt&amp;gt;sqlite&amp;lt;/tt&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
==使用==&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;
==外部链接==&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>MisakaSora</name></author>
		
	</entry>
</feed>