Скриптер
Проверенный
Пользователь
- Сообщения
- 219
- Реакции
- 184
- Помог
- 3 раз(а)
Цели статьи научить вас:
1) Правильно подключаться к базе данных
2) Сохранять данные
3) Удалять данные
4) Обновлять данные
5) Получать данные
6) Дать общее понятие по работе с модулем
В данной статье мы будем использовать асинхронные запросы к Базе Данных (далее БД).
Почему их? Всё просто. Данный вид запросов не тормозит сервер, т.к. сервер не ждёт ответа, а дальше выполняет свою работу.
У этих запросов есть своя особенность: при каждом запросе идет подключение и отключение БД. В конце статьи будет весь код целиком с комментариями.
Итак, первым делом нам надо подключиться к БД, чтобы это сделать создадим переменную:
Но для начала нужно запомнить данные для входа. В этом нам поможет следующая функция:
Подключаться мы будем в форварде plugin_cfg. Именно здесь, чтобы дать всем плагинам прогрузиться и не нагружать в этот момент сервер еще больше.
Подготовим данные для входа
данные для входа я подставляю вот таким образом
указываем кодировку utf8 для правильного отображения русского текста
Отлично, если Вы всё сделали правильно, то Вам удалось подключиться к БД.
Теперь, когда мы убедились, что всё работает, можно и записывать данные в БД.
Данные можно записывать как во время игры, например, так работает плагин мута или бана, а можно сохранять данные при выходе игрока. Мы рассмотрим второй вариант.
Момент выхода игрока ловится форвардом client_disconnect.
Для записи в БД нам нужно определиться с ключом. Им может выступить ник игрока, стим или IP. Я предпочитаю стим.
Если вы решили использовать в качестве ключа Ник или же будете отправлять запрос в БД,
где могут быть символы "разбивающие” запрос (нарушающие синтаксис), то такие данные следует экранировать.
В этом Вам поможет стоковая функция mysql_escape_string. Подробное описание будет в конце статьи.
Внимание!
Распространённая ошибка. В БД записываются нулевые значения, что может привести к “слёту” данных игрока, т.е. их обнулению, чтобы это избежать делаем проверку
если всё окей, то идем дальше
Создадим необходимые массивы
Теперь, когда мы сохранили данные, то их надо как-то получить обратно, верно?
В этом нам поможет всё та же функция SQL_ThreadQuery. Как я сказал выше, она основная, а все остальные подготавливают информацию для неё. Всё так или иначе закручено на ней.
Приступим.
Получать данные мы будем в форварде client_connect.
Здесь практически всё также как и при записи, поэтому расписывать всё так подробно не буду. Остановлюсь на подготовке запроса. Наглядно Вы можете увидеть в конце статьи.
Как вы видите появился новый параметр iData. Он передается в обработчик
Именно его, мы будем в дальнейшем использовать для фильтрования ответов от БД
Обновление данных происходит по тому же принципу, меняется лишь запрос в БД.
При обновлении используется оператор UPDATE.
SET говорит БД установить значение поля на указанное WHERE (где) стим равен указанному
Удаляются данные следующим образом:
Оператор DELETE говорит БД, что надо строку стереть, где стим равен указанному
Наконец-то добрались до самого вкусного – обработчика запроса. Как я и сказал выше, сюда приходит ответ от БД. Обработчик имеет следующий синтаксис.
// FailState – отсюда узнаем об успешности отправки запроса
// Query – массив для запроса, не очищайте его
// error – массив для ошибки, если будут
// errnum – количество ошибок
// data – тот волшебный параметр, который передается из SQL_ThreadQuery
// size – размер параметра
// QueryTime – время за которое пришёл ответ
Для начала нужно убедиться, что запрос был успешный.
Для общего понимания.
В FailState может прийти три значения:
#define TQUERY_CONNECT_FAILED -2 - неудачная попытка подключения
#define TQUERY_QUERY_FAILED -1 - ошибка в запросе
#define TQUERY_SUCCESS 0 - удачный запрос
проверяем всё ли в порядке
Теперь можно рассказать о “волшебном параметре”. Я его использую для записи id игрока.
Сохраняем id игрока в переменную
Проверяем, что Userid совпадал, чтобы убедиться, что игрок в запросе соответствует текущему игроку.
Ну вот и всё, ничего сложного надо только разобраться. Удачи в освоении.
Как и обещал, прикладываю код:
Итак, стоковая функция mysql_escape_string и обещал.
Важно!
Массив dest должен быть в 2 раза больше массива src, чтобы избежать ошибок запросах или инъекции.
Как использовать?
Всегда нужно экранировать имя игрока для надежности. На этом примере и рассмотрим
Далее смело готовим запрос и отправляем
1) Правильно подключаться к базе данных
2) Сохранять данные
3) Удалять данные
4) Обновлять данные
5) Получать данные
6) Дать общее понятие по работе с модулем
В данной статье мы будем использовать асинхронные запросы к Базе Данных (далее БД).
Почему их? Всё просто. Данный вид запросов не тормозит сервер, т.к. сервер не ждёт ответа, а дальше выполняет свою работу.
У этих запросов есть своя особенность: при каждом запросе идет подключение и отключение БД. В конце статьи будет весь код целиком с комментариями.
Итак, первым делом нам надо подключиться к БД, чтобы это сделать создадим переменную:
PHP:
new Handle:g_h_Sql // сюда сохраним данные для входа в БД
PHP:
native Handle:SQL_MakeDbTuple(const host[], const user[], const pass[], const db[], timeout=0);
/**
host – IP вашей БД.
User – логин,
pass – пароль,
db – название БД,
timeout – как долго будет ждать ответа сервер от БД. По дефолту 0. Данный параметр нам не нужен, оставляем как есть.
**/
Подготовим данные для входа
данные для входа я подставляю вот таким образом
PHP:
new const SQLx_host[] = "Ваш IP" // IP БД
new const SQLx_db[] = "Ваше имя БД" // имя БД
new const SQLx_user[] = "Ваш логин" // пользователь БД
newconst SQLx_pass[] = "Ваш пароль" // пароль к БД
g_h_Sql = SQL_MakeDbTuple(SQLx_host, SQLx_user, SQLx_pass, SQLx_db)
PHP:
SQL_SetCharset(g_h_Sql, "utf8");
Теперь, когда мы убедились, что всё работает, можно и записывать данные в БД.
Данные можно записывать как во время игры, например, так работает плагин мута или бана, а можно сохранять данные при выходе игрока. Мы рассмотрим второй вариант.
Момент выхода игрока ловится форвардом client_disconnect.
Для записи в БД нам нужно определиться с ключом. Им может выступить ник игрока, стим или IP. Я предпочитаю стим.
Если вы решили использовать в качестве ключа Ник или же будете отправлять запрос в БД,
где могут быть символы "разбивающие” запрос (нарушающие синтаксис), то такие данные следует экранировать.
В этом Вам поможет стоковая функция mysql_escape_string. Подробное описание будет в конце статьи.
Внимание!
Распространённая ошибка. В БД записываются нулевые значения, что может привести к “слёту” данных игрока, т.е. их обнулению, чтобы это избежать делаем проверку
PHP:
if(iMyValue > 0 )
Создадим необходимые массивы
PHP:
new szSteamId[35] // сюда запишем стим игрока
new szQuery[256] // сюда мы запишем запрос
// этой функцией мы узнаем стим игрока
get_user_authid(id, szSteamId, charsmax(szSteamId))
// сюда мы запишем сам запрос к БД
// INSERT INTO – оператор отвечающий за добовление строки в таблицу
// DataBase – название таблицы
// SteamID – поле куда запишется ключ
// Value – поле куда запишутся наши данные
formatex(szQuery, charsmax(szQuery), "INSERT INTO DataBase (SteamID, Value) VALUES('%s', '%d')", szSteamId, iMyValue)
// Функция SQL_ThreadQuery отправляет наш запрос в БД. Ради неё мы тут все собрались
// QueryHandler – название обработчика нашего запроса, т.е. куда придет ответ от БД.
// szQuery – Запрос, который будет отправлен
SQL_ThreadQuery(g_h_Sql, "QueryHandler", szQuery)
В этом нам поможет всё та же функция SQL_ThreadQuery. Как я сказал выше, она основная, а все остальные подготавливают информацию для неё. Всё так или иначе закручено на ней.
Приступим.
Получать данные мы будем в форварде client_connect.
Здесь практически всё также как и при записи, поэтому расписывать всё так подробно не буду. Остановлюсь на подготовке запроса. Наглядно Вы можете увидеть в конце статьи.
PHP:
// SELECT * FROM – оператор, говорящий БД, что мы хотим получить данные
formatex(szQuery, charsmax(szQuery), "SELECT * FROM DataBase WHERE SteamID = '%s'", szSteamId)
Именно его, мы будем в дальнейшем использовать для фильтрования ответов от БД
PHP:
SQL_ThreadQuery(g_h_Sql, " QueryHandler", szQuery, iData, charsmax(iData))
При обновлении используется оператор UPDATE.
SET говорит БД установить значение поля на указанное WHERE (где) стим равен указанному
PHP:
formatex(szQuery, charsmax(szQuery), "UPDATE DataBase SET Value = '%d' WHERE SteamID= '%s'", iMyValue, szSteamId)
SQL_ThreadQuery(g_h_Sql, "QueryHandler", szQuery, iData, charsmax(iData))
Оператор DELETE говорит БД, что надо строку стереть, где стим равен указанному
PHP:
formatex(szQuery, charsmax(szQuery), "DELETE FROM DataBase WHERE SteamID = '%s'", szSteamId)
// выполняем запрос
SQL_ThreadQuery(g_h_Sql, "QueryHandler", szQuery)
// FailState – отсюда узнаем об успешности отправки запроса
// Query – массив для запроса, не очищайте его
// error – массив для ошибки, если будут
// errnum – количество ошибок
// data – тот волшебный параметр, который передается из SQL_ThreadQuery
// size – размер параметра
// QueryTime – время за которое пришёл ответ
PHP:
public QueryHandler(FailState, Handle:Query, error[], iErrNum, data[], size, Float:QueryTime)
Для общего понимания.
В FailState может прийти три значения:
#define TQUERY_CONNECT_FAILED -2 - неудачная попытка подключения
#define TQUERY_QUERY_FAILED -1 - ошибка в запросе
#define TQUERY_SUCCESS 0 - удачный запрос
проверяем всё ли в порядке
PHP:
if(FailState != TQUERY_SUCCESS)
{
// если нет, то логируем
log_amx("sql error: %d (%s)", iErrNum, error)
// завершаем выполнение паблика
return
}
Сохраняем id игрока в переменную
PHP:
new id = data[0]
PHP:
if(data[1] != get_user_userid(id))
return;
{
//Проверяем, чтобы не пришел нулевой результат
if( SQL_NumResults(Query) > 0 )
{
//чтобы считать результат нужно узнать номер колонки
//для этого создадим переменную и запишем номер
//нумерация колонок (столбцов) начинается с нуля
//Можно самостоятельно подставить номер столбца либо воспользоваться функцией
new iField = SQL_FieldNameToNum(Query, "Value")
// Value – название колонки
// Результат же нужно прочитать и куда то записать?
new szTemp[35] // сюда мы запишем результат
SQL_ReadResult(Query, iField, szTemp, charsmax(szTemp))
//либо
new iTemp = SQL_ReadResult(Query, 0);
// В iTemp у нас сохранится число (int)
}
}
Как и обещал, прикладываю код:
PHP:
#include <amxmodx>
const MAXPLAYERS = 32 // максимально количество игроков на сервере
const SQLx_host = "Ваш IP" // IP БД
const SQLx_db = "Ваше имя БД" // имя БД
const SQLx_user = "Ваш логин" // пользователь БД
const SQLx_pass = "Ваш пароль" // пароль к БД
new Handle:g_h_Sql // сюда сохраним данные для входа в БД
new g_iMoney[MAXPLAYERS + 1] // Глобальный массив для хранения значения
public plugin_init() register_plugin("DB Example", "0.1", "gyxoBka")
// Подключаться мы будем в форварде plugin_cfg
public plugin_cfg()
{
g_h_Sql = SQL_MakeDbTuple(SQLx_host, SQLx_user, SQLx_pass, SQLx_db) // кэшируем данные для входа
}
// ловим момент выхода игрока
public client_disconnect(id)
{
//Создадим необходимые массивы
new szSteamId[35] // сюда запишем стим игрока
new szQuery[256] // сюда мы запишем запрос
new iMyValue = 25 // для наглядности создим переменную, которую будем записывать в БД
// этой функцией мы узнаем стим игрока
get_user_authid(id, szSteamId, charsmax(szSteamId))
// форматируем запрос в БД
formatex(szQuery, charsmax(szQuery), "INSERT INTO DataBase (SteamID, Value) VALUES('%s', '%d')", szSteamId, iMyValue)
// Отправляем запрос
SQL_ThreadQuery(g_h_Sql, "QueryHandler", szQuery)
}
// ловим момент входа игрока
public client_connect(id)
{
new szSteamId[35] // сюда запишем стим игрока
new szQuery[256] // сюда мы запишем запрос
new iData[2] // волшебный параметр
iData[0] = id // сохраняем id игрока
iData[1] = get_user_userid(id); // сохраняем userid
// этой функцией мы узнаем стим игрока
get_user_authid(id, szSteamId, charsmax(szSteamId))
// форматируем запрос в БД
formatex(szQuery, charsmax(szQuery), "SELECT * FROM DataBase WHERE SteamID = '%s'", szSteamId)
// Отправляем запрос
SQL_ThreadQuery(g_h_Sql, "QueryHandler", szQuery, iData, sizeof(iData))
}
// Обработчик ответа от БД
public QueryHandler(FailState, Handle:Query, error[], iErrNum, data[], size, Float:QueryTime)
{
if(FailState != TQUERY_SUCCESS)
{
// если нет, то логируем
log_amx("sql error: %d (%s)", iErrNum, error)
// завершаем выполнение паблика
return
}
//Сохраняем для оптимизации id игрока
new id = data[0]
//Проверяем наш ли это игрок, если нет, то завершаем выполнение функции
if(data[1] != get_user_userid(id))
return;
//Проверяем, чтобы не пришел нулевой результат
if( SQL_NumResults(Query) > 0 )
{
//чтобы считать результат нужно узнать номер колонки
// для этого создадим переменную и запишем номер
new iField = SQL_FieldNameToNum(Query, "Value")
// Value – название колонки
// Результат же нужно прочитать и куда то записать?
new szTemp[35] // сюда мы запишем результат
//При помощи данной функции можно прочитать ответ как целое число, дробное или строку. Ниже пример чтения как строки
SQL_ReadResult(Query, iField, szTemp, charsmax(szTemp))
//Сохраняем значение в глобальный массив
g_iMoney[id] = str_to_num(szTemp)
}
}
PHP:
// dest[] – массив куда скопируются экранированные данные
// len – длина массива dest
// src[] – исходная строка для экранирования
stock mysql_escape_string(dest[], len, src[])
{
copy(dest, len, src);
replace_all(dest, len, "\", "\\");
replace_all(dest, len, "\0", "\\0");
replace_all(dest, len, "\r", "\\r");
replace_all(dest, len, "\n", "\\n");
replace_all(dest, len, "\x1a", "\Z");
replace_all(dest, len, "'", "\'");
replace_all(dest, len, "^"", "\^"");
return PLUGIN_HANDLED;
}
Массив dest должен быть в 2 раза больше массива src, чтобы избежать ошибок запросах или инъекции.
Как использовать?
Всегда нужно экранировать имя игрока для надежности. На этом примере и рассмотрим
PHP:
New UserName[32], UserNameSQL[64]
// получаем имя игрока
get_user_name(id, UserName, 31);
// экранируем имя игрока
mysql_escape_string(UserNameSQL, charsmax(UserNameSQL), UserName)
Последнее редактирование: