Dennis_Chikin 3 658 Опубликовано 4 Января 2015 Поделиться Опубликовано 4 Января 2015 (изменено) С чего начинать и где взять. Установка 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 Изменено 2 Марта 2015 пользователем Kirgudu Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
xStream 86 Опубликовано 16 Января 2012 Поделиться Опубликовано 16 Января 2012 (изменено) Отталкивалась исключительно от АСДС. Пока вылетов с использованием этих пакетов я не обнаруживала. 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]); } } Изменено 16 Января 2012 пользователем xStream Все, кто стоит на моем пути: идите нахрен и там погибните! © Ссылка на комментарий
Artos 99 Опубликовано 16 Января 2012 Поделиться Опубликовано 16 Января 2012 (изменено) Тоже пока не обнаруживал вылетов, но обуславливаю это тем, что в игре может вообще не правятся такие объекты (с 'num_items' ~= 0), а может редко читаются/изменяются эти апдейт-параметры, тем более пока экспериментировал не отловил предметов с 'num_items' ~= 0. Но как раз в ACDC (руководствуюсь последней универсальной версией) и прописано, что отсекаются последующие (древние версии игры не рассматриваю), да и по аналогии с подобными параметрами в др.классах - отсекается все что после ... P.S. В приведенном тобою куске кода из ACDC невозможно понять какая версия или хотя бы о каких пропертях идет речь в коде. Кусок из универсального не выкладываю, т.к. ... "много буковок" и много веток. ;-) Изменено 16 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Monnoroch 6 Опубликовано 16 Января 2012 Поделиться Опубликовано 16 Января 2012 Вот, добавил собственно то, что хотел: возможность множественных подключений слотов, возможность это запретить, некоторые настройки с этим связанные и вообще привел в порядок код. http://ifolder.ru/28140737 Ссылка на комментарий
xStream 86 Опубликовано 16 Января 2012 Поделиться Опубликовано 16 Января 2012 Artos, if updnum_items==0 then return end ЭТОТ кусок то понятен? Если ноль, то все, дальше ничего не читается из апдейта. И не пишется. Все, кто стоит на моем пути: идите нахрен и там погибните! © Ссылка на комментарий
Artos 99 Опубликовано 16 Января 2012 Поделиться Опубликовано 16 Января 2012 (изменено) xStream, опять ... ;-( Не буду надоедать, но обрати внимание, что пропущенное при чтении - НЕ скипится при записи(!) и из дефолтов вылезут несуществующие в 'data' проперти. (в ЧН/ЗП еще к этим поприбавится) P.S. Тут параллельно идет обсуждение о подписках/отписках, что конечно важно, но ... в теме (пере)записи нет-пакетов каждый байт/бит важен и при ошибке - никакие отписки не помогут. Поэтому и такая с моей стороны дотошность. (сорри, но это я еще не выверял каждый класс начисто, а только в черновую). P.P.S. Пока пишу некоторые куски с заделом/запасом для ЧН/ЗП расширения. Заменить break на return можно в конце (начисто), а вот если потребуется и забыть что завершил - можно долго в код всматриваться ... Сейчас у меня эта строка так: break --/ => return --/> Изменено 16 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
xStream 86 Опубликовано 16 Января 2012 Поделиться Опубликовано 16 Января 2012 Не опять, а снова. При чтении проблем нет. В записи я увидела и исправила. Добавлено через 10 мин.: Я еще не понимаю, зачем делать break, если однозначно надо завершать функцию... Все, кто стоит на моем пути: идите нахрен и там погибните! © Ссылка на комментарий
Monnoroch 6 Опубликовано 17 Января 2012 Поделиться Опубликовано 17 Января 2012 Вот, еще немного дополненная версия: дефолтные настройки вынесены из логики. malandrinus, теперь если в mon_utils.default_slot поставить unique = true, то получится ровно твоя система, но которую можно использовать более гибко http://ifolder.ru/28160609 Ссылка на комментарий
Andrey07071977 18 Опубликовано 19 Января 2012 Поделиться Опубликовано 19 Января 2012 (изменено) malandrinus, вопрос по проверке зависаний. Как я понимаю, колбэк заданный в level.add_call(...) вызывается с частотой апдэйта актера - не слишком ли часто это для проверки? Если, чисто теоретически, предположить что подписанно 100 функций, и не все сработают в течении 25 миллисекунд, то игра будет вылетать хотя не факт что колбэк именно завис. Или же есть ограничение что ВСЕ колбэки должны быть завершены в течении одного апдэйта? Соответственно, если таких ограничений нет, то не лучше ли сделать проверку допустим посекундно? Изменено 19 Января 2012 пользователем Andrey07071977 Ссылка на комментарий
Artos 99 Опубликовано 19 Января 2012 Поделиться Опубликовано 19 Января 2012 (изменено) Рабочий вариант модуля для работы с нет-пакетами в игре. - модуль сделан на базе xs_netpk и m_net_utils; - работа с нет-пакетами версий ТЧ/ЧН/ЗП (патч ТЧ 1.0004 и выше); - наличие единого вызова для всех игровых объектов (не исключающий указание конкретного класса); - раздельная работа с обоими типами пакетов (state|update), что не исключает и объединенный пакет данных; - максимально упрощенная работа с изменением свойств 'cse_abstract'; - облегченная адаптация под моды с новыми классами объектов (настройки в конфиг-файле); - автономность работы (коды необходимых хелперов интегрированы в модуль); - недоделаны только несколько "сложных" классов объектов типа smart_terrain ... ссылка: m_netpk_120120 (~21.2 кБ) Прошу заинтересовавшихся проверить (перед выкладыванием в общую тему по скриптам) и дать комментарии/замечания/пожелания. (в дальнейшем по работе модуля - в топик по скриптам) P.S. Обновил архив, исправлены отдельные ощибки, добавлен функционал классу шейперов. Изменено 20 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 19 Января 2012 Автор Поделиться Опубликовано 19 Января 2012 Andrey07071977, вопрос по проверке зависаний. Как я понимаю, колбэк заданный в level.add_call(...) вызывается с частотой апдэйта актера Вызывается с частотой fast_call, который, как я понимаю, вызывается с частотой пересчета физики. По идее даже быстрее апдейта, хотя на последних патчах у меня стабильно работает с частотой апдейта (или апдейт с частотой fast_call, уж не знаю). Это может также от машины зависеть, я на самом деле так и не понял от чего. не слишком ли часто это для проверки? Если, чисто теоретически, предположить что подписанно 100 функций, и не все сработают в течении 25 миллисекунд, то игра будет вылетать хотя не факт что колбэк именно завис. Этот вызов никак не может сработать во время работы другого вызова. Lua однопоточный, все вызовы или следуют один за другим или вызывают друг друга. Для проверки специально используется отдельный механизм чтобы исключить второй вариант. Поэтому эта проверка срабатывает строго в моменты между вызовами. Значит, если счетчик не нулевой, то было зависание. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Andrey07071977 18 Опубликовано 20 Января 2012 Поделиться Опубликовано 20 Января 2012 malandrinus, что-то совсем я запутался, ведь еслиЭтот вызов никак не может сработать во время работы другого вызова, то по идее при зависшем колбэке вообще ничего не должно сработать, ведь работа функции так и не завершилась. Ссылка на комментарий
Malandrinus 615 Опубликовано 20 Января 2012 Автор Поделиться Опубликовано 20 Января 2012 Andrey07071977, подвисание одного колбека никак не влияет на работу других. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Andrey07071977 18 Опубликовано 20 Января 2012 Поделиться Опубликовано 20 Января 2012 (изменено) Пока ничего не понял, одно противоречит другому Буду вечером разбираться в синхронизации колбэков Изменено 20 Января 2012 пользователем Andrey07071977 Ссылка на комментарий
Malandrinus 615 Опубликовано 20 Января 2012 Автор Поделиться Опубликовано 20 Января 2012 Andrey07071977, многие игровые колбеки независимы друг от друга. Подвисание колбека по-простому означает, что такой колбек перестает вызываться. Скажем, произошел сбой в апдейте биндера актора. Апдейты биндера больше не вызываются. Соответственно, не работает обновление логики, работающее на этих апдейтах. Но все остальное работает. Скажем, колбек на взятие предмета будет продолжать работать. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Andrey07071977 18 Опубликовано 20 Января 2012 Поделиться Опубликовано 20 Января 2012 malandrinus, ну да именно так я и предполагал. Допустим, ради примера, навесили на колбэк взятия предмета некую функцию которая будет выполнятся больше одного интервала апдэйта, например тот же перебор значений get_actor_float(...) из твоего x-ray extensions, от 1 до скажем 1000000. Этот вызов, во всяком случае на моем лаптопе, займет больше секунды. Так вот я не совсем понимаю почему проверка зависаний не сработает до тех пор пока не закончится данный вызов - ведь колбэки разные? Ссылка на комментарий
Malandrinus 615 Опубликовано 20 Января 2012 Автор Поделиться Опубликовано 20 Января 2012 Andrey07071977, почему проверка зависаний не сработает до тех пор пока не закончится данный вызов - ведь колбэки разные? Потому что Lua строго однопоточный и движок игры в общем тоже однопоточный, по крайней мере игровая логика вся работает в один поток. Пока не закончится один вызов, другой не начнется, даже если пришло его время. Это в общем-то легко проверить. Поставь в Lua бесконечный цикл - это подвесит всю игру. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Andrey07071977 18 Опубликовано 20 Января 2012 Поделиться Опубликовано 20 Января 2012 (изменено) malandrinus, кажется я понял в чем дело. Как бы ты сформулировал (блин, перевожу с Английского на Русский, дожились) "зависание колбэка"? В моем понимании, это происходит при ошибке в исполнении функции, когда был вход но небыло выхода. При этом происходит подвисание определенной нити, остальные при этом спокойно продолжают работу. Изменено 20 Января 2012 пользователем Andrey07071977 Ссылка на комментарий
Malandrinus 615 Опубликовано 20 Января 2012 Автор Поделиться Опубликовано 20 Января 2012 Andrey07071977, происходит исключение, его ловит движок, но по каким-то причинам решает не вылетать. Из-за аварийного завершения нить Lua, в которой работал вызов, остается в аварийном состоянии и больше не срабатывает. Как-то так, думаю. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Nazgool 250 Опубликовано 20 Января 2012 Поделиться Опубликовано 20 Января 2012 (изменено) Конечно "спецы" давно знают (и не посвящали почему-то?). Я далеко не спец, и тоже молчал. но... Чего-то вдруг вспомнил, что в Сталке используется 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 Изменено 20 Января 2012 пользователем Gun12 Ссылка на комментарий
Andrey07071977 18 Опубликовано 21 Января 2012 Поделиться Опубликовано 21 Января 2012 В продолжение дискуссии о колбэках… Так как не имел четкого представления о том как происходит синхронизация колбэков в сталкере, решил поэкспериментировать и разобраться. Для этого попытался смоделировать нечто подобное в чистом 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 в общем и конкретно колбэков в сталкере. Если есть замечания, вопросы, и тд. пишите. Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти