Краш при использовании wait в

Downesa

Известный
Автор темы
345
54
Версия MoonLoader
.026-beta
Хотел изначально сделать скрипт чтобы получал уведы об отелях, когда у них появляются места, в итоге в начале добавляю проверку на lvl, пришлось её сделать циклом, потому что несколько раз выдавало 0 при обращении к sampGetPlayerScore, в итоге крашит, попробовал через сделать кастомный делей с помощью функции c_sleep, вот что получилось, но опять ловлю краши игры, может в чём-то ещё ошибка, крашит когда срабатывает кастомный евент. Но почему-то когда пробовал триггерить другой текст, чтобы выполнить функционал, краша не было.

Код проверки на payday:
local ffi = require("ffi")
require "sampfuncs"
require 'moonloader'
local events = require 'samp.events'

local snet = require "snet"
local bstream = snet.bstream
local encoding = require("encoding")
encoding.default = 'CP1251'
local u8 = encoding.UTF8
local effil = require("effil")

function split(str, delim, plain)
    local tokens, pos, plain = {}, 1, not (plain == false)
    repeat
        local npos, epos = string.find(str, delim, pos, plain)
        table.insert(tokens, string.sub(str, pos, npos and npos - 1))
        pos = epos and epos + 1
    until not pos
    return tokens
end

function requestRunner()
    return effil.thread(function(u, a)
        local https = require 'ssl.https'
        local ok, result = pcall(https.request, u, a)
        if ok then
            return { true, result }
        else
            return { false, result }
        end
    end)
end

function threadHandle(runner, url, args, resolve, reject)
    local t = runner(url, args)
    local r = t:get(0)
    while not r do
        r = t:get(0)
        wait(0)
    end
    local status = t:status()
    if status == 'completed' then
        local ok, result = r[1], r[2]
        if ok then resolve(result) else reject(result) end
    elseif err then
        reject(err)
    elseif status == 'canceled' then
        reject(status)
    end
    t:cancel(0)
end

function async_http_request(url, args, resolve, reject)
    local runner = requestRunner()
    if not reject then reject = function() end end
    lua_thread.create(function()
        threadHandle(runner, url, args, resolve, reject)
    end)
end

function encodeUrl(str)
    str = str:gsub(' ', '%+')
    str = str:gsub('\n', '%%0A')
    return u8:encode(str, 'CP1251')
end

function closeDialog()
    for i = 1, 3 do
        sampCloseCurrentDialogWithButton(0)
        wait(30)
    end
end

function forechText(text, f)
    local list = (sampGetCurrentDialogType() == 5 and -1 or 0)
    for l in text:gmatch("[^\n]+") do
        f(l, list)
        list = list + 1
    end
end

function selectListDialogWithText(text)
    if text == nil then return end
    local list = (sampGetCurrentDialogType() == 5 and -1 or 0)
    for l in sampGetDialogText():gmatch("[^\n]+") do
        if l:find(text) then
            sampSendDialogResponse(sampGetCurrentDialogId(), 1, list, _)
            sampCloseCurrentDialogWithButton(0)
            return
        end
        list = list + 1
    end
end

local c_vars = {
    telegram = {
        telegram_token = "",
        chat_group_id = "",
        message_thread_id = 0,
    },
    sleep = false
}



function telegram_sendMessage(msg)
    msg = encodeUrl(msg)
    async_http_request(
        'https://api.telegram.org/bot' ..
        tostring(c_vars.telegram.telegram_token) ..
        '/sendMessage?chat_id=' ..
        tostring(c_vars.telegram.chat_group_id) ..
        '&message_thread_id=' ..
        tostring(c_vars.telegram.message_thread_id) .. '&text=' .. msg .. "&parse_mode=HTML", '',
        function(result) end)
end

function c_sleep(sleep_time)
    lua_thread.create(function()
        c_vars.sleep = true
        wait(sleep_time)
        c_vars.sleep = false
    end)
end

function get_hotels_data()
    lua_thread.create(function()
        local hotels, hotel_finded = {
            ["Four Seasons"] = { -2172.458984375, 252.12530517578, 35.339500427246 },
            ["Hotel Business"] = { -2426.2446289063, 338.02389526367, 36.992198944092 },
        }, { status = false }
        while not sampIsDialogActive() do
            sampSendChat("/hotel")
            wait(250)
        end
        while not sampGetDialogCaption():find("%{BFBBBA%}%{FF6666%}Отель") do wait(0) end
        forechText(sampGetDialogText(), function(l, item)
            local hotel, free, dist = l:match("%d+%.%s*%{......%}%s*\'(.-)\'\t%{......%}(%S+) номеров\t%{......%}(%d+)")

            if dist ~= nil and hotels[hotel] ~= nil and free ~= "0" then
                hotel_finded = { status = true, hotel = hotel }
                dist = dist:gsub("%p", "")
                sampSendDialogResponse(sampGetCurrentDialogId(), 1, item, "")
                sampCloseCurrentDialogWithButton(0)
                return
            end
        end)

        wait(300)

        if hotel_finded.status then
            local SERVER_NAME = split(sampGetCurrentServerName():lower(), "|")[2]
            SERVER_NAME = (SERVER_NAME:lower()):gsub("%p", ""):gsub(" ", "")
            if false then  --SERVER_NAME ~= 'drake' and SERVER_NAME ~= 'love' and SERVER_NAME ~= 'tucson' or
                sampProcessChatInput("/tpz cpm")
            end

            telegram_sendMessage(
                "[PayDay]\nОсвободился отель и тпнулся до отеля <b>" ..
                hotel_finded.hotel .. "</b>\nСервер: <code>" .. sampGetCurrentServerName() .. " </code>"
            )
        else
            closeDialog()
        end
    end)
end

function get_my_lvl()
    local _, id = sampGetPlayerIdByCharHandle(PLAYER_PED)
    local attempts = 8
    local score = 0


    for i = 1, attempts do
        score = tonumber(sampGetPlayerScore(id))
        if score ~= 0 then
            return score
        end
        c_sleep(500)
        while c_vars.sleep do
            wait(0)
        end
    end

    return 0
end

function PAYDAY_event()
    if not isSampAvailable() or not sampIsLocalPlayerSpawned() then
        print("[PAYDAY_event] >> Error #1")
        return
    end
    local currentTime = os.time()
    local time_tbl = os.date("*t", currentTime)
    if tonumber(time_tbl.hour) ~= 0 then
        print("[PAYDAY_event] >> Error check time")
        return
    end

    get_hotels_data()
end

function events.onServerMessage(color, text)
    if text:find("Банковский чек") and not text:find("говорит") then
        lua_thread.create(function()
            local my_lvl = get_my_lvl()
            if my_lvl > 7 then
                PAYDAY_event()
                print("Payday event active")
            else
                print("Payday event skip checking, lvl < 8")
            end
        end)
    end
end
 

Vintik

Через тернии к звёздам
Проверенный
1,562
1,033
Привет.

1. В функции function events.onServerMessage(color, text) нельзя использовать задержки (только в отдельном lua_thread). Функция сделана так, что она перехватывает события и позволяет изменять данные (в данном случае — можно изменить текст сообщения). Поэтому при срабатывании события вызывается функция и ожидается, пока она завершится, а потом игра продолжается. Чтобы вся игра полностью не зависала — сделано ограничение, запрещающее использовать wait() внутри функции. Отдельный поток (lua_thread) может использовать задержки, но тогда игра не ожидает его завершения (в момент его создания программа раздваивается: в основном потоке функция events.onServerMessage завершается без задержек, а параллельно выполняется второй поток — и там уже разрешены wait()), а следовательно изменить перехваченные данные в новом lua_thread невозможно.

2. В этом коде в подсвеченной строке ошибка:
Lua:
for i = 1, attempts do
    score = tonumber(sampGetPlayerScore(id))
    if score ~= 0 then
        return score
    end
    c_sleep(500)
    while c_vars.sleep do
        wait(0)
    end
end
Функция c_sleep() создаёт новый поток параллельно (т. е. раздваивает программу), не останавливая основную (смотри подсвеченные строки):
Lua:
function c_sleep(sleep_time)
    lua_thread.create(function()
        c_vars.sleep = true
        wait(sleep_time)
        c_vars.sleep = false
    end)
end
По сути, «засыпает» новый поток, а старый сразу продолжается, поэтому все итерации цикла пройдут мгновенно.

3. Уверен, что hour? Может, min? Если проверка идёт на 00 минут.
Lua:
local time_tbl = os.date("*t", currentTime)
if tonumber(time_tbl.hour) ~= 0 then
    print("[PAYDAY_event] >> Error check time")
    return
end

4. Откуда берутся аргументы (u, a) тут?
Lua:
function requestRunner()
    return effil.thread(function(u, a)
        local https = require 'ssl.https'
        local ok, result = pcall(https.request, u, a)
        if ok then
            return { true, result }
        else
            return { false, result }
        end
    end)
end

Дальше я запутался. Я бы на твоём месте функцию telegram_sendMessage() заменил на print() и проверил работоспособность. До этого момента всё должно работать более менее. Кажется, что жопа начинается именно с этой функции.

Я бы на твоём месте использовал простейшую downloadUrlToFile(url, file, callback) вот так:
Lua:
downloadUrlToFile(
    'https://api.telegram.org/bot' ..
    tostring(c_vars.telegram.telegram_token) ..
    '/sendMessage?chat_id=' ..
    tostring(c_vars.telegram.chat_group_id) ..
    '&message_thread_id=' ..
    tostring(c_vars.telegram.message_thread_id) .. '&text=' .. msg .. "&parse_mode=HTML"
, '', function()
    if status == dlstatus.STATUS_ENDDOWNLOADDATA then
        print('Запрос в API Telegram отправлен.')
    end
end)
Функция стандартная и не требует дополнительных библиотек.
 

Downesa

Известный
Автор темы
345
54
Привет.

1. В функции function events.onServerMessage(color, text) нельзя использовать задержки (только в отдельном lua_thread). Функция сделана так, что она перехватывает события и позволяет изменять данные (в данном случае — можно изменить текст сообщения). Поэтому при срабатывании события вызывается функция и ожидается, пока она завершится, а потом игра продолжается. Чтобы вся игра полностью не зависала — сделано ограничение, запрещающее использовать wait() внутри функции. Отдельный поток (lua_thread) может использовать задержки, но тогда игра не ожидает его завершения (в момент его создания программа раздваивается: в основном потоке функция events.onServerMessage завершается без задержек, а параллельно выполняется второй поток — и там уже разрешены wait()), а следовательно изменить перехваченные данные в новом lua_thread невозможно.

2. В этом коде в подсвеченной строке ошибка:
Lua:
for i = 1, attempts do
    score = tonumber(sampGetPlayerScore(id))
    if score ~= 0 then
        return score
    end
    c_sleep(500)
    while c_vars.sleep do
        wait(0)
    end
end
Функция c_sleep() создаёт новый поток параллельно (т. е. раздваивает программу), не останавливая основную (смотри подсвеченные строки):
Lua:
function c_sleep(sleep_time)
    lua_thread.create(function()
        c_vars.sleep = true
        wait(sleep_time)
        c_vars.sleep = false
    end)
end
По сути, «засыпает» новый поток, а старый сразу продолжается, поэтому все итерации цикла пройдут мгновенно.

3. Уверен, что hour? Может, min? Если проверка идёт на 00 минут.
Lua:
local time_tbl = os.date("*t", currentTime)
if tonumber(time_tbl.hour) ~= 0 then
    print("[PAYDAY_event] >> Error check time")
    return
end

4. Откуда берутся аргументы (u, a) тут?
Lua:
function requestRunner()
    return effil.thread(function(u, a)
        local https = require 'ssl.https'
        local ok, result = pcall(https.request, u, a)
        if ok then
            return { true, result }
        else
            return { false, result }
        end
    end)
end

Дальше я запутался. Я бы на твоём месте функцию telegram_sendMessage() заменил на print() и проверил работоспособность. До этого момента всё должно работать более менее. Кажется, что жопа начинается именно с этой функции.

Я бы на твоём месте использовал простейшую downloadUrlToFile(url, file, callback) вот так:
Lua:
downloadUrlToFile(
    'https://api.telegram.org/bot' ..
    tostring(c_vars.telegram.telegram_token) ..
    '/sendMessage?chat_id=' ..
    tostring(c_vars.telegram.chat_group_id) ..
    '&message_thread_id=' ..
    tostring(c_vars.telegram.message_thread_id) .. '&text=' .. msg .. "&parse_mode=HTML"
, '', function()
    if status == dlstatus.STATUS_ENDDOWNLOADDATA then
        print('Запрос в API Telegram отправлен.')
    end
end)
Функция стандартная и не требует дополнительных библиотек.
1. В функции function events.onServerMessage(color, text) нельзя использовать задержки (только в отдельном lua_thread).
Внимательно посмотри код, в евенте не используется задержки.
Дальше я запутался. Я бы на твоём месте функцию telegram_sendMessage() заменил на print() и проверил работоспособность. До этого момента всё должно работать более менее. Кажется, что жопа начинается именно с этой функции.
Краш идёт игры, а не именно скрипта, не могу получить ошибку.
 

Vintik

Через тернии к звёздам
Проверенный
1,562
1,033
Краш идёт игры, а не именно скрипта, не могу получить ошибку.
Ты сделал то, что я тебе сказал?
Заменил отправку в телеграм на просто print() в консоль?
Краш остался?
 

Downesa

Известный
Автор темы
345
54
Ты сделал то, что я тебе сказал?
Заменил отправку в телеграм на просто print() в консоль?
Краш остался?
Посмотри внимательно код и поймёшь что отправка обычного http запроса не влияет на краш.
 

Downesa

Известный
Автор темы
345
54
нужно меньше потоков использовать, это одна из частых ошибок при работе с запросами
А теперь внимательно посмотри код и прочитай что изначально написано в посте.