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

Язык 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
Ссылка на комментарий

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

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

@Shredder, я ни слова не сказал, что я регистрирую одну и туже функцию несколько раз, т.к. это не логично, ведь она будет затираться последующими регистрациями. Именно поэтому пришлось сделать шесть функций  с разным названием, но проделывающие абсолютно одинаковый алгоритм действий. Нужно мне это как раз в практических целях, когда необходимо медленно восстанавливать разные свойства ГГ: здоровье, выносливость, радиацию, при этом абсолютно независимо, поскольку действие на какое-либо свойство может прекратиться в любой момент. А так-как функций - шесть, решил сократить код, за что, собственно, спасибо Charsi, Gun12 и malandrinus - ваши советы, оказались как нельзя к стати. 

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

По поводу скрипта xr_s.script. По мне так нет смысла использовать такой упрощённый и древний подход, при том что имеются продвинутые системы сигналов.
Я лично использую свою систему, но с таким же успехом можно использовать систему xStream. Вот последние версии (за песочницу xStream не уверен, это то, что мне доступно).

"Песочница" от xStream. В архиве вложен файл описания xs_sandbox_manual.txt

Менеджер сигналов. Описание добавлено комментариями в файле ogse_signals.script
 

  • Нравится 2
 

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

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

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

 

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

Можешь написать, в чём профит продвинутых систем? Глянул описание. В "Песочнице" принципиальной разницы не увидел, разве что пара плюшек (e:stop(), e:removeThisCallback()), но у меня такой надобности не возникало. В ogse_signals принцип регистрации практически тот же, про слоты не понял.

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

про слоты не понял.

 

слоты и сигналы

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

 

 

 

в чём профит продвинутых систем?

Пожалуйста:

xr_s.script

Регистрация нового колбека требует изменения системного по сути скрипта, что плохо само по себе.

Как следствие, добавляемый туда код каждый раз повторяет уже существующий (перебор таблицы и вызов), т.е. надо заниматься копипастой.

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

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

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

 

Система сигналов.

Регистрация колбека не требует изменения системного модуля. Нужно получить ссылку на экземпляр менеджера сигналов с помощью глобальной функции, после чего зарегистрировать колбек с помощью методов этого менеджера. Максимум для подписки модуля надо прописать его имя в таблице в отдельном файле. Таким образом, создание нового колбека полностью локализовано в том модуле, где этот колбек используется.

 

Регистрация минимальна, в самом простом случае всего одна строка. Никакого дублирования кода. Код перебора прицепленных колбеков один на всех, скрыт внутри менеджера сигналов и его не надо дублировать, да и знать о нём не надо.

 

Унификация вызова колбеков позволила добавить несколько дополнительных сервисов:

а) детектирование подвисаний движковых колбеков. Поскольку этот код интегрирован в менеджер сигналов, то он автоматически распознаёт подвисания всех прицепленных через эту систему колбеков. Ровным счётом ничего для этого делать не нужно.

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

в) взаимодействие между прицепленными к одному сигналами колбеками. Два случая, когда это особенно полезно - работа с инвентарными предметами и обработка нажатий клавиш (если такие колбеки добавлены). Вот к примеру пошла обработка съеденного инвентарного предмета. К событию съедания может быть прицеплено несколько колбеков, каждый из которых проверяет свой тип предмета и что-то делает, если это "его" предмет: сон от спального мешка, лечение от аптечки, диалог радио от радио и т.д. В зависимости от стратегии работы с предметом он либо удаляется движком либо скриптом и восстановить его скриптом надо или не надо. Если надо предмет удалить, то после этой операции продолжат срабатывать колбеки, которым передаётся уже удалённый предмет (серверной части нет). Во-первых, это потенциально вызывает сбои, если колбеки выполняют проверки, рассчитывая на существование серверной части. Т.е. даже если колбек ничего не собирается делать с предметом, а просто хочет что-то проверить, то всё равно можно получить вылет. Значит, надо навешивать дополнительные проверки, усложнять код. Кроме того, эти вызовы уже попросту лишние, поскольку тот колбек, которому этот вызов был адресован, уже отработал. Выходом является простой приём. Если колбек возвращает true, то менеджер сигналов завершает цепочку вызовов, и все оставшиеся в цепочке колбеки не срабатывают. Из тех же соображений это полезно при обработке нажатий клавиш и в ряде других случаев. Обработку возвращаемого значения выполняет менеджер сигналов в том самом унифицированном коде.

 

Ещё пара фишек, которые реализованы в моём менеджере:

Возможность подключения в качестве колбека метода класса. При регистрации надо передать ссылку на объект класса, так что при вызове колбека будет передан неявный аргумент self. С xSream у меня был диспут на тему полезности этой фишки, но я её использую в своих скриптах довольно активно. Одним из примеров является реализация таймеров. Таймер - объект класса. При создании он сам себя подписывает на событие апдейта (точнее один из своих методов), а после срабатывания - отписывает. Красота подхода оказалась в том, что пользователю-программисту даже не надо нигде хранить ссылку на этот таймер, поскольку он хранится как ссылка на self во внутренней таблице менеджера сигналов и автоматически удаляется при отрегистрации.

 

Автоподключение модулей. Страшно удобно для отладочных модулей. Используя эту фишку я могу написать модуль с колбеками, не изменив вообще ни строки ни в одном другом модуле. Всё, что надо сделать для его использования, просто поместить файл в папку scripts. Например, я могу написать модуль, который выводит на худ информацию об объекте под прицелом по нажатию сочетания клавиш (нужны естественно колбеки на клавиши), или удаляет объект под прицелом, или меняет его свойства, или телепортирует актора на три метра вперёд, или открывает окно тестового спавна и т.п. Не нужен модуль, просто убрал его из папки со скриптами.

 

Не знаю, насколько это фишка, но менеджеров сигналов может быть несколько. Когда я это делал, то планировал использовать это для более надёжного разделения пространств имён сигналов, но потом пришёл к мысли, что проще задать сигналу сложное имя, чтобы оно не пересекалось ни с чём. Тем не менее, возможность заведения нескольких менеджеров сигналов осталась.

В целом примерно так, хотя может и забыл что-то.

 

 

Система xStream, за исключением особенностей, которые я явно указал, функционально эквивалентна и обладает теми же достоинствами. Из различий могу отметить принятый стиль передачи аргументов в колбек. У меня передача аргументов традиционная, просто список аргументов. Они все будут переданы каждому колбеку для этого сигнала. В "песочнице" принят способ передачи всех аргументов через таблицу. В том числе в таблицу записывается код возврата, сигналящий о том, что этот вызов должен быть последним. В подходе с таблицей есть свои преимущества. К примеру, всегда строго один аргумент в колбеке, проще изменить/дополнить список аргументов колбека, выглядит более унифицированно и можно наладить общение между колбеками через эту общую таблицу (сомнительная возможность, колбеки лучше изолировать друг от друга). Такой подход используется в частности в системе делегатов в .NET (там конечно не таблица, а экземпляр класса). В целом, никто не мешает сделать так и в моей системе, и передавать не произвольное число аргументов, а одну таблицу. У меня это скорее вопрос соглашения, чем требования.

  • Нравится 4
 

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

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

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

 

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

Это типа func2 = function(val) func(val) end ?

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

 

Возьму стандартный пример :

function f(mem)
    return  function (val)
                return val * mem
            end
end

И вызов :

x = f(2)

Переменной x присваивается значение, возвращаемое функцией f

Функция f возвращает только что созданную (новую) функцию!

Т.е. на данный момент (после вызова - f(2))  x - это функция, которую позже можно вызвать

Вызывая f(2) , в функцию f передаётся аргумент - число 2. Это значение присваивается переменной mem - f(mem)

 

Но новая (возвращаемая) функция тоже использует значение с именем mem, поэтому для неё mem это то значение, которое было на момент вызова f

Т.е. в данном случае функция, которая сохранилась в x, "запомнила" внутри себя значение mem как 2 (в строке return val * mem)

 

И теперь, вызвав x (она же функция) можем передать в неё некое значение.

local v = x(3)

Функция x (которую мы получили) принимает аргумент val как число 3 и возвращает вычисленное значение (return val * mem)

val мы её только что передали. Это 3, а mem она "помнит" - это 2

В результате получим 3х2=6

 

Это позволяет "заряжать" функции различными данными, так как каждый раз вызывая f(mem) создаётся новая функция, в каждой из них храниться собственное значение mem.

А затем добавляя новые данные (как val) производить нужные действия.

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

Замыкания конечно полезная штука, но есть и недостатки. Замыкания отчасти пересекаются с идеей классов, в том смысле, что объединяет действия и состояние. Однако, полноценной замены классам никак составить не могут. Всё таки действие всего одно, да и всех прочих фишек классов нет. Неявность создания с одной стороны хорошо, поскольку не требует от программиста особенных усилий, с другой - эта же неявность может сильно усложнить сопровождение программы, а в итоге повысить общую стоимость разработки. О чём здесь речь, набор данных, связанных таким образом с функцией, программисту не виден и задаётся неявно, текстом функции. Разобраться в такой программе сложнее, чем в устройстве класса, где всё задано явно. Другой аспект, а если есть конфликты имён, переменные на разных уровнях с одинаковыми именами. Легко сделать ошибку и крайне трудно её выловить, поскольку всё неявно. Я бы из этих соображений принял некое эмпирическое правило, замыкание использовать только в случае количества внешних переменных не более скажем двух-трёх. Другой аспект, из-за неявности всей конструкции вряд ли получится как-то сохранить её состояние, что в другую переменную, что на устройство хранения, что ещё ограничивает область применения.

 

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

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

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

 

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

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

 

local pe = ps:get_element_by_bone_name("link") -- (C++ class physics_element)

-- pe считается как userdata

 

-- Этот метод вылетает с руганью, что в pairs передана не таблица

for k, v in pairs(pe) do
  Log(tostring(k).." = "..tostring(v))
end

 

-- Этот метод рабочий, но выводит только стандартные функции луавских мета-таблиц (__pow, __add, __index,...)

for k, v in pairs(getmetatable(pe)) do
  Log(tostring(k).." = "..tostring(v))
end

 

По интернету судя по всему работают оба этих варианта, так что не могу понять - то ли это особенность сталкерского луа, то ли я не правильно пытаюсь получить список методов объекта. Про lua_help.script знаю, интересует вариант без его участия.

(Обрезанный Lua 5.1 из ЧН)

Можно просто Shoker, форум АМК съел моё старое имя и не хочет отдавать о_О

Мастер аномалий на свою заднюю точку.

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

@*Shoker*,

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

  • Нравится 1
Ссылка на комментарий

Логично, погуглил - нужны методы class_info() и class_names()

Оба не подключены к игре. В игре нашёл тока class_registrator - таблицу, щас гляну что это. 
___
Ничего интересного, таблица с функциями для регистрации классов. Видимо без изменения luabind методы классов получить нельзя. 

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

Можно просто Shoker, форум АМК съел моё старое имя и не хочет отдавать о_О

Мастер аномалий на свою заднюю точку.

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

 

 


последствия обрезанного Lua

Не первый раз слышу про "обрезанный" Lua. Ерунда это. Lua в сталкере совершенно стандартный, точнее соответствует своей версии. Более того, включены опции для расширенной совместимости со старыми версиями. Например, можно взаимозаменяемо использовать string.gmatch и string.gfind. Библиотек некоторых нет, но это не относится к языку, да и исправляемо в общем.

Как совершенно верно заметил abramcumner, язык напротив расширен. В luabind для вызова методов классов используется метаметод "call" пользовательского объекта. Т.е. он внутри себя выбирает функцию, которую надо вызвать по аргументу этого метода - имени функции. Без специальных средств выяснить список методов класса не получится.

 

  • Нравится 1
 

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

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

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

 

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

-- Этот метод вылетает с руганью, что в pairs передана не таблица

...

По интернету судя по всему работают оба этих варианта,...

Интересно, каким это образом pairs, которая может работать только с lua-таблицами, разберёт, "судя по интернету" userdat-у?

Где такое написано в интернете?

 

Более того, включены опции для расширенной совместимости со старыми версиями

Да во всех версиях 5.1 работают "старые" string.gfind, table.foreach (i), table.getn.

Так что "более того" совсем и не более.

 

А luabind...на LuaForge столько всего, что можно прилепить к lua. Но вроде же говорилось только о стандартном наборе.

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

, в официальном описании версии 5.1 функции string.gfind нет, значит всё-таки "более". И я вообще-то пытался сказать, что Lua в сталкере не обрезанный, только и всего.

 

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

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

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

 

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

Интересно, каким это образом pairs, которая может работать только с lua-таблицами, разберёт, "судя по интернету" userdat-у?

pairs работает со всем, у чего есть метаметод __pairs. У юзердаты он тоже мог бы быть.

pairs (t)

If t has a metamethod __pairs, calls it with t as argument and returns the first three results from the call.

Otherwise, returns three values: the next function, the table t, and nil, so that the construction

for k,v in pairs(t) do body end

will iterate over all key–value pairs of table t.

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

Однако, полноценной замены классам никак составить не могут. Всё таки действие всего одно, да и всех прочих фишек классов нет

Lua в принципе очень маленький и подключаемый! к хосту язык программирования.

Чего же от него ещё требовать?

Замыкания это просто фишка. Не более того.

Но существует немало реализаций классов, которые мало в чём уступают монстру С(++).

Скорость выполнения, конечно, уступает. Но писать то на нём (нам, простякам) на порядок легче.

Поэтому выбора и не остаётся. Пишем как умеем. Замыкания - так замыкания. А если не поможет, помогут расширения

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

Есть такой вопрос.

В созданном скриптовом классе, на стадии __finalize обязательно следует всем переменным класса присваивать nil или уборщик сам уберет? В примерах смотрел, везде по-разному. Где-то делают, где-то нет.

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

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

  • Нравится 1
 

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

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

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

 

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

А вот такой вопрос назрел, когда допустим в Lua в функцию tostring() передаётся таблица или функция, то она возвращает что то вроде

table: 0xАдрес

 

Так вот я хотел уточнить два момента у знающих людей:

1) Чем является возвращаемый адрес, это относительный адрес от начала приложения  или же вообще адрес объекта во всей ОЗУ (я не очень разбираюсь как это работает, поэтому мог бред сказать)

 

Если что игра возвращает адрес пустой таблицы типа такого: FFBEA6C8.

 

2) Есть ли методы (чистые, без ковыряния исходников Lua) получения адреса у любого объекта?  
 

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

Можно просто Shoker, форум АМК съел моё старое имя и не хочет отдавать о_О

Мастер аномалий на свою заднюю точку.

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

@*Shoker*,

1. В реализации под виндой скорее всего полный адрес в виртуальном пространстве процесса. Должен быть уникальным, чтобы можно было сравнивать на равенство. Я думаю, не имеет смысла задумываться о точной природе этого числа. Использовать его по-любому не получится никак, кроме как сравнить с другим таким же числом и выяснить, равны они или нет.

 

2. Методов нет (по крайней мере я таких не знаю). Опять же, если подумать, то вряд ли имеет смысл пытаться получить такой адрес. Это будет сильная привязка к платформе и кроме того к реализации самого языка. Объекты же завёрнуты в некие внутренние структуры, да и указатели, вообще говоря, не всегда можно представить одним числом.

 

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

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

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

 

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

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

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

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

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

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

Войти

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

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

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