SA:MP lua51.dll

БеzликиЙ

Автор темы
Автор темы
Проверенный
1,844
1,084
Ебусь тут чутка с copas во имя загрузки картинок в arizona-mimgui. Загрузка картинок работает.

1770396899055.png

Проблема в том, что спустя какое-то время без видимой причины игра берёт и крашится с вот таким трейсом:

1770396985200.png
(нажмите "Да", чтобы посмотреть полный репорт)

По словам Фупа, copas - лучшая из возможных реализаций для асинхронного HTTP/S взаимодействия в Lua. Куда копать?

Аналогичный краш: https://www.blast.hk/threads/79871/

Проблема в том, что у меня нет потоков в хуках. У меня потоки создаются в шести местах:
- строка 65, в функции установки КД для подсказки keyboardHint (аккуратно удаляется при следующем вызове или по завершению задержки)
- строка 83 (такой же)
- строка 99 (аналогично)
- строка 114 (он же)
- строка 206 (main loop библиотеки copas, идёт не более одного раза, потому что напрямую зависит от переменной copas.running)
- строка 984 (поток, в котором загружается изображение - создаётся по одному на картинку в момент её загрузки, очевидно, проблема где-то тут, но она не должна происходить от всего 32 картинок??!?)

Немного поигрался с картинками, во имя реализации инвентаря вынес шестой поток в очередь, ну и добавил main() для собственно обработки этой очереди. Итого у меня на весь скрипт всего семь потоков - main и шесть lua_thread.

Последний такой краш произошёл при загрузке всего ТРЁХ!!! картинок.
1770482846975.png

Нет, я не идиот, я проверяю, запущен ли поток загрузки в очереди, перед тем, как пускать туда новый.
 
Последнее редактирование:
  • Вау
Реакции: pastow

БеzликиЙ

Автор темы
Автор темы
Проверенный
1,844
1,084
Пробуй юзать мьютекс, подозреваю, что у тебя нет синхронизации потока

Также ограничить кол-во выполняемых потоков
уже всё поограничивал, сколько можно, сами потоки друг с другом никак не взаимодействуют, только отслеживают состояние запуска друг у друга. уже начинаю грешить на то, что игре банально не хватает оперативы на 150 пнг иконок содержимого моего инвентаря

нет, дело определённо в потоках. если вручную скачать все 9790 существующих на данный момент картинок предметов, всё заебись, только фпс проседает до пяти из-за обилия картинок
1770512839217.png


в исходной теме есть комментарий "не запускай потоки так часто":


между потоками загрузки картинок у меня задержка, одна секунда между завершением старого потока и появлением нового 🥴
 
Последнее редактирование:

kernelich

Участник
17
14
Попробуй через библиотеку effil.
Что то на подобии этого.


lua:
function requestRunner()
    return effil.thread(function(httpMethod, url, requestBody)
        local requestLib = require("requests")
        
        local success, response = pcall(requestLib.request, httpMethod, url, {data = requestBody})

        if success then
            response.json, response.xml = nil
            return true, response
        else
            return false, response
        end
    end)
end
function handleAsyncHttpRequestThread(requestThread, successCallback, errorCallback)
    local threadStatus
    local threadError

    repeat
        threadStatus, threadError = requestThread:status()
        wait(0)
    until threadStatus ~= "running"

    if not threadError then
        if threadStatus == "completed" then
            local requestSuccess, response = requestThread:get()

            if requestSuccess then
                successCallback(response)
            else
                errorCallback(response)
            end
            return
        elseif threadStatus == "canceled" then
            return errorCallback(threadStatus)
        end
    else
        return errorCallback(threadError)
    end
end
function asyncHttpRequest(httpMethod, url, requestBody, successCallback, errorCallback)
    local requestThread = requestRunner()(httpMethod, url, requestBody)

    successCallback = successCallback or function() return end
    errorCallback = errorCallback or function() return end

    return {
        effilRequestThread = requestThread,
        luaHttpHandleThread = lua_thread.create(handleAsyncHttpRequestThread, requestThread, successCallback, errorCallback)
    }
end

gui.WebImage = function(url, size)
    local iid = string.gsub(url, ".*arizona%-rp", "")
    local cachepath = cachedir .. iid

    if not imagesbuffer[iid] then
        if doesFileExist(cachepath) then
            imagesbuffer[iid] = gui.CreateTextureFromFile(cachepath)
        elseif not imagesthreads[iid] then
            imagesthreads[iid] = true
            
            asyncHttpRequest("GET", url, nil,
                function(response)
                    if response.status_code == 200 then
                        local dir = cachepath:match("(.*[/\\])")
                        if dir and not doesDirectoryExist(dir) then createDirectory(dir) end

                        local f = io.open(cachepath, 'wb')
                        if f then
                            f:write(response.text)
                            f:close()
                            imagesbuffer[iid] = gui.CreateTextureFromFile(cachepath)
                        end
                    end
                    imagesthreads[iid] = false
                end,
                function(err)
                    print("Error: " .. tostring(err))
                    imagesthreads[iid] = false
                end
            )
        end
    end

    if imagesbuffer[iid] then
        gui.Image(imagesbuffer[iid], size)
    else
        gui.Dummy(size)
    end
end
 

constersuonsis

Известный
249
152
Где то с полгода назад столкнулся с такой проблемой и в самп и в раксамп. Возможно изобрёл велосипед, но оно работает стабильно, учитывая что запросы отправляются каждые 3 сек и бывает пачками.
???:
local http = require("socket.http")
local ltn12 = require("ltn12")
local requests = require("requests")
local cjson = require("cjson")
local socket = require("socket")

-- usage:
-- local temp_headers = {
--        ["Content-Type"] = "application/json"
-- }
-- local r = handleResponse("https://api.???.su/", "POST", cjson.encode({}), temp_headers)

function async_http_request(url, request_data, callback, proxy)
    coroutine.wrap(function()
        local request_params = {
            url = url,
            method = "POST",
            source = ltn12.source.string(request_data),
            headers = {
                ["Content-Type"] = "application/json",
                ["Content-Length"] = tostring(#request_data)
            },
            sink = ltn12.sink.string
        }
        if proxy then
            request_params.proxy = proxy
        end

        local response_body, status_code = http.request(request_params)

        if callback then
            if status_code then
                callback(true, response_body, status_code)
            else
                callback(false, nil, nil)
            end
        end
    end)()
end

function async_request(url, method, request_data, headers, proxy)
    coroutine.wrap(function()
        local options = {
            url = url,
            data = request_data,
            headers = headers
        }
        if proxy then
            options.proxy = proxy
        end

        local success, response = pcall(requests.request, method, options)

        if success and response then
            print(response.status_code, url, success)
            if response.status_code == 200 then
                print("Success: " .. response.text .. " | URL: " .. url)
                return true
            else
                print("Error: Status Code: " .. tostring(response.status_code) .. " | URL: " .. url .. " | Body: " .. tostring(response.text))
                return false
            end
        else
            print("ERROR: Request failed. URL: " .. url .. " | Error: " .. tostring(response))
            return false
        end
    end)()
end

function handleResponse(url, method, request_data, headers, proxy)
    local options = {
        data = request_data,
        headers = headers
    }
    if proxy then
        options.proxy = proxy
    end

    local success, response = pcall(requests.request, method, url, options)

    if not success then
        return { status_code = nil, text = tostring(response), json = nil }
    end

    local json_data = nil
    if response and response.text and #response.text > 0 then
        local json_success, result = pcall(cjson.decode, response.text)
        if json_success then
            json_data = result
        end
    end

    return {
        status_code = response.status_code,
        text = response.text,
        json = json_data
    }
end