Некорректная загрузка из БД

Сообщения
73
Реакции
8
Всем привет.
Использую плагин для хранения игровой валюты в базе данных.
Код:
#include <amxmodx>
#include <sqlx>

new const TASKID = 11575757;

new bool:g_isLoad[33];

native zp_get_user_ammo_packs(id);
native zp_set_user_ammo_packs(id, amount);

enum _:ques { INSTALL_PLUGIN, LOAD_AMMO, SAVE_AMMO, PRUNED };
new Handle:g_sql_tuple, szQuery[1024], data[3];

public plugin_init()
    set_task(1.0, "PluginCfg");

public PluginCfg()
{
    new szHost[64], szUser[32], szPasswd[32], szDb[32];
    get_cvar_string("amx_sql_host", szHost, charsmax(szHost));
    get_cvar_string("amx_sql_user", szUser, charsmax(szUser));
    get_cvar_string("amx_sql_pass", szPasswd, charsmax(szPasswd));
    get_cvar_string("amx_sql_db", szDb, charsmax(szDb));
    
    g_sql_tuple = SQL_MakeDbTuple(szHost, szUser, szPasswd, szDb);
    
    data[0] = INSTALL_PLUGIN;
    formatex(szQuery, charsmax(szQuery), "CREATE TABLE IF NOT EXISTS `zp_ammo_manager` (`id` int(11) NOT NULL AUTO_INCREMENT, `Name` varchar(33) NOT NULL, `Ammo` int(10) NOT NULL, `SteamID` varchar(25) NOT NULL UNIQUE, `LastConnect` int(11) NOT NULL, PRIMARY KEY (`id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8;");
    SQL_ThreadQuery(g_sql_tuple, "SQL_Handler", szQuery, data, sizeof(data));
    
    SQL_SetCharset(g_sql_tuple, "utf8");
    
    data[0] = PRUNED;
    formatex(szQuery, charsmax(szQuery), "DELETE FROM `zp_ammo_manager` WHERE `LastConnect` < (UNIX_TIMESTAMP(NOW()) - %d)", (30 * 86400)); // delete players after 30 days
    SQL_ThreadQuery(g_sql_tuple, "SQL_Handler", szQuery, data, sizeof(data));
}

public client_connect(id)
    g_isLoad[id] = false;

public client_putinserver(id)
{
    if(is_user_bot(id) || is_user_hltv(id)) return;
    
    zp_set_user_ammo_packs(id, 0);
    set_task(3.0, "LoadAmmo", id+TASKID);
}

public LoadAmmo(taskid)
{
    new id = taskid - TASKID;
    
    if(!is_user_connected(id) || g_isLoad[id]) return;
    
    data[0] = LOAD_AMMO;
    data[1] = id;
    data[2] = get_user_userid(id);
    
    new szAuthid[25]; get_user_authid(id, szAuthid, charsmax(szAuthid));
    
    formatex(szQuery, charsmax(szQuery), "SELECT * FROM `zp_ammo_manager` WHERE `SteamID` = '%s'", szAuthid);
    SQL_ThreadQuery(g_sql_tuple, "SQL_Handler", szQuery, data, sizeof(data));
}

public client_disconnected(id)
{
    if(is_user_bot(id) || is_user_hltv(id) || !g_isLoad[id]) return;
    
    remove_task(id+TASKID);
    
    new ammo = zp_get_user_ammo_packs(id);
    
    new szName[32];        get_user_name(id, szName, charsmax(szName));
    new szAuthid[25];    get_user_authid(id, szAuthid, charsmax(szAuthid));
    
    new szNewName[64];
    SQL_QuoteString(Empty_Handle, szNewName, charsmax(szNewName), szName);
    
    formatex(szQuery, charsmax(szQuery), "INSERT INTO `zp_ammo_manager` (`Name`, `Ammo`, `SteamID`, `LastConnect`) \
    VALUES ('%s', '%d', '%s', UNIX_TIMESTAMP(NOW())) \
    ON DUPLICATE KEY UPDATE `Name` = '%s', `Ammo` = '%d', `LastConnect` = UNIX_TIMESTAMP(NOW())",
    szNewName, ammo, szAuthid, szNewName, ammo);
    
    SQL_ThreadQuery(g_sql_tuple, "SQL_Handler", szQuery);
    
    g_isLoad[id] = false;
}

public SQL_Handler(failstate, Handle:query, err[], errcode, data[], datasize)
{
    if(failstate == TQUERY_SUCCESS)
    {
        switch(data[0])
        {
            case LOAD_AMMO:
            {
                new id = data[1];
                
                if(!is_user_connected(id) || g_isLoad[id]) return 0;
                
                if(data[2] != get_user_userid(id))
                {
                    log_amx("[Ammo Manager] Загрузка другому игроку");
                    return 0;
                }
                
                new szName[32]; get_user_name(id, szName, charsmax(szName));
                
                if(SQL_NumResults(query) > 0)
                {
                    zp_set_user_ammo_packs(id, SQL_ReadResult(query, 3));
                    log_amx("[Ammo Manager] Загружено %d аммо для игрока %s", SQL_ReadResult(query, 3), szName);
                }
                else // first connect
                {
                    zp_set_user_ammo_packs(id, 50); // start bonus
                    log_amx("[Ammo Manager] Игрок %s получил стартовый бонус", szName);
                }
                
                g_isLoad[id] = true;
            }
            case PRUNED:
            {
                new pruned = SQL_AffectedRows(query);
                if(pruned) log_amx("[Pruned] Removed %d players!", pruned);
            }
            case INSTALL_PLUGIN, SAVE_AMMO: { }
        }
    }
    else    log_amx("[State #%d] Error [#%d] %s", data[0], errcode, err);
    return PLUGIN_CONTINUE;
}

public plugin_end()
    SQL_FreeHandle(g_sql_tuple);

К сожалению, за время использования плагина было выявлено 2 странных ситуации, исправить которые не знаю как:
ps баги появляются достаточно редко

  • перед установкой игроку валюты, проверяю, есть ли такой игрок в бд (109 строка) - иногда запрос почему-то проскакивает, и игрок получает стартовый бонус вместо того значения, которое записано под его steamid в бд.
  • при загрузке валюты из бд может придти 2 запроса в один момент (в консоли отображается 2 раза логирование 112й строки)

Прошу помощи в виде каких-либо подсказок по изменению кода - может что-то криво сделано, устарело и т.п.
 

Ayk

Сообщения
763
Реакции
478
Помог
19 раз(а)
To_be_or_not_to_be, в 84 не передается data.
7 Янв 2021
А еще этот data может измениться между вызовом и выполнением, ибо глобален и юзается трэд-квери.
 
Сообщения
1,701
Реакции
1,512
Помог
26 раз(а)
А еще этот data может измениться между вызовом и выполнением, ибо глобален и юзается трэд-квери.
он ж передается в хендлер.

А вообще нового юзера обычно создают и снова получают шоб он точно лоад был и просто с бд деф.значения взяло в первый раз.
 
Сообщения
272
Реакции
282
Помог
2 раз(а)
Почему не используешь тернарный оператор?
 
Сообщения
1,701
Реакции
1,512
Помог
26 раз(а)
Сообщения
272
Реакции
282
Помог
2 раз(а)
fl0wer,
Твой код:
Код:
if(SQL_NumResults(query) > 0)
{
    zp_set_user_ammo_packs(id, SQL_ReadResult(query, 3));
    log_amx("[Ammo Manager] Загружено %d аммо для игрока %s", SQL_ReadResult(query, 3), szName);
}
else // first connect
{
    zp_set_user_ammo_packs(id, 50); // start bonus
    log_amx("[Ammo Manager] Игрок %s получил стартовый бонус", szName);
}
Мой вариант:
Код:
zp_set_user_ammo_packs(id, SQL_NumResults(query) > 0 ? SQL_ReadResult(query, 3) : 50);
И всё, if else не нужны, условие в одну строку, с log_amxx сам подумай как сделать.
 
Сообщения
1,701
Реакции
1,512
Помог
26 раз(а)
Arni, не мой) а лог? Вообще как я выше написал надо записать нового игрока и снова взять его новым запросом.
 
Сообщения
272
Реакции
282
Помог
2 раз(а)
fl0wer,
Я с MySQL дружу с самого начала и скажу так, чтобы получить твоего пользователя после запроса, например:
- запрос на добавления нового пользователя в БД.
То можно обратится к бд, чтобы он вернул твой ID:
- SELECT LAST_INSERT_ID()

И совет, не пиши (`) каждый раз. Он лишний.
 
Сообщения
272
Реакции
282
Помог
2 раз(а)
fl0wer, Почему оно не работает? Скачай для тестов HeidiSQL и тести там свои запросы:

Процесс работы запросов:
insert ...
select last_insert_id()
Твои действия, например:
update
 
Сообщения
1,701
Реакции
1,512
Помог
26 раз(а)
Arni, я про мускул что в амхх.
 
Сообщения
272
Реакции
282
Помог
2 раз(а)
fl0wer, пиши пожалуйста по-человечески, а то не догоняю иногда тебя с "волшебными" словами.
8 Янв 2021
fl0wer, это dll или inc, что работает с БД должен только запросы отправлять и получать. Любой запрос, связанный с БД MySQL, должен работать.
8 Янв 2021
fl0wer, отпишись, как проверишь.
 
Сообщения
1,032
Реакции
828
Помог
10 раз(а)
Arni, SQL_GetInsertId в амх нативка есть тоже, но не проверял
8 Янв 2021
Почему не используешь тернарный оператор?
чисто для того что бы повыпендирваться?

А если я отправляю одним запросом сразу 5 запросов на добавление строк, какой id он мне вернет? Я думаю только последний

И совет, не пиши (`) каждый раз. Он лишний.
Чем он помешал? Свои вкусы навязываем?
 
Сообщения
272
Реакции
282
Помог
2 раз(а)
Javekson, с каких пор оптимизации - это выпендрёж? Глупо говорить подобное. У вас в названии таблиц и бд пробелы в названи?? Если говорить о запросах, вы пишите бред. Через цикл всё спокойно прорабатывается. Никто не мешает создавать второе подключение.
 
Сообщения
1,032
Реакции
828
Помог
10 раз(а)
с каких пор оптимизации - это выпендрёж?
оптимизация на чем? На спичках? В данной ситуации я бы не сказал, что она нужна вообще, ломать интерфейс если только.

У вас в названии таблиц и бд пробелы в названи??
И что теперь? Значит не рекомендуется их использовать? Здесь этот совет тоже считаю не уместным.

Никто не мешает создавать второе подключение.
Плодить кучу подключений такое себе удовольствие я считаю, оптимизацию лучше проводить в этом направлении чем в условиях
 
Сообщения
1,032
Реакции
828
Помог
10 раз(а)
перед установкой игроку валюты, проверяю, есть ли такой игрок в бд (109 строка) - иногда запрос почему-то проскакивает, и игрок получает стартовый бонус вместо того значения, которое записано под его steamid в бд.
ошибок в этот момент никаких не было с запросами?

при загрузке валюты из бд может придти 2 запроса в один момент (в консоли отображается 2 раза логирование 112й строки)
Точно в один момент? Или с разницей в три секунды?
 
Сообщения
272
Реакции
282
Помог
2 раз(а)
Javekson, если ты не можешь реализовать запрос по человеку, то даже не берись. Можно спокойно стим айди и д. использовать. Запросы через через таск выполнять. Тебя никто не просит подключать 32 соединения. Хватит 2. Смысла мне отвечать дальше нет.
 
Сообщения
333
Реакции
290
Помог
9 раз(а)
data[3] делайте локально для каждой функции
 

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу