Вопросы по 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
 
Последнее редактирование:

Святой Леоне

Участник
87
8
Помогите, проблема в том, что если в команде /test два аргумента и один пустой, то команда перестает работать
Lua:
function test(arg)
    local arg1, arg2 =  arg:match('(.+) (.+)')
    sampAddChatMessage(""..arg2, -1)
end
мне нужно, что если например arg2 пустой то писало: Введите 2 аргумент
 

Andrinall

Известный
702
518
Итак, есть у меня значит вот такой вот код:
Lua:
local ffi = require 'ffi'
local winmm = ffi.load('winmm.dll')
if winmm ~= nil and winmm ~= false then
    JOYSTICKID1 = 0
    JOYSTICKID2 = 1
    JOYERR_BASE      = 160
    JOYERR_NOERROR   = 0
    JOYERR_PARMS     = JOYERR_BASE + 5
    JOYERR_NOCANDO   = JOYERR_BASE + 6
    JOYERR_UNPLUGGED = JOYERR_BASE + 7

    joyerr = { [0] = "NO ERROR", [160] = "BASE", [165] = "ERR PARMS", [166] = "ERR NOCANDO", [167] = "ERR UNPLUGGED" }
    JOY_RETURNALL = 0x00000001 or 0x00000002 or 0x00000004 or 0x00000008 or 0x00000010 or 0x00000020 or 0x00000040 or 0x00000080

    ffi.cdef[[
        typedef unsigned long  DWORD;
        typedef unsigned short WORD;
        typedef unsigned int   UINT;
        typedef char            CHAR;
        typedef unsigned int   UINT_PTR;
        typedef int            BOOL;
        typedef UINT           MMRESULT;
        typedef UINT            *LPUINT;
        typedef wchar_t        WCHAR;
        struct HWND__ { int unused; };
        typedef struct HWND__ *HWND;

        int MAXPNAMELEN;
        int MAX_JOYSTICKOEMVXDNAME;
    ]]
    MAXPNAMELEN = ffi.new("int"); MAXPNAMELEN = 32
    MAX_JOYSTICKOEMVXDNAME = ffi.new("int"); MAX_JOYSTICKOEMVXDNAME = 260
    ffi.cdef[[
        typedef struct tagJOYCAPSW {
            WORD    wMid;                                /* manufacturer ID */
            WORD    wPid;                                /* product ID */
            WCHAR   szPname[MAXPNAMELEN];                /* product name (NULL terminated string) */
            UINT    wXmin;                               /* minimum x position value */
            UINT    wXmax;                               /* maximum x position value */
            UINT    wYmin;                               /* minimum y position value */
            UINT    wYmax;                               /* maximum y position value */
            UINT    wZmin;                               /* minimum z position value */
            UINT    wZmax;                               /* maximum z position value */
            UINT    wNumButtons;                         /* number of buttons */
            UINT    wPeriodMin;                          /* minimum message period when captured */
            UINT    wPeriodMax;                          /* maximum message period when captured */
            UINT    wRmin;                               /* minimum r position value */
            UINT    wRmax;                               /* maximum r position value */
            UINT    wUmin;                               /* minimum u (5th axis) position value */
            UINT    wUmax;                               /* maximum u (5th axis) position value */
            UINT    wVmin;                               /* minimum v (6th axis) position value */
            UINT    wVmax;                               /* maximum v (6th axis) position value */
            UINT    wCaps;                               /* joystick capabilites */
            UINT    wMaxAxes;                            /* maximum number of axes supported */
            UINT    wNumAxes;                            /* number of axes in use */
            UINT    wMaxButtons;                         /* maximum number of buttons supported */
            WCHAR   szRegKey[MAXPNAMELEN];                /* registry key */
            WCHAR   szOEMVxD[MAX_JOYSTICKOEMVXDNAME];     /* OEM VxD in use */
        } JOYCAPSW, *PJOYCAPSW, *NPJOYCAPSW, *LPJOYCAPSW; /* Если здесь поставить ... NEAR *NPJOYCAPSW, FAR *LPJOYCAPSW; */
                                                          /* как в оригинале - будет выдавать ошибку "ожидалось ;" или типо того */
    ]]
    ffi.cdef[[
        typedef struct joyinfo_tag {
            UINT wXpos;                 /* x position */
            UINT wYpos;                 /* y position */
            UINT wZpos;                 /* z position */
            UINT wButtons;              /* button states */
        } JOYINFO, *PJOYINFO, *NPJOYINFO, *LPJOYINFO; // near *NPJOYINFO, far *LPJOYINFO
   
        typedef struct joyinfoex_tag {
            DWORD dwSize;                /* size of structure */
            DWORD dwFlags;               /* flags to indicate what to return */
            DWORD dwXpos;                /* x position */
            DWORD dwYpos;                /* y position */
            DWORD dwZpos;                /* z position */
            DWORD dwRpos;                /* rudder/4th axis position */
            DWORD dwUpos;                /* 5th axis position */
            DWORD dwVpos;                /* 6th axis position */
            DWORD dwButtons;             /* button states */
            DWORD dwButtonNumber;        /* current button number pressed */
            DWORD dwPOV;                 /* point of view state */
            DWORD dwReserved1;           /* reserved for communication between winmm & driver */
            DWORD dwReserved2;           /* reserved for future expansion */
        } JOYINFOEX, *PJOYINFOEX, *NPJOYINFOEX, *LPJOYINFOEX; // near *NPJOYINFOEX, far *LPJOYINFOEX
    ]]
    ffi.cdef[[
        UINT joyGetNumDevs(void);
        MMRESULT joyGetDevCapsW(    UINT_PTR uJoyID, LPJOYCAPSW pjc,   UINT cbjc);
        MMRESULT joyGetPos(         UINT uJoyID,       LPJOYINFO pji);
        MMRESULT joyGetPosEx(         UINT uJoyID,       LPJOYINFOEX pji);
    ]]

    joyinfo              = ffi.new("JOYINFO");
    joyinfoex           = ffi.new("JOYINFOEX");
    lpjoycapsw            = ffi.new("JOYCAPSW");
    joyname            = ffi.new("WCHAR");
    joyinfoex.dwSize   = ffi.sizeof(joyinfoex);
    joyinfoex.dwFlags  = JOY_RETURNALL;
    result                  = ffi.new("MMRESULT");
    caps_result        = ffi.new("MMRESULT");

    TrueDeviceID        = -1;
    nums                = {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
    wDeviceID            = 0;
    wcDeviceID          = false;
    globalErr           = 0;
    JOY_Buttons        = {
        [0]="None",    [1]="A",                 [2]="B",                [4]="X",
        [8]="Y",       [16]="LB",               [32]="RB",               [64]="Back",
        [128]="Start", [256]="LStickButton", [512]="RStickButton"
    }
    JOY_POVS           = {
        [0] = "UP",       [4500] = "RIGHT-UP",      [9000] = "RIGHT", [13500] = "RIGHT-DOWN",
        [18000] = "DOWN", [22500] = "LEFT-DOWN", [27000] = "LEFT", [31500] = "LEFT-UP", [65535] = "None"
    }
else error("Lib WinMM.dll not Found. Script Crashed!"); return false;
end

local font = renderCreateFont('Century Gothic', 13, 0x01+0x04)
local visible = true;

function main()
    while not isSampfuncsLoaded() do wait(100) end
    repeat wait(100) until isSampAvailable()
    wNumDevs = winmm.joyGetNumDevs();

    sampRegisterChatCommand('joytest', function() visible = not visible end)

    while true do wait(0)
        if visible then
            bDev1Attached = winmm.joyGetPos(JOYSTICKID1, joyinfo) ~= JOYERR_UNPLUGGED;
            --if wNumDevs >= 2 and winmm.joyGetPos(JOYSTICKID2, joyinfo) ~= JOYERR_UNPLUGGED then bDev2Attached = wNumDevs end
            if bDev1Attached or bDev2Attached then wDeviceID = bDev1Attached end
       
            if wDeviceID then
                local r;
                for i = 1, #nums do r = winmm.joyGetPosEx(nums[i], joyinfoex); if r ~= 165 and r ~= 166 and r ~= 167 and r == 0 then TrueDeviceID = nums[i]; wcDeviceID = true; end    end
                if not wcDeviceID then globalErr = r; end
            end
       
            if wcDeviceID then
                result = winmm.joyGetPosEx(TrueDeviceID, joyinfoex);
                if result ~= 0 then wcDeviceID = false; end
           
                if wcDeviceID then           
                    if joyinfoex.dwZpos == 127 then joyinfoex.dwZpos = 0 end
                    if joyinfoex.dwZpos == 65407 then joyinfoex.dwZpos = 65535 end
               
                    caps_result = winmm.joyGetDevCapsW(TrueDeviceID, lpjoycapsw, ffi.sizeof(lpjoycapsw));
                    local formated = {
                        [1] = function() return "Device Name: "..caps_result.." > "..tostring(lpjoycapsw.szPname).."" end, -- здесь должно показывать Gamepad F310 (Controller)
                        [2] = function() return string.format("Result: %s | Struct info: %s", joyerr[result], tostring(joyinfoex)) end,
                        [3] = function() return string.format("X: %d%% | Y: %d%% |Z: %d%%",
                                                    (joyinfoex.dwXpos / 1000) * 1.525902189669643,
                                                    (joyinfoex.dwYpos / 1000) * 1.525902189669643,
                                                    (joyinfoex.dwZpos / 1000) * 1.525902189669643)
                              end,
                        [4] = function() return string.format("V: %d%% | U: %d%% | R: %d%%",
                                                    (joyinfoex.dwVpos / 1000) * 1.525902189669643,
                                                    (joyinfoex.dwUpos / 1000) * 1.525902189669643,
                                                    (joyinfoex.dwRpos / 1000) * 1.525902189669643)
                              end,
                        [5] = function() return string.format("Current Buttons: %s ", JOY_Buttons[joyinfo.wButtons]) end,
                        [6] = function() return string.format("POV: %s", JOY_POVS[joyinfoex.dwPOV]) end
                    }
                    for i = 1, #formated do renderFontDrawText(font, tostring(formated[i]()), 20, 430 + (i* 20), 0x99FFFFFF) end
                end
            elseif not wcDeviceID then
                renderFontDrawText(font, "Device Not Found. Error: "..joyerr[globalErr], 20, 430, 0x99FFFFFF)
            end
        end
    end
end

Он вроде как адекватно отображает значения (Оси геймпада).
Но... я столкнулся с такой проблемой, что он почему-то отказывается показывать
имя девайса через winmm.joyGetDevCapsW(UINT_PTR uJoyID, LPJOYCAPSW pjc, UINT cbjc); ...
и выдаёт 165 ошибку JOYERR_PARMS или 11 ошибку MMSYSERR_INVALPARAM на все возможные uJoyID.. (не понял от чего зависит ошибка)
Пробовал через ffi.new создавать переменную с типом UINT_PTR, заранее объявив её в ffi.cdef,
пробовал менять типы данных для переменной lpjoycapsw, используемой 2 аргументов в вызове функции.
Ой, да чего только я не пробовал...
Пробовал даже циклом чекать не от -1 до 15(16), а до 500.. Это ничего не дало.

Хотя через winmm.joyGetPosEx(UINT uJoyID, LPJOYINFOEX pji); нормально возвращается MMRESULT == 0
то есть JOYERR_NOERROR или MMSYSERR_NOERROR.

Скрин рендера... В Device Name записывается MMRESULT от вызова winmm.joyGetDevCapsW(TrueDeviceID, lpjoycapsw, ffi.sizeof(lpjoycapsw));
и lpjoycaps.szPname
( "Device Name: "..caps_result.." > "..tostring(lpjoycapsw.szPname).."" )

В чём может быть проблема? (Кроме "говнокод", это я и так знаю. Нужно повествование по конкретной проблеме)

Всё ещё актуально.
 

Andrinall

Известный
702
518
Помогите, проблема в том, что если в команде /test два аргумента и один пустой, то команда перестает работать
Lua:
function test(arg)
    local arg1, arg2 =  arg:match('(.+) (.+)')
    sampAddChatMessage(""..arg2, -1)
end
мне нужно, что если например arg2 пустой то писало: Введите 2 аргумент
Lua:
function test(arg)
    local arg1, arg2 = arg:match('(.+) (.+)')
    if arg1 ~= nil and arg2 ~= nil then
        -- код если 2 аргумента введены
    else
        sampAddChatMessage('Используйте: /test [arg1][arg2]', -1) -- если какой-то из аргументов оказался равен nil
    end
end

Что-то вроде того?
 
  • Нравится
Реакции: Святой Леоне

chapo

чопа сребдс // @moujeek
Модератор
8,865
11,553
Из какого-то кода нашел функцию, где ищутся чекпоинты. Но к сожалению он не ищет "большой" чекпоинт. Крч как заставить искать большой чекпоинт, а не "мелкие"

Lua:
function SearchMarker(isRace)
    local ret_posX = 0.0
    local ret_posY = 0.0
    local ret_posZ = 0.0
    local isFind = false
    for id = 0, 31 do
        local MarkerStruct = 0
        if isRace then MarkerStruct = 0xC7F168 + id * 56
        else MarkerStruct = 0xC7DD88 + id * 160 end
        local MarkerPosX = representIntAsFloat(readMemory(MarkerStruct + 0, 4, false))
        local MarkerPosY = representIntAsFloat(readMemory(MarkerStruct + 4, 4, false))
        local MarkerPosZ = representIntAsFloat(readMemory(MarkerStruct + 8, 4, false))
        if MarkerPosX ~= 0.0 or MarkerPosY ~= 0.0 or MarkerPosZ ~= 0.0 then
            ret_posX = MarkerPosX
            ret_posY = MarkerPosY
            ret_posZ = MarkerPosZ
            isFind = true
        end
    end
    return isFind, ret_posX, ret_posY, ret_posZ
end


попробуй вызывать функу с "true", то есть SearchMarker(true)
 

chapo

чопа сребдс // @moujeek
Модератор
8,865
11,553
Спасибо, работает. А как на +2 поднять от исходной координаты y? При телепортации чекпоинт не сразу берется
Lua:
function tp()
    _, mx, my, mz = SearchMarker(true)
    if _ then
        setCarCoordinates(storeCarCharIsInNoSave(PLAYER_PED), mx, my, mz
    end
end
на 4 строке вместо mz напиши mz + 2 если ты про это
 

copypaste_scripter

Известный
1,261
235
И как ты собрался его открывать? Можешь отправлять синхру на корды на которых он вызывается и попробовать там сделать действия после которых он появляется.

эм, тут просто нажатие кнопки, а я сказал мышки, надо по координатам
 

Dmitriy Makarov

25.05.2021
Проверенный
2,500
1,131
Не знаете, почему при сохранении HotKey'я, там появляются лишние пробелы и из-за этого, скрипт из ini не может нормально подгрузить клавишу?

Lua:
local tLastKeys = {}
-- ini
local mainIni = inicfg.load({
    config = {
        hot_key = "[67]" -- Это С английская
        -- В .ini - hot_key = [67]
    }
}, "test.ini")

-- Подгружаю
local hotkey = {
    v = decodeJson(mainIni.config.hot_key) -- Ошибка тут.
}

-- main
hotKey = rkeys.registerHotKey(hotkey.v, true, function() end)

-- onDrawFrame
if imgui.HotKey("##1", hotkey, tLastKeys, 100) then
    rkeys.changeHotKey(hotKey, hotkey.v) -- Перерегистрирую
    mainIni.config.hot_key = encodeJson(hotkey.v)
    inicfg.save(mainIni, 'test.ini')
end
Итог, если изменить клавишу на другую
Lua:
-- Сохраняется так
hot_key = [
  71
]

-- Надо вот так
hot_key = [71]

-- Если вручную в ini изменить так, как нужно и перезагрузить скрипт, то клавиша меняется

Сама ошибка
Код:
[ML] (error) scr: json parse: Invalid value. (at 1)
stack traceback:
    [C]: in function 'decodeJson'
    D:\GTA - êîïèÿ\moonloader\scr.lua:38: in main chunk
Актуально ещё.
 

kin4stat

mq-team · kin4@naebalovo.team
Всефорумный модератор
2,744
4,808
Не знаете, почему при сохранении HotKey'я, там появляются лишние пробелы и из-за этого, скрипт из ini не может нормально подгрузить клавишу?

Lua:
local tLastKeys = {}
-- ini
local mainIni = inicfg.load({
    config = {
        hot_key = "[67]" -- Это С английская
        -- В .ini - hot_key = [67]
    }
}, "test.ini")

-- Подгружаю
local hotkey = {
    v = decodeJson(mainIni.config.hot_key) -- Ошибка тут.
}

-- main
hotKey = rkeys.registerHotKey(hotkey.v, true, function() end)

-- onDrawFrame
if imgui.HotKey("##1", hotkey, tLastKeys, 100) then
    rkeys.changeHotKey(hotKey, hotkey.v) -- Перерегистрирую
    mainIni.config.hot_key = encodeJson(hotkey.v)
    inicfg.save(mainIni, 'test.ini')
end
Итог, если изменить клавишу на другую
Lua:
-- Сохраняется так
hot_key = [
  71
]

-- Надо вот так
hot_key = [71]

-- Если вручную в ini изменить так, как нужно и перезагрузить скрипт, то клавиша меняется

Сама ошибка
Код:
[ML] (error) scr: json parse: Invalid value. (at 1)
stack traceback:
    [C]: in function 'decodeJson'
    D:\GTA - êîïèÿ\moonloader\scr.lua:38: in main chunk
потому что encodeJson пытается сохранить в человекочитаемом формате. Почему нельзя выбрать indent level как например в питоне - спрашивай у фипа.
Решение - хранить весь массив в JSON либо самому переводить цифру из строки в инт