Главная Новости Услуги Форум | База знаний

Вверх

Меню
Пользователей онлайн
Онлайн игроков: 60

Пример плагина: 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.
Назад