Перейти к контенту
Malandrinus

Язык Lua. Общие вопросы программирования

Рекомендуемые сообщения

С чего начинать и где взять.

 

Установка Lua:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=629106

 

Руководство «Программирование на языке Lua», третье издание:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=905308

Изменено пользователем Kirgudu
Ссылка на комментарий

Отталкивалась исключительно от АСДС. Пока вылетов с использованием этих пакетов я не обнаруживала.

sub update_read {
    ($_[0]->{'upd:num_items'}) = $_[1]->unpack('C');
    return if ($_[0]->{'upd:num_items'} == 0);
    @{$_[0]->{'upd:ph_position'}} = $_[1]->unpack('f3');
    @{$_[0]->{'upd:ph_rotation'}} = $_[1]->unpack('C4');
    my $info_mask = $_[0]->{'upd:num_items'} >> 5;
    if (($info_mask & 0x02) == 0) {
        die unless $_[1]->length() >= 3;
        @{$_[0]->{'upd:ph_angular_vel'}} = $_[1]->unpack('C3');
    }
    if (($info_mask & 0x04) == 0) {
        die unless $_[1]->length() >= 3;
        @{$_[0]->{'upd:ph_linear_vel'}} = $_[1]->unpack('C3');
    }
}
sub update_write {
    $_[1]->pack_properties($_[0], (upd_properties_info)[0]);
    return if ($_[0]->{'upd:num_items'} == 0);
    $_[1]->pack_properties($_[0], (upd_properties_info)[1 .. 2]);
    my $info_mask = $_[0]->{'upd:num_items'} >> 5;
    if (($info_mask & 0x02) == 0) {
        $_[1]->pack_properties($_[0], (upd_properties_info)[3]);
    }
    if (($info_mask & 0x04) == 0) {
        $_[1]->pack_properties($_[0], (upd_properties_info)[4]);
    }
}

 

Изменено пользователем xStream

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

Тоже пока не обнаруживал вылетов, но обуславливаю это тем, что в игре может вообще не правятся такие объекты (с 'num_items' ~= 0), а может редко читаются/изменяются эти апдейт-параметры, тем более пока экспериментировал не отловил предметов с 'num_items' ~= 0.

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

 

P.S. В приведенном тобою куске кода из ACDC невозможно понять какая версия или хотя бы о каких пропертях идет речь в коде. Кусок из универсального не выкладываю, т.к. ... "много буковок" и много веток. ;-)

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

Вот, добавил собственно то, что хотел: возможность множественных подключений слотов, возможность это запретить, некоторые настройки с этим связанные и вообще привел в порядок код.

http://ifolder.ru/28140737

Ссылка на комментарий

Artos,

                if updnum_items==0 then
                    return
                end

ЭТОТ кусок то понятен? :) Если ноль, то все, дальше ничего не читается из апдейта. И не пишется.

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

xStream, опять ... ;-(

Не буду надоедать, но обрати внимание, что пропущенное при чтении - НЕ скипится при записи(!) и из дефолтов вылезут несуществующие в 'data' проперти. (в ЧН/ЗП еще к этим поприбавится)

 

P.S. Тут параллельно идет обсуждение о подписках/отписках, что конечно важно, но ... в теме (пере)записи нет-пакетов каждый байт/бит важен и при ошибке - никакие отписки не помогут. Поэтому и такая с моей стороны дотошность. (сорри, но это я еще не выверял каждый класс начисто, а только в черновую).

 

P.P.S. Пока пишу некоторые куски с заделом/запасом для ЧН/ЗП расширения. Заменить break на return можно в конце (начисто), а вот если потребуется и забыть что завершил - можно долго в код всматриваться ... Сейчас у меня эта строка так: break --/ => return --/>

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

Не опять, а снова. При чтении проблем нет. В записи я увидела и исправила.

 

Добавлено через 10 мин.:

Я еще не понимаю, зачем делать break, если однозначно надо завершать функцию...

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

Ссылка на комментарий

Вот, еще немного дополненная версия: дефолтные настройки вынесены из логики.

malandrinus, теперь если в mon_utils.default_slot поставить unique = true, то получится ровно твоя система, но которую можно использовать более гибко :)

 

http://ifolder.ru/28160609

Ссылка на комментарий
malandrinus, вопрос по проверке зависаний. Как я понимаю, колбэк заданный в level.add_call(...) вызывается с частотой апдэйта актера - не слишком ли часто это для проверки? Если, чисто теоретически, предположить что подписанно 100 функций, и не все сработают в течении 25 миллисекунд, то игра будет вылетать хотя не факт что колбэк именно завис. Или же есть ограничение что ВСЕ колбэки должны быть завершены в течении одного апдэйта? Соответственно, если таких ограничений нет, то не лучше ли сделать проверку допустим посекундно? Изменено пользователем Andrey07071977
Ссылка на комментарий

Рабочий вариант модуля для работы с нет-пакетами в игре.

 

- модуль сделан на базе xs_netpk и m_net_utils;

- работа с нет-пакетами версий ТЧ/ЧН/ЗП (патч ТЧ 1.0004 и выше);

- наличие единого вызова для всех игровых объектов (не исключающий указание конкретного класса);

- раздельная работа с обоими типами пакетов (state|update), что не исключает и объединенный пакет данных;

- максимально упрощенная работа с изменением свойств 'cse_abstract';

- облегченная адаптация под моды с новыми классами объектов (настройки в конфиг-файле);

- автономность работы (коды необходимых хелперов интегрированы в модуль);

- недоделаны только несколько "сложных" классов объектов типа smart_terrain ...

 

ссылка: m_netpk_120120 (~21.2 кБ)

 

Прошу заинтересовавшихся проверить (перед выкладыванием в общую тему по скриптам) и дать комментарии/замечания/пожелания.

(в дальнейшем по работе модуля - в топик по скриптам)

 

P.S. Обновил архив, исправлены отдельные ощибки, добавлен функционал классу шейперов.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Ссылка на комментарий

Andrey07071977,

вопрос по проверке зависаний. Как я понимаю, колбэк заданный в level.add_call(...) вызывается с частотой апдэйта актера

Вызывается с частотой fast_call, который, как я понимаю, вызывается с частотой пересчета физики. По идее даже быстрее апдейта, хотя на последних патчах у меня стабильно работает с частотой апдейта (или апдейт с частотой fast_call, уж не знаю). Это может также от машины зависеть, я на самом деле так и не понял от чего.

 

не слишком ли часто это для проверки? Если, чисто теоретически, предположить что подписанно 100 функций, и не все сработают в течении 25 миллисекунд, то игра будет вылетать хотя не факт что колбэк именно завис.

Этот вызов никак не может сработать во время работы другого вызова. Lua однопоточный, все вызовы или следуют один за другим или вызывают друг друга. Для проверки специально используется отдельный механизм чтобы исключить второй вариант. Поэтому эта проверка срабатывает строго в моменты между вызовами. Значит, если счетчик не нулевой, то было зависание.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий
malandrinus, что-то совсем я запутался, ведь если
Этот вызов никак не может сработать во время работы другого вызова
, то по идее при зависшем колбэке вообще ничего не должно сработать, ведь работа функции так и не завершилась.
Ссылка на комментарий

Andrey07071977,

подвисание одного колбека никак не влияет на работу других.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Пока ничего не понял, одно противоречит другому :huh: Буду вечером разбираться в синхронизации колбэков

Изменено пользователем Andrey07071977
Ссылка на комментарий

Andrey07071977,

многие игровые колбеки независимы друг от друга. Подвисание колбека по-простому означает, что такой колбек перестает вызываться. Скажем, произошел сбой в апдейте биндера актора. Апдейты биндера больше не вызываются. Соответственно, не работает обновление логики, работающее на этих апдейтах. Но все остальное работает. Скажем, колбек на взятие предмета будет продолжать работать.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий
malandrinus, ну да именно так я и предполагал. Допустим, ради примера, навесили на колбэк взятия предмета некую функцию которая будет выполнятся больше одного интервала апдэйта, например тот же перебор значений get_actor_float(...) из твоего x-ray extensions, от 1 до скажем 1000000. Этот вызов, во всяком случае на моем лаптопе, займет больше секунды. Так вот я не совсем понимаю почему проверка зависаний не сработает до тех пор пока не закончится данный вызов - ведь колбэки разные?
Ссылка на комментарий

Andrey07071977,

почему проверка зависаний не сработает до тех пор пока не закончится данный вызов - ведь колбэки разные?

Потому что Lua строго однопоточный и движок игры в общем тоже однопоточный, по крайней мере игровая логика вся работает в один поток. Пока не закончится один вызов, другой не начнется, даже если пришло его время. Это в общем-то легко проверить. Поставь в Lua бесконечный цикл - это подвесит всю игру.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий
malandrinus, кажется я понял в чем дело. Как бы ты сформулировал (блин, перевожу с Английского на Русский, дожились) "зависание колбэка"? В моем понимании, это происходит при ошибке в исполнении функции, когда был вход но небыло выхода. При этом происходит подвисание определенной нити, остальные при этом спокойно продолжают работу. Изменено пользователем Andrey07071977
Ссылка на комментарий

Andrey07071977,

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

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Конечно "спецы" давно знают (и не посвящали почему-то?). Я далеко не спец, и тоже молчал. но...

Чего-то вдруг вспомнил, что в Сталке используется JIT. Хоть и версии 1.1.5, но тем не менее для кого-то может найтись несколько полезных моментов.

На скорую руку описал вот кое что интересное и может быть кому-то нужное???

Заранее извините - копипаст из SciTE (в расширении *.properties. Ну знаете ли - у каждого свои предпочтения:)):

arch        = x86
version     = LuaJIT 1.1.5
version_num = 10105
debug       = function
compilesub  = function
compile     = function
on          = function
off         = function
attach      = function
[util       = table]
        [stats      = function]; вызов : jit.util.stats(name_function)   -- возвращает таблицу
                upvalues   = number  -- Количество внешних локальных переменных для данной функции.
                                     -- Получить их можно в функции "upvalue" ниже.
                bytecodes  = number  -- Очень интересный для меня вопрос. Пока воздержусь от комментария.
                                     -- Информация и кое-какие знания по нему у меня есть, но... Изучу подробнее - тогда и...
                env        = table   -- Таблица окружения для данной функции
                consts     = number  -- Как я понял - это количество глобальных переменных. Как объявленных внутри функции, так и получаемых извне.
                                     -- Использовать функцию "const" ниже.
                subs       = number  -- Количество вложенных (действий???).
                                     -- Проверял на функциях (в данном случае равно 2, т.е. вложенные функции b и c???).
                                                function a()
                                                    function b() end
                                                    local function c() end
                                                end
                status     = 0/1     -- Вызывалась ли функция? 0 - вызывалась, 1 - нет.
                isvararg   = boolean -- Передаётся ли в функцию переменное количество аргументов
                stackslots = 4
                params     = number  -- Количество передаваемых в функцию аргументов
        [const      = function]      -- Получение имён глобальных переменных
                for i=1,jit.util.stats(name_function).consts do
                    print(jit.util.const(name_function, i))
                end
        [upvalue    = function]      -- Получение имён внешних локальных переменных
                local i = 0
                while jit.util.upvalue(test,i) do
                    print(jit.util.upvalue(test,i))
                    i = i + 1
                end
        jsubmcode  = function
        stackptr   = function
        closurenup = function
        bytecode   = function
        mcode      = function
        [fhints     = table]
                NOCLOSE = 65536
        [hints      = table]
                INLINE     = 327680
                TYPE       = 196608
                FOR_STEP_K = 131072
                COMBINE    = 65536
                TYPEKEY    = 262144
        [status     = table]
                1              = NONE
                2              = OFF
                3              = ENGINE_OFF
                4              = DELAYED
                5              = TOOLARGE
                6              = COMPILER_ERROR
                7              = DASM_ERROR
                0              = OK
                COMPILER_ERROR = 6
                OFF            = 2
                OK             = 0
                DASM_ERROR     = 7
                ENGINE_OFF     = 3
                TOOLARGE       = 5
                DELAYED        = 4
                NONE           = 1

 

Чуть не забыл. Всё это находиться так :

for k,v in pairs(jit) do
    print(k,v)
end

Изменено пользователем Gun12
Ссылка на комментарий

В продолжение дискуссии о колбэках… Так как не имел четкого представления о том как происходит синхронизация колбэков в сталкере, решил поэкспериментировать и разобраться. Для этого попытался смоделировать нечто подобное в чистом Lua. Вот что получилось и некоторые общие замечания по теме.

 

1. Из справочного руководства Lua:

Lua поддерживает подпрограммы, эту технологию часто называют общей многопоточностью. Подпрограмма Lua представляет собой независимый поток выполнения. Несмотря на это, в отличие от потоков в традиционных многопоточных системах, подпрограмма может приостановить свое выполнение только в результате явного вызова функции yield.
Это может ввести в заблуждение, так как на самом деле функции выполняются последовательно (синхронно) – в данный момент может выполнятся ОДНИН поток (coroutine). Эффект многопоточности достигается за счет очень быстрого переключения между coroutines а так же возможности останавливать и продолжать работу. Данные о состоянии потока при остановке будут сохранены в Lua State потока.

2. Каждый поток создается с помощью команды co = coroutine.create(func). Где func это функция которую нужно запустить в отдельном потоке.

3. Запустить исполнение потока (один из способов) можно через команду coroutine.resume(cor), где cor это поток созданный выше.

4. Потоки запущенные с помощью resume выполняются в песочнице (protected environment) на подобии pcall. !!! Все ошибки при этом не распространяются в основную нить программы а лишь приводят данный поток в нерабочее состояние (coroutine.status(cor) = “dead”). Это и есть причина того что колбэки в сталкере молча зависают а не приводят к вылету.

5. Ошибки которые произошли при выполнении функций в потоках, можно получить с помощью local status, err = coroutine.resume(cor). Где err и будет строка с ошибкой.

6. При зависании одного из потоков, все остальные продолжают спокойно работать, так как имеют свою собсвенную, независимую среду (state). Попытки вызвать зависший поток будут лишь выдавать ошибку «cannot resume dead coroutine», которую в общем то легко отловить и отреагировать, но движок сталкера, к сожалению, ее просто сьедает.

7. Каждому колбэку в игре создается свой персональный поток, который в итоге и зависнет ☺

 

Теперь собственно модель колбэков:

--/ Фунции которые будут вызваны в колбэках
function hang() --/ this function will hang a coroutine (thread)
    print("------> i am good now, but will hang on next run :(")
    local b = 2/nil     --/ error will be introduced here
    print("------> i am dead and you will never see this print :P")
end
function good() --/ this is a valid function and will NOT hang a thread
    print("------> i am still alive ...")
end

--/ Сами колбэки
function callback_hang()    
    while true do
        --/ script functions will be called here
        -- ...
        hang()    --/ triggering this function will cause a thread to hang
        -- ...
        --//
        coroutine.yield() --/ all functions have finished - return control to main thread
    end
end
function callback_good()
    while true do
        --/ script functions will be called here
        -- ...
        good()
        -- ...
        --// 
        coroutine.yield() --/ all functions have finished - return control to main thread
    end
end

--/ main processing thread – основная нить игры. Эта нить является связующим звеном и координатором всех остальных потоков (нитей/coroutines). Рано или поздно все потоки вернут контроль именно сюда.
function main_thread()
    --/ create a thread for each callback
    coroutine_hang = coroutine.create(callback_hang)
    coroutine_good = coroutine.create(callback_good)
    
    --/ Trigger callbacks first time
    print("*** first trigger of HANGING callback ***")
    local st, err = coroutine.resume(coroutine_hang) --/ trigger hanging callback
    print("-> status = "..tostring(st))
    print("-> coroutine status = "..tostring(coroutine.status(coroutine_hang)))
    print("-> error = "..tostring(err))
    print("*** first trigger of GOOD callback ***")
    st, err = coroutine.resume(coroutine_good) --/ trigger valid callback
    print("-> status = "..tostring(st))
    print("-> coroutine status = "..tostring(coroutine.status(coroutine_good)))
    print("-> error = "..tostring(err))
    
    print("\n... some main thread activity between callbacks ...\n")
    
    --/ Trigger callbacks second time
    print("*** second trigger of HANGING callback ***")
    st, err = coroutine.resume(coroutine_hang) --/ retrigger hanging callback
    print("-> return status = "..tostring(st))
    print("-> coroutine status = "..tostring(coroutine.status(coroutine_hang)))
    print("-> error = "..tostring(err))
    print("*** second trigger of GOOD callback ***")
    st, err = coroutine.resume(coroutine_good) --/ retrigger valid callback
    print("-> status = "..tostring(st))
    print("-> coroutine status = "..tostring(coroutine.status(coroutine_good)))
    print("-> error = "..tostring(err))
    
    print("\n... some main thread activity between callbacks ...\n")
    
    --/ Trigger callbacks third time ... and so on ...
    print("*** third trigger of HANGING callback ***")
    st, err = coroutine.resume(coroutine_hang) --/ retrigger hanging callback
    print("-> return status = "..tostring(st))
    print("-> coroutine status = "..tostring(coroutine.status(coroutine_hang)))
    print("-> error = "..tostring(err))
    print("*** third trigger of GOOD callback ***")
    st, err = coroutine.resume(coroutine_good) --/ retrigger valid callback
    print("-> status = "..tostring(st))
    print("-> coroutine status = "..tostring(coroutine.status(coroutine_good)))
    print("-> error = "..tostring(err))
end

main_thread()

 

OUTPUT:

*** first trigger of HANGING callback ***
------> i am good now, but will hang on next run :(
-> status = false
-> coroutine status = dead
-> error = [string "function hang() --/ this function will
hang..."]:3: attempt to perform arithmetic on a nil value
*** first trigger of GOOD callback ***
------> i am still alive ...
-> status = true
-> coroutine status = suspended
-> error = nil

... some main thread activity between callbacks ...

*** second trigger of HANGING callback ***
-> return status = false
-> coroutine status = dead
-> error = cannot resume dead coroutine
*** second trigger of GOOD callback ***
------> i am still alive ...
-> status = true
-> coroutine status = suspended
-> error = nil

... some main thread activity between callbacks ...

*** third trigger of HANGING callback ***
-> return status = false
-> coroutine status = dead
-> error = cannot resume dead coroutine
*** third trigger of GOOD callback ***
------> i am still alive ...
-> status = true
-> coroutine status = suspended
-> error = nil

 

 

 

Видно что при зависании, первый поток не вылетает а просто сообщает что его больше не возможно перезапустить. Второй продолжает спокойно работать.

 

Некоторые интересные моменты:

1. Если исполнение какой либо функции поместить в отдельно созданный поток и запусть его с помощью команды local st, err = coroutine.resume(cor), то информацию о ошибке можно считать в переменной err. Таким образом можно значительно упростить отладку колбэков в игре. В игре не тестировал пока, но в теории должно сработать. Позже напишу подробней.

2. Движок игры скрывает создание потоков. Функция coroutine.running() у меня всегда возвращает nil – что означает основную нить игры. С чем связанно не знаю, пока не разбирался.

 

Надеюсь поможет кому понять работу coroutines Lua в общем и конкретно колбэков в сталкере. Если есть замечания, вопросы, и тд. пишите.

 

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    Ни один зарегистрированный пользователь не просматривает эту страницу.

AMK-Team.ru

×
×
  • Создать...