[SourcePawn] Урок 13 - Работа с базами данных (MySQL, SQLite)

Сообщения
161
Реакции
698
public void SQL_Callback_SelectClient(Database hDatabase, DBResultSet results, const char[] sError, any iUserID) // Обратный вызов
{
if(
sError[0]) // Если произошла ошибка
{
LogError("SQL_Callback_SelectClient: %s", sError); // Выводим в лог
return; // Прекращаем выполнение ф-и
}

int iClient = GetClientOfUserId(iUserID);
if(
iClient)
{
// Игрок всё еще на сервере
}
}
Согласно официальной документации: SQLQueryCallback
Код:
error: Error string if there was an error. The error could be empty even if an error condition exists, so it is important to check the actual results value instead.
(Строка ошибки, если она произошла. Строка может быть пуста, даже если ошибка произошла, так что важно проверять значение results, вместо error.)
 
Сообщения
30
Реакции
5
Помогите пожалуйста. Есть следующая проблема:
Если mysql не доступна то плагин который выполняет mysql запросы при попытке сделать запрос "крашит" сервер
Как можно "чекнуть" базу на доступность перед запросом!? Чтобы проверить доступна ли база данных или нет?
К примеру если нет то пропустить выполнение запроса. Или есть другие варианты решения подобной проблемы?
Спасибо за любую оказанную помощь.

bool ClientInBlackList(int client)
{
if(g_hDB == null)
{
LogError("database is invalid");
return false;
}

SQL_LockDatabase(g_hBugDB);

char szQuery[128];
char szAuth[32];
GetClientAuthId(client, AuthId_Steam2, szAuth, 32);
FormatEx(szQuery, sizeof(szQuery), "SELECT `auth` FROM `users` WHERE `auth` = '%s' AND `block` = '1' LIMIT 1;", szAuth);
DBResultSet hQuery = SQL_Query(g_hBugDB, szQuery);

SQL_UnlockDatabase(g_hBugDB);

if (hQuery == null)
{
return false;
}

if(hQuery.FetchRow())
{
g_ClientInBL[client] = true;
}
else
{
g_ClientInBL[client] = false;
}

delete hQuery;

return false;
}
 
Сообщения
288
Реакции
582
@Iamboy, используй везде SQL_TQuery, и не будет крашей, запрос просто повиснет на 'x' сек, а потом отвиснет и выдаст ошибку что-то вроде:
Код:
Lost Connection to MySQL server during query
 

Crocell

Мошенник
Сообщения
106
Реакции
39
sqlite
Обучаюсь sourcepawn, вот хотелось бы узнать, есть ли урок, направленный именно на то как сделать:
Запись чего-то в базу.
Создание той базы.
Получение содержимого из базы.

Как это сделано напримере с куки, там все очень подробно про это рассказано, тут же что-то не понятное.. мб я слепой? и тут это где-то есть?
--- Добавлено позже ---
@R1KO Хотелось бы видеть от тебя урок, как в примере с куками, более направленный на sqlite.
Например я хочу сделать свой личный кабинет, вот хочу увидеть уроки, как работать с базой, как записывать в неё данные под каждого игрока и получать их когда надо и т.д
 

DeeperSpy

Гений, миллиардер, плейбой, филантроп, майнкрафтер
Сообщения
396
Реакции
173
Код:
public void ConnectCallBack (Database hDB, const char[] error, any data) // Пришел результат соеденения
{
    if (hDB == null)    // Соединение  не удачное
    {
        SetFailState("Database failure: %s", sError); // Отключаем плагин
        return;
    }
    
    g_hDatabase = hDB; // Присваиваем глобальной переменной соеденения значение текущего соеденения
    CreateTables(); // Функция пока не реализована, но по имени, думаю, ясно что она делает
}
Опечатка, соединения*
Код:
SetFailState("Database failure: %s", sError); // Отключаем плагин
А тут разве не error должен стоять?
 
  • Like
Реакции: R1KO
Сообщения
2
Реакции
0
@r1ko
Привет, можете помочь понять, когда следует блокировать базу данных, а когда нет?
И в чём отличаются поточные и не поточные запросы?

Заранее спасибо.​
 
Последнее редактирование:

Kruzya

Супермодератор
Сообщения
7,434
Реакции
5,689
@login, базу блокировать надо при выполнении непоточных запросов.
Отличие, собственно, вот в чём: поточные запросы выполняются в другом, отдельном от сервера, потоке, а потому не вызывают никаких фризов. Непоточные же выполняются в одном потоке с игровым тиком, а потому, если сервер будет очень долго думать, то будет фриз гарантированный.
 
Сообщения
3
Реакции
0
@login, базу блокировать надо при выполнении непоточных запросов.
Отличие, собственно, вот в чём: поточные запросы выполняются в другом, отдельном от сервера, потоке, а потому не вызывают никаких фризов. Непоточные же выполняются в одном потоке с игровым тиком, а потому, если сервер будет очень долго думать, то будет фриз гарантированный.

Подскажите, при реализации MySQL запросов через отдельные потоки информация, возвращаемая в обратном вызове, будет доступна не сразу согласно тому же SQL (SourceMod Scripting) - AlliedModders Wiki
Там сказано, что "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."
Поэтому возникает вопрос, как правильно обработать задержку при получении данных, что бы это не начало вызывать фризы на сервере? Данные из БД нужны не для вывода в лог или показу пользователю, а для принятия решения при коннекте игрока к серверу. Но обратный вызов слишком долго отдает результаты запроса на выборку данных, в итоге обрабатывающая функция при опросе переменных получает сплошные нули вместо данных, и лишь спустя пол секунды приходят сами данные.
К тому же сама задержка может сильно разниться. Можно было бы вставить искуственную задержку типа Sleep() или же в цикле через 100 мс опрашивать CallBack на предмет получил он уже ответ или нет, но в этом случае по сути запрос становится как обычный, ведь я циклом занимаю основной поток сервера, и торможу его, дожидаясь ответа от базы. Или нет?
Смотрел уже реализации поточных запросов в плагинах Levels Ranks, Vip модуль Test Vip, MA, но пока не увидел особой обработки обратных вызовов. Как правило просто в функции обратного вызова заполняют переменные или DataPack и далее уже в других функциях сразу же обрабатывают эти данные. Может я что-то проглядел?
Подскажите, как правильно стоит обрабатывать такие задержки, что бы они не сказывались на "гладкости" игрового процесса и не мешали игрокам?
 
Последнее редактирование:

Kruzya

Супермодератор
Сообщения
7,434
Реакции
5,689
как правильно обработать задержку при получении данных, что бы это не начало вызывать фризы на сервере? Данные из БД нужны не для вывода в лог или показу пользователю, а для принятия решения при коннекте игрока к серверу.
Никак. Забудьте. Такие каллбеки требуют моментальной реакции.
Наиболее правильным решением будет уйти на OnClientAuthorized() и использовать асинхронщину.

Как правило просто в функции обратного вызова заполняют переменные или DataPack и далее уже в других функциях сразу же обрабатывают эти данные.
Потому что иного, правильного способа обработать результат - нет.
SourceMod не запускает несколько "асинхронных" запросов одновременно. Он их сначала складывает в очередь, а потом запускает уже поочерёдно. На все плагины - одна, общая очередь.
Если какой-то плагин "наспамил" запросами в очередь, Ваш может неизвестно сколько ждать, пока будет исполнен.

Потому всю обработку и выносят в обратный вызов.
 
Сообщения
3
Реакции
0
Каким образом в sourcemod реализуются очереди задач и выдаются прерывания? Например, когда работает несколько плагинов, то каким образом между ними распределяется процессорное время внутри самого процесса Srcds.exe. На каком моменте может быть приостановлено выполнение плагина для того, чтобы передать расчётные мощности другим плагинам? Ведь по сути и сама игра и плагины работают в одном потоке, но при этом они исполнятся асинхронно. Значит существуют ещё и внутренние механизмы многозадачности.
Должен ли код плагина полностью исполнится и только потом он передаст освободившиеся ресурсы остальным плагинам или нет? Если Вам не сложно, то можете мне объяснить эти механизмы или дать ссылку, где об этом можно почитать.

При отслеживании событий форворда OnClientAuthorized() если сразу подклчюается несколько клиентов, то как будет обрабатывать их плагин. Параллельно или в начале полностью закончит с одним клиентом, а потом уже займётся другим?
 

Kruzya

Супермодератор
Сообщения
7,434
Реакции
5,689
Каким образом в sourcemod реализуются очереди задач и выдаются прерывания?
Понятия "прерывания" в SourceMod нет.

Например, когда работает несколько плагинов, то каким образом между ними распределяется процессорное время внутри самого процесса Srcds.exe
Это посложнее объяснить, да и это выходит за рамки этого текста.
Единицей времени в движке Source считается "кадр" (он же тик). Кол-во вырабатываемых кадров зависит от настроенного тикрейта и способностей железки.
Поскольку SourceMod позволяет хукать любые события игры, он вынужден так же зависеть от этих самых кадров.
Здесь мы понемногу подходим к самой идее того, как работает SourceMod. Он вызывает функции плагинов при обработке любого кадра. Если рассматривать "асинхронную работу базами данных", то SM выполняет запрос в другом потоке и складывает информацию о завершении с результатами - в некий "стек", который разбирается при обработке кадра: вызываются обработчики плагинов, им передаются нужные им данные.
JIT SourcePawn'а не нацелен на "параллельное выполнение" нескольких функций одновременно, потому обработчики плагинов вызываются строго последовательно.

При отслеживании событий форворда OnClientAuthorized() если сразу подклчюается несколько клиентов, то как будет обрабатывать их плагин. Параллельно или в начале полностью закончит с одним клиентом, а потом уже займётся другим?
Такого не произойдёт, конечно же. В один тик - навряд ли.
А так - последовательно: сначала один, потом - другой.
 
Сообщения
21
Реакции
1
Никак. Забудьте. Такие каллбеки требуют моментальной реакции.
Наиболее правильным решением будет уйти на OnClientAuthorized() и использовать асинхронщину.
А что это дает, ведь пока завершится асинхронный запрос, юзер уже пройдет стадию авторизации или можно как то принудить клиента ожидать принятия решения?
И как это реализовано в том же sourcebans?

Пару вопросов еще, если можно.
1. В каком случае оправдано (либо не обойтись) без использования не поточных (синхронных) запросов?

2. Правильно ли я понимаю, операция блокировки бд приостанавливает работу основного потока, пока не будет завершена текущая (либо все?) поточные запросы к бд (в т.ч.) от контекста других плагинов?

Кто владелец блокировки, т.е. в теории можно ли разблокировку выполнить из под контекста другого плагина?

Где/кто хранит состояние блокировки? Т.е. если в теории плагин будет внезапно выгружен/ нарвется на setfailstate до разблокировки бд, произойдет ли дедлок на физически другой машине, которая юзает эту бд через синхронные запросы?

Из вики:
Leaving a lock on a database and then executing a threaded query results in a dead lock! Make sure to call SQL_UnlockDatabase()!

Не совсем понимаю логику написанного. ,Плагин отправивший асинхронный запрос ведь априори ничего дожидаться (приостанавливать поток) не будет. Дедлок должен произойти при очередном не поточном (синхронном) запросе.
 

Kruzya

Супермодератор
Сообщения
7,434
Реакции
5,689
можно как то принудить клиента ожидать принятия решения?
И как это реализовано в том же sourcebans?
В форварде OnClientPreAdminCheck() вернуть Plugin_Stop, вроде. В доку глянуть не могу (с телефона), но там написано, что вернуть для блокировки Post-форварда.
Потом, когда сделаете что нужно, вызываете NotifyPostAdminCheck(). Название тоже не точное, в доке найдете.

1. В каком случае оправдано (либо не обойтись) без использования не поточных (синхронных) запросов?
Когда есть некий хендл, который удалится по завершению выполнения функции, и клонировать не представляется возможным.

2. Правильно ли я понимаю, операция блокировки бд приостанавливает работу основного потока, пока не будет завершена текущая (либо все?) поточные запросы к бд (в т.ч.) от контекста других плагинов?
Операция блокировки БД попросту не дает самому SM начать выполнение любых асинхронных запросов.
Это делается во избежание проблем, когда в один коннект запихивается еще один запрос, когда ответа по другому все еще нет.

Кто владелец блокировки, т.е. в теории можно ли разблокировку выполнить из под контекста другого плагина?
Если в этом же потоке - можно.

Где/кто хранит состояние блокировки?
Сам драйвер БД хранит, кажется.

Т.е. если в теории плагин будет внезапно выгружен/ нарвется на setfailstate до разблокировки бд, произойдет ли дедлок на физически другой машине, которая юзает эту бд через синхронные запросы?
Блокировка базы производится чисто логическая. Удаленная база не блокируется.

Leaving a lock on a database and then executing a threaded query results in a dead lock! Make sure to call SQL_UnlockDatabase()!

Не совсем понимаю логику написанного. ,Плагин отправивший асинхронный запрос ведь априори ничего дожидаться (приостанавливать поток) не будет. Дедлок должен произойти при очередном не поточном (синхронном) запросе.
Плагин, отправляя асинхронный запрос, на самом деле, ничего не отправляет. Запрос уходит в очередь. Один из воркеров СМ разбирает ее, постоянно вешая лок перед выполнением очередного запроса, и так же снимая по завершению.
Поэтому локать базу при выполнении однопоточных и необходимо. Сам SM не проверяет, занята база или нет.
 
Сверху