Вопросы по Lua скриптингу

Общая тема для вопросов по разработке скриптов на языке программирования Lua, в частности под MoonLoader.
  • Задавая вопрос, убедитесь, что его нет в списке частых вопросов и что на него ещё не отвечали (воспользуйтесь поиском).
  • Поищите ответ в теме посвященной разработке Lua скриптов в MoonLoader
  • Отвечая, убедитесь, что ваш ответ корректен.
  • Старайтесь как можно точнее выразить мысль, а если проблема связана с кодом, то обязательно прикрепите его к сообщению, используя блок [code=lua]здесь мог бы быть ваш код[/code].
  • Если вопрос связан с MoonLoader-ом первым делом желательно поискать решение на wiki.

Частые вопросы

—
Как научиться писать скрипты? С чего начать?
Информация - Гайд - Всё о Lua скриптинге для MoonLoader(https://blast.hk/threads/22707/)
— Как вывести текст на русском? Вместо русского текста у меня какие-то каракули.
Изменить кодировку файла скрипта на Windows-1251. В Atom: комбинация клавиш Ctrl+Shift+U, в Notepad++: меню Кодировки -> Кодировки -> Кириллица -> Windows-1251.
— Как получить транспорт, в котором сидит игрок?
Lua:
local veh = storeCarCharIsInNoSave(PLAYER_PED)
— Как получить свой id или id другого игрока?
Lua:
local _, id = sampGetPlayerIdByCharHandle(PLAYER_PED) -- получить свой ид
local _, id = sampGetPlayerIdByCharHandle(ped) -- получить ид другого игрока. ped - это хендл персонажа
— Как проверить, что строка содержит какой-то текст?
Lua:
if string.find(str, 'текст', 1, true) then
-- строка str содержит "текст"
end
— Как эмулировать нажатие игровой клавиши?
Lua:
local game_keys = require 'game.keys' -- где-нибудь в начале скрипта вне функции main

setGameKeyState(game_keys.player.FIREWEAPON, -1) -- будет сэмулировано нажатие клавиши атаки
Все иды клавиш находятся в файле moonloader/lib/game/keys.lua.
Подробнее о функции setGameKeyState здесь: lua - setgamekeystate | BlastHack — DEV_WIKI(https://www.blast.hk/wiki/lua:setgamekeystate)
— Как получить id другого игрока, в которого целюсь я?
Lua:
local valid, ped = getCharPlayerIsTargeting(PLAYER_HANDLE) -- получить хендл персонажа, в которого целится игрок
if valid and doesCharExist(ped) then -- если цель есть и персонаж существует
  local result, id = sampGetPlayerIdByCharHandle(ped) -- получить samp-ид игрока по хендлу персонажа
  if result then -- проверить, прошло ли получение ида успешно
    -- здесь любые действия с полученным идом игрока
  end
end
— Как зарегистрировать команду чата SAMP?
Lua:
-- До бесконечного цикла/задержки
sampRegisterChatCommand("mycommand", function (param)
     -- param будет содержать весь текст введенный после команды, чтобы разделить его на аргументы используйте string.match()
    sampAddChatMessage("MyCMD", -1)
end)
— Крашит игру при вызове sampSendChat. Как это исправить?
Это происходит из-за бага в SAMPFUNCS, когда производится попытка отправки пакета определенными функциями изнутри события исходящих RPC и пакетов. Исправления для этого бага нет, но есть способ не провоцировать его. Вызов sampSendChat изнутри обработчика исходящих RPC/пакетов нужно обернуть в скриптовый поток с нулевой задержкой:
Lua:
function onSendRpc(id)
  -- крашит:
  -- sampSendChat('Send RPC: ' .. id)

  -- норм:
  lua_thread.create(function()
    wait(0)
    sampSendChat('Send RPC: ' .. id)
  end)
end
 
Последнее редактирование:

Randy

Известный
88
48
Возможно ли при использовании функции из socket dns.toip("example.com"), если происходит долгий резолвинг (или вовсе не резолвится т. к. хоста нет), чтобы она не тормозила основной поток и игру на время проверки (а то игра может зависнуть примерно на минуту, если не больше)?
 

вайега52

cache miss
Модератор
3,084
3,415
Возможно ли при использовании функции из socket dns.toip("example.com"), если происходит долгий резолвинг (или вовсе не резолвится т. к. хоста нет), чтобы она не тормозила основной поток и игру на время проверки (а то игра может зависнуть примерно на минуту, если не больше)?
попробуй по аналогии с ассинхронными https запросами, заюзать effil
 
  • Нравится
Реакции: Randy

AdCKuY_DpO4uLa

Адский дрочер
Друг
425
915
Возможно ли при использовании функции из socket dns.toip("example.com"), если происходит долгий резолвинг (или вовсе не резолвится т. к. хоста нет), чтобы она не тормозила основной поток и игру на время проверки (а то игра может зависнуть примерно на минуту, если не больше)?
Можешь попробовать обычную корутину заюзать, по идее этого должно хватить
 

Randy

Известный
88
48
Можешь попробовать обычную корутину заюзать, по идее этого должно хватить
Не, не помогает, уже пробовал. Либо я криворукий просто.

попробуй по аналогии с ассинхронными https запросами, заюзать effil
В принципе получилось. Единственный момент, что это создаёт лишнее нагромождение кода :D
На дистанции с несколькими скриптами, которые используют такую проверку, просто задолбаешься везде этот код вставлять)
 
Последнее редактирование:
  • Нравится
Реакции: вайега52

вайега52

cache miss
Модератор
3,084
3,415
Не, не помогает, уже пробовал. Либо я криворукий просто.


В принципе получилось. Единственный момент, что это создаёт лишнее нагромождение кода :D
На дистанции с несколькими скриптами, которые используют такую проверку, просто задолбаешься везде этот код вставлять)
сделай какой-нибудь сниппет) имхо самый некостыльный способ
 
  • Нравится
Реакции: Randy

coulside

Известный
54
1
Не в какую не могу тыкнуть по диалогу 2155, пробовал всеми способами, максимум что получалось что флудит 2155 (закоментил специально)
local sampev = require("lib.samp.events")
local encoding = require("encoding")
encoding.default = "CP1251"
local u8 = encoding.UTF8
local status = false
Lua:
local sampev = require("lib.samp.events")
local encoding = require("encoding")
encoding.default = "CP1251"
local u8 = encoding.UTF8


function sampev.onShowDialog(dialogId, style, title, button1, button2, text)
    if dialogId then
         sampAddChatMessage(dialogId, -1)
         sampAddChatMessage(text, -1)
     end

     if dialog == 5629 then
        return false
     end

--- не получилось
---
-- if dialogId == 2155 then
--     if text:find("Перевод на счет игрока офлайн") then
--     lua_thread.create(function()
--         wait(1150)
--         sampSendDialogResponse(1, 6, "")
--     end)
--     end
-- end

-- не получилось

-- if dialogId == 2155 then
--          sampSendDialogResponse(1, 6, "")

-- end

-- не получилось

--     if text:find("Перевод на счет игрока офлайн") then
--     lua_thread.create(function()
--         wait(1150)
--         sampSendDialogResponse(1, 6, "")
--     end)
-- end
1791235200254.png
1791235210356.png
 

moreveal

Известный
1,000
766

damirtakioma

Участник
57
35
Не в какую не могу тыкнуть по диалогу 2155, пробовал всеми способами, максимум что получалось что флудит 2155 (закоментил специально)
local sampev = require("lib.samp.events")
local encoding = require("encoding")
encoding.default = "CP1251"
local u8 = encoding.UTF8
local status = false
Lua:
local sampev = require("lib.samp.events")
local encoding = require("encoding")
encoding.default = "CP1251"
local u8 = encoding.UTF8


function sampev.onShowDialog(dialogId, style, title, button1, button2, text)
    if dialogId then
         sampAddChatMessage(dialogId, -1)
         sampAddChatMessage(text, -1)
     end

     if dialog == 5629 then
        return false
     end

--- не получилось
---
-- if dialogId == 2155 then
--     if text:find("Перевод на счет игрока офлайн") then
--     lua_thread.create(function()
--         wait(1150)
--         sampSendDialogResponse(1, 6, "")
--     end)
--     end
-- end

-- не получилось

-- if dialogId == 2155 then
--          sampSendDialogResponse(1, 6, "")

-- end

-- не получилось

--     if text:find("Перевод на счет игрока офлайн") then
--     lua_thread.create(function()
--         wait(1150)
--         sampSendDialogResponse(1, 6, "")
--     end)
-- end
Посмотреть вложение 302012Посмотреть вложение 302013
сначала проверяешь диалог, потом
sampsendialogresponse
return false
 
  • Грустно
Реакции: moreveal

Baklaxa

Участник
66
25
почему двигатель не заводится? (окна пропадают, транспорт блокируется, но двигатель не работает)

emul_rpc("onSetVehicleParamsEx", {carid, {1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}})

Lua:
function emul_rpc(name,p)
    local sampev = require'samp.events'
    local handler = require 'samp.events.handlers'
    local function getn(t); local i = 0;    for k,v in pairs(t) do i = i + 1 end return i;  end
    local function reverseTable(tbl)
        local rev = {}
        for i=#tbl, 1, -1 do
            rev[#rev+1] = tbl[i]
        end
        return rev
    end
    for _,INTERFACE in pairs({'OUTCOMING_RPCS','OUTCOMING_PACKETS','INCOMING_RPCS','INCOMING_PACKETS'}) do
        for k,v in pairs(sampev.INTERFACE[INTERFACE]) do
            if (name == v[1] or name == k) then
                local values, bs = getn(v)-1, raknetNewBitStream()
                if type(v[2]) == 'function' then
                    v[3](bs,p)
                else
                    local T = ((p[1] ~= nil and type(p[1]) == 'table') or (#p == 0 and getn(p) ~= 0))
                    if not T then
                        p = reverseTable(p)
                    elseif T and (p[1] ~= nil and type(p[1]) == 'table') then
                        local res = {}
                        for k,v in pairs(p) do
                            for kk,vv in pairs(p[k]) do
                                res[kk] = vv
                            end
                        end
                        p = res
                    end
                    for _,vv in pairs(v) do
                        if type(vv) == 'table' then
                            for k,v in pairs(vv) do
                                for kk,vv in pairs(sampev.INTERFACE.BitStreamIO) do
                                    if kk == v then
                                        sampev.INTERFACE.BitStreamIO[kk].write(bs, p[(T and k or values)])
                                        values = values - 1
                                    end
                                end
                            end
                        end
                    end
                end
                raknetEmulRpcReceiveBitStream(k, bs)
                raknetDeleteBitStream(bs)
            end
        end
    end
end
 

CaJlaT

07.11.2024 14:55
Модератор
2,850
2,729
почему двигатель не заводится? (окна пропадают, транспорт блокируется, но двигатель не работает)

emul_rpc("onSetVehicleParamsEx", {carid, {1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}})

Lua:
function emul_rpc(name,p)
    local sampev = require'samp.events'
    local handler = require 'samp.events.handlers'
    local function getn(t); local i = 0;    for k,v in pairs(t) do i = i + 1 end return i;  end
    local function reverseTable(tbl)
        local rev = {}
        for i=#tbl, 1, -1 do
            rev[#rev+1] = tbl[i]
        end
        return rev
    end
    for _,INTERFACE in pairs({'OUTCOMING_RPCS','OUTCOMING_PACKETS','INCOMING_RPCS','INCOMING_PACKETS'}) do
        for k,v in pairs(sampev.INTERFACE[INTERFACE]) do
            if (name == v[1] or name == k) then
                local values, bs = getn(v)-1, raknetNewBitStream()
                if type(v[2]) == 'function' then
                    v[3](bs,p)
                else
                    local T = ((p[1] ~= nil and type(p[1]) == 'table') or (#p == 0 and getn(p) ~= 0))
                    if not T then
                        p = reverseTable(p)
                    elseif T and (p[1] ~= nil and type(p[1]) == 'table') then
                        local res = {}
                        for k,v in pairs(p) do
                            for kk,vv in pairs(p[k]) do
                                res[kk] = vv
                            end
                        end
                        p = res
                    end
                    for _,vv in pairs(v) do
                        if type(vv) == 'table' then
                            for k,v in pairs(vv) do
                                for kk,vv in pairs(sampev.INTERFACE.BitStreamIO) do
                                    if kk == v then
                                        sampev.INTERFACE.BitStreamIO[kk].write(bs, p[(T and k or values)])
                                        values = values - 1
                                    end
                                end
                            end
                        end
                    end
                end
                raknetEmulRpcReceiveBitStream(k, bs)
                raknetDeleteBitStream(bs)
            end
        end
    end
end
Lua:
function main()
    while not isSampAvailable() do wait(0) end
    sampRegisterChatCommand('emulcar', function()
        if not isCharInAnyCar(1) then return sampAddChatMessage('Вы должны находиться в транспорте', -1) end
        local car = storeCarCharIsInNoSave(1)
        sampAddChatMessage(string.format('Двигатель %s для ID %s', (not isCarEngineOn(car) and 'включен' or 'выключен'), select(2, sampGetVehicleIdByCarHandle(car))), -1)
        emulateEngine(select(2, sampGetVehicleIdByCarHandle(car)), not isCarEngineOn(car))
    end)
    wait(-1)
end

function emulateEngine(vehId, state)
    local res, car = sampGetCarHandleBySampVehicleId(vehId)
    if not res then return print(('Ошибка, транспорт с ID %s не найден'):format(vehId)) end
    local bs = raknetNewBitStream()
    raknetBitStreamWriteInt16(bs, vehId)
    raknetBitStreamWriteInt8(bs, state and 1 or 0)
    for i = 1, 15 do raknetBitStreamWriteInt8(bs, 0) end
    raknetEmulRpcReceiveBitStream(24, bs)
    raknetDeleteBitStream(bs)
end

Необязательный хук события на изменение параметров транспорта, чтобы не выключался двигатель

Lua:
local samp = require 'samp.events'
function samp.onSetVehicleParamsEx(vehId, params, doors, windows)
    if isCharInAnyCar(PLAYER_PED) and vehId == select(2, sampGetVehicleIdByCarHandle(storeCarCharIsInNoSave(1))) then
        if params.engine == 0 then
            print('Двигатель выключен, игнорируем', -1)
            return false
        end
    end
end
 
Последнее редактирование:
  • Нравится
Реакции: Baklaxa

Lokapon

Активный
112
69
диалог не нажимается
(вообще ничего с ним не происходит, хотя print10 выводит)

sampSendDialogResponse не работает


Lua:
local ev = require "lib.samp.events"


local act = false

function main()
    if not isSampLoaded() or not isSampfuncsLoaded() then return end
    while not isSampAvailable() do wait(100) end
    sampRegisterChatCommand("evf", function()
        act = not act
        if act then printStringNow("En", 450) else printStringNow("Off", 450) end
    end)
    while true do
        wait(0)
     
        if act then
            sampSendChat('/dialog')
            wait(500)
        end
 
    end
end


function ev.onShowDialog(dialogId, style, title, btn1, btn2, text)
    if act then
     
        if dialogId == 2040 then
            print(10)
            sampSendDialogResponse(2040, 0, 0, "")
        end
    end
end
Замени 30 строку на:
Код:
sampSendDialogResponse(2040, 1, 0, "")
 

romacaddy

Известный
Проверенный
245
207
диалог не нажимается
(вообще ничего с ним не происходит, хотя print10 выводит)

sampSendDialogResponse не работает


Lua:
local ev = require "lib.samp.events"


local act = false

function main()
    if not isSampLoaded() or not isSampfuncsLoaded() then return end
    while not isSampAvailable() do wait(100) end
    sampRegisterChatCommand("evf", function()
        act = not act
        if act then printStringNow("En", 450) else printStringNow("Off", 450) end
    end)
    while true do
        wait(0)
      
        if act then
            sampSendChat('/dialog')
            wait(500)
        end
  
    end
end


function ev.onShowDialog(dialogId, style, title, btn1, btn2, text)
    if act then
      
        if dialogId == 2040 then
            print(10)
            sampSendDialogResponse(2040, 0, 0, "")
        end
    end
end
учись пользоваться wiki
 

Baklaxa

Участник
66
25
в скрипте я уже применял GetBodyPartCoordinates и оно работало без ошибок, но в этом случае скрипт ломается
время достаточно позднее, возможно я сейчас очень не внимателен


attempt to call global 'GetBodyPartCoordinates' (a nil value)
stack traceback:
C:\Program Files (x86)\GTA SAMP\moonloader\newfile.lua:4504: in function 'callback'
...Files (x86)\GTA SAMP\moonloader\lib\samp\events\core.lua:79: in function <...Files (x86)\GTA SAMP\moonloader\lib\samp\events\core.lua:53>
[ML] (error) newfile.lua: Script died due to an error. (3295745C)


Lua:
local ffi = require 'ffi'
local getBonePosition = ffi.cast("int (__thiscall*)(void*, float*, int, bool)", 0x5E4280)

function getBodyPartCoordinates(id, handle)
    if doesCharExist(handle) then
        local pedptr = getCharPointer(handle)
        local vec = ffi.new("float[3]")
        getBonePosition(ffi.cast("void*", pedptr), vec, id, true)
        return vec[0], vec[1], vec[2]
    end
end

function ev.onSendGiveDamage(playerId, damage, weapon, bodypart)
    local res, HandlePidor = sampGetCharHandleBySampPlayerId(playerId)
    if res then
        print(GetBodyPartCoordinates(bodypart, HandlePidor))
    end
end
 

romacaddy

Известный
Проверенный
245
207
в скрипте я уже применял GetBodyPartCoordinates и оно работало без ошибок, но в этом случае скрипт ломается
время достаточно позднее, возможно я сейчас очень не внимателен


attempt to call global 'GetBodyPartCoordinates' (a nil value)
stack traceback:
C:\Program Files (x86)\GTA SAMP\moonloader\newfile.lua:4504: in function 'callback'
...Files (x86)\GTA SAMP\moonloader\lib\samp\events\core.lua:79: in function <...Files (x86)\GTA SAMP\moonloader\lib\samp\events\core.lua:53>
[ML] (error) newfile.lua: Script died due to an error. (3295745C)


Lua:
local ffi = require 'ffi'
local getBonePosition = ffi.cast("int (__thiscall*)(void*, float*, int, bool)", 0x5E4280)

function getBodyPartCoordinates(id, handle)
    if doesCharExist(handle) then
        local pedptr = getCharPointer(handle)
        local vec = ffi.new("float[3]")
        getBonePosition(ffi.cast("void*", pedptr), vec, id, true)
        return vec[0], vec[1], vec[2]
    end
end

function ev.onSendGiveDamage(playerId, damage, weapon, bodypart)
    local res, HandlePidor = sampGetCharHandleBySampPlayerId(playerId)
    if res then
        print(GetBodyPartCoordinates(bodypart, HandlePidor))
    end
end
16: print(GetBodyPartCoordinates(bodypart, HandlePidor)) -- проблема
16: print(getBodyPartCoordinates(bodypart, HandlePidor)) -- решение
внимательнее.
 
  • Нравится
Реакции: Baklaxa