Пример плагина: MySQL запросы через sqlx.inc
Пример: топ-5 игроков по убийствам через MySQL (sqlx)
<sqlx> — стандартный модуль AMX Mod X для работы с базами данных (MySQL/SQLite/Postgres). Поддерживает синхронные и асинхронные (threaded) запросы, prepared statements, transaction. Очень полезно для сохранения статистики игроков на сервере между перезапусками.
Что делает плагин
- При заходе игрока асинхронно SELECT его фраги/смерти из таблицы
player_stats. - Команда
/topасинхронно SELECT топ-5 и показывает в MOTD-окне. - Создаёт таблицу
player_statsпри загрузке плагина если её нет.
Полный код плагина
#include <amxmodx>
#include <sqlx>
new Handle:g_sql_tuple;
new g_sql_ready = 0;
public plugin_init()
{
register_plugin("MYSQL Top Demo", "1.0", "WikiExample");
// Создаём tuple с параметрами подключения
// host, user, password, database
g_sql_tuple = SQL_MakeDbTuple("127.0.0.1", "amxx_user", "secret", "amxx_db");
// Создаём таблицы при старте (threaded)
SQL_ThreadQuery(g_sql_tuple, "handle_create_table",
"CREATE TABLE IF NOT EXISTS player_stats ( authid VARCHAR(35) PRIMARY KEY, name VARCHAR(64), frags INT NOT NULL DEFAULT 0, deaths INT NOT NULL DEFAULT 0, last_seen INT NOT NULL DEFAULT 0)");
register_clcmd("say /top", "cmd_top");
}
public plugin_end()
{
if (g_sql_tuple != Empty_Handle)
SQL_FreeHandle(g_sql_tuple);
}
public handle_create_table(failstate, Handle:query, error[], errcode, data[], datasize, Float:queuetime)
{
if (failstate == TQUERY_CONNECT_FAILED)
{
log_amx("[SQLX] Connect failed: %s", error);
return;
}
if (failstate == TQUERY_QUERY_FAILED)
{
log_amx("[SQLX] Query failed: %s", error);
return;
}
g_sql_ready = 1;
log_amx("[SQLX] Table ready.");
}
public client_putinserver(id)
{
if (is_user_bot(id) || !g_sql_ready) return;
new authid[35], name[32];
get_user_authid(id, authid, charsmax(authid));
get_user_name(id, name, charsmax(name));
// Подставляем параметры в запрос (?)
new query[512];
formatex(query, charsmax(query),
"INSERT INTO player_stats (authid, name, last_seen) VALUES (\x27%%s\x27,\x27%%s\x27, %%d) ON DUPLICATE KEY UPDATE name=\x27%%s\x27, last_seen=%%d",
authid, name, get_systime(), name, get_systime());
new data[1];
data[0] = id;
SQL_ThreadQuery(g_sql_tuple, "handle_connect_query", query, data, 1);
}
public handle_connect_query(failstate, Handle:query, error[], errcode, data[], datasize, Float:queuetime)
{
if (failstate)
{
log_amx("[SQLX] Connect query failed: %s", error);
return;
}
new id = data[0];
if (!is_user_connected(id)) return;
// SELECT top-5 действует параллельно
}
public cmd_top(id)
{
if (!g_sql_ready)
{
client_print(id, print_chat, "[Top] БД ещё не готова, попробуйте позже.");
return PLUGIN_HANDLED;
}
new query[256];
formatex(query, charsmax(query),
"SELECT name, frags, deaths FROM player_stats ORDER BY frags DESC LIMIT 5");
new data[1];
data[0] = id;
SQL_ThreadQuery(g_sql_tuple, "handle_top_query", query, data, 1);
client_print(id, print_chat, "[Top] Загрузка топ-5...");
return PLUGIN_HANDLED;
}
public handle_top_query(failstate, Handle:query, error[], errcode, data[], datasize, Float:queuetime)
{
if (failstate)
{
log_amx("[SQLX] Top query failed: %s", error);
return;
}
new id = data[0];
if (!is_user_connected(id)) return;
new motd[1536], pos = 0;
pos += formatex(motd[pos], charsmax(motd)-pos,
"<html><body style=\x27background:#111;color:#eee;font-family:sans-serif\x27>");
pos += formatex(motd[pos], charsmax(motd)-pos,
"<h2>Топ-5 игроков</h2><table border=1 cellpadding=4 style=\x27border-collapse:collapse\x27>");
pos += formatex(motd[pos], charsmax(motd)-pos,
"<tr><th>#</th><th>Имя</th><th>Frags</th><th>Deaths</th></tr>");
new row = 1;
new name[64], fr, dt;
while (SQL_MoreResults(query))
{
SQL_ReadResult(query, 0, name, charsmax(name));
fr = SQL_ReadResult(query, 1);
dt = SQL_ReadResult(query, 2);
pos += formatex(motd[pos], charsmax(motd)-pos,
"<tr><td>%d</td><td>%s</td><td>%d</td><td>%d</td></tr>",
row, name, fr, dt);
SQL_NextRow(query);
row++;
}
pos += formatex(motd[pos], charsmax(motd)-pos, "</table></body></html>");
show_motd(id, motd, "Top 5 Players");
}
Ключевые функции sqlx.inc
SQL_MakeDbTuple(host, user, pass, db)— создаёт Handle с параметрами подключения.SQL_FreeHandle(handle)— освобождает (в plugin_end обязательно).SQL_ThreadQuery(tuple, handler, query, data, datasize)— асинхронный запрос. НЕ блокирует главный поток сервера. handler(failstate, Handle:query, error[], errcode, data[], datasize, Float:queuetime).SQL_PrepareQuery(tuple, fmt, ...)— синхронный, блокирует сервер. Только для критичных short-queries.SQL_MoreResults(query)/SQL_NextRow(query)— итерация по SELECT.SQL_ReadResult(query, column, buffer, len)— чтение ячейки текущей строки.- Константы ошибок:
TQUERY_CONNECT_FAILED,TQUERY_QUERY_FAILED.
Подготовка базы: создайте БД и пользователя. Дать права:
CREATE DATABASE amxx_db;
CREATE USER amxx_user IDENTIFIED BY secret;
GRANT ALL ON amxx_db.* TO amxx_user;
FLUSH PRIVILEGES;
Совет: ВСЕГДА используйте threaded queries в AMXX. Синхронные блокируют игровой поток (сервер фризит) — это видно как высокий variance у tickrate. В коде выше одинарные кавычки в SQL подставлены через Pawn-escape \x27 чтобы не закрывать строку Pawn-кода раньше времени.
Подробные инструкции и обсуждения ждём вас на нашем форуме — в разделе CS 1.6.