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

Багфикс к таймерам.

http://rghost.net/35897685

 

Monnoroch,

почему ASSERT, зачем такая жестокость? Обычно в такой ситуации бросают исключение, потому на мой взгляд логичнее было бы просто ничего не сделать и как-то сообщить пользователю, что он не совсем прав. А то сразу assert делать слишком сурово.

В добавок мне видится ситуация, когда я хочу подписаться на эвент, если еще не подписан, и ничего не делать если подписан.

Понимаешь, убрать эту проверку можно. В принципе можно даже не ограничивать попытки подписать один и тот же слот два раза. По большому счёту системе всё равно, что в очереди два одинаковых вызова. Вызовет оба, а при отписывании отпишет первый найденный, а при втором отписывании - второй. Но на мой взгляд это лишено всяческого смысла и практического применения я этому не вижу. Предположим, с этим решили. Два раза не подписываем. Теперь вопрос, что делать, вылетать/предупреждать/тихо ничего не делать? Вот ты говоришь, что представляешь себе ситуацию, когда можно захотеть подписаться на сигнал и при этом неизвестно, подписаны мы уже или нет. Но дело в том, что я то как раз такой ситуации из моего собственного опыта не вижу. Вот два примера подписывания. Первый, для глобальной функции при подписывании модуля. Но каждый модуль подписывается только один раз и никогда не будет подписан второй раз. Или скажем в таймерах, где подписывание методов происходит в конструкторе класса. Конструктор не может быть вызван более одного раза для объекта, так что даже теоретически подписывание не произойдёт дважды.

Если пытаться себе представить некую необычайно динамическую ситуацию с частым подписыванием/отписыванием некой функции, то как быть с парностью этих действий? Я имею в виду, что вот мы подписали, затем подписали ещё раз, затем ещё и сделали это неопределённое число раз (ведь именно это хочется, не так ли). Теперь надо отписать. Можно конечно принять ту же идеологию и для отписывания: есть слот - отписали, нет - ну и фиг с ним. Но ведь как бы подразумевается, что между подписыванием и отписыванием идут вызовы, ради которых мы это делали. А выходит, что мы даже не знаем, шли они или уже давно закончились, поскольку парность вызовов порушена.

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

Я надеюсь, что смог объяснить, почему у меня там стоит ассерт. Это вообще то не столь уж значительно дело. Честно. Если ты действительно найдёшь практическую ситуацию (не придуманную "на всякий случай"), когда этот ассерт будет мешать, ну так и убери его. Делов то на пару секунд. Это ведь не мой персональный способ усложнить себе и другим жизнь. Я пытался сказать, что эта техника помогает избегать ошибок. Она на самом деле экономит мне время.

 

Замена ассерта на более мягкую обработку ошибки позволит тут избавиться от ветвлений типа

if not obj:is_already_subscribed(slot) then
obj:subscribe(slot)
end

Исходя из всего сказанного ранее, я на самом деле предпочёл бы, чтобы такой фрагмент стоял в моём коде именно в таком виде. В этом случае я бы явно видел, что вот здесь я преодолеваю ограничение безопасности. Повторюсь только, что если этот код вдруг стал появляться в большом количестве, то это повод задуматься о проблемах дизайна.

насчёт этого

Либо добавить signals_mgr:subscribe_silently() :)

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

 

К тому же с ветвлением obj:is_already_subscribed вызывается дважды.

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

 

xStream,

Опять разговор о фломастерах начинается. Только вот твои фломастеры у тебя никто не отбирает :wub:

Ох, Аня, ну где я что-то у тебя отнимаю? Если ты опасаешься, что я полезу в твой код всё там менять, то не переживай, не полезу =)

 

Andrey07071977,

- выкладывай :). Перепробовал уже с пяток разных методов, ни один не нравится, последнее на чем остановился это то с чего начал (1/uninitialized).

один из методов - использование функции fail, добавленной в рамках проекта x-ray extensions. Это собственно движковый вылет. Второй метод, использование консольной команды "quit", которая незатейливо закрывает игру.

 

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

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

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

 

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

malandrinus,

Ох, Аня, ну где я что-то у тебя отнимаю? Если ты опасаешься, что я полезу в твой код всё там менять, то не переживай, не полезу =)

Глупости говоришь. Мне все равно, кто что будет делать, я свой сделала. Ты просто постоянно высказываешь мысли так, как будто это непреложная истина в конечной инстанции. Или как будто тебя лишают возможности использовать тот стиль программирования, к которому ты привык. Я не понимаю, как такое можно вообще в принципе сделать (запретить). Ты всегда можешь делать так, как хочешь, перед тем как использовать какой-то компонент: сделать 100 проверок и впихнуть 40 ассертов - дело то твое. Мягче надо быть, мягче.

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

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

malandrinus,

Я имею в виду, что вот мы подписали, затем подписали ещё раз, затем ещё и сделали это неопределённое число раз

Хранить счетчик подписываний, а не подписывать каждый раз.

 

А пример я приводил: вот у меня класс хелпер какой-то. Мне присылают обьект и список эвентов на которые подписать.

Задача - подписать на все. Ну понятно же, что не хочется каждый раз проверять - подписан ли уже, и если нет, то подписывать, хочется же сделать код прозрачней.

Ну вот метод:

function subscribe_all(mgr, slots)
  for _,v in pairs(slots)
    mgr:subscribe(v)
  end
end

 

Допустим код одной схемы вызывает:

helper.subscribe_all(mgr, {s1,s2,s3})

А второй:

helper.subscribe_all(mgr, {s2,s3,s4})

Вон как все красиво. Да, можно всунуть if, но как-то уже не то.

Хотя вот я тут подумал - если просто в сам класс подписывателя инкапсулировать этот subscribe_all с if-ом, то будет все хорошо в плане использования.

 

По поводу этого:

когда этот ассерт будет мешать, ну так и убери его...До тех пор у меня такой функции не будет.

Я думал обо всей системе, как о библиотеке, у которой есть интерфейс а реализация меня волновать не должна.

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

 

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

 

Ты не пойми неправильно, я не пытаюсь учить ученого, просто мне показался странным подобный дизайн модуля, который потенциально может использоваться кучей народу в куче модификаций :)

 

 

Во, кстати, я еще досмотрел и такой вопрос: почему сигнал встраивается в слот? Можно же было бы сделать как-то так:

obj:subscribe(signal,slot), тогда один слот мог бы быть подпинан на множество сигналов в разное время разным кодом.

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

http://dl.dropbox.com/u/46539648/xs_scripts.rar

Все скрипты в куче.

-----------------------

Допилена работа с нетпакетами:

- Вариант, когда собрались менять абстрактную часть, а объекта уже нет.

- Скелет, скелет - надо проверить, грустняша...

- Ну и описание, вроде тянет на мануал. (можно использовать as is, а вот для абстрактной части пакетов придется попотеть)

Изменена слегка песочница:

- проверка на зависание делается иначе, спасибо malandrinus

- как следствие, за ненадобностью удален метод :hangCheck() у ивентов.

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

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

xStream, часть пакета, свазанная со skeleton_flags - именно в STATE части. Вот кусок псевдокода из CSE_PHSkeleton::STATE_Read:

  NET_Packet::r_stringZ(tNetPacket, &v4->startup_animation);
  NET_Packet::r(tNetPacket, &v3->_flags, 1u);
  NET_Packet::r(tNetPacket, &v3->source_id, 2u);
  if ( v3->_flags.flags & 4 )
    v3->vfptr->data_load(v3, tNetPacket);

Идет вызов виртуальной функции, в конечном счете вызывается SPHBonesData::net_Load(). Что читается в ней, я уже в этой теме "типа" писал.

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

Окей, только при попытке это сымитировать именно так, как ты написал, приводит к жопе под названием CTD

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

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

xStream, но чтение-то заблокировано. Вот тут, например:

if prop.name~="skeleton" or not (bit_and(ret.skeleton_flags, 4)==0) then

Как это выглядело при включенном чтении?

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

Если бит включен, то читается скелет, той самой функцией - _r_skeleton

Если выключен, то скипается.

То есть - выглядело точно так же. Выключено простым комментированием

        --{ name = 'skeleton',        type = 'skeleton' }

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

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

xStream, в писалке '_w_skeleton' все же не добавила: pk:w_u16(#val.bones) --/< bones_count

function _w_skeleton(pk, val)
    for i=1,4 do --/ u16x4 == u64
        pk:w_u16(val.bones_mask[i])
    end
    pk:w_u16(val.root_bone)
    pk:w_vec3(val.ph_angular_velosity)
    pk:w_vec3(val.ph_linear_velosity)
    pk:w_u16(#val.bones) --/< bones_count
    for _,bone in ipairs(val.bones) do
        _w_q8v(pk,bone.ph_position)
        _w_q8v(pk,bone.ph_rotation)
        pk:w_u8(bone.enabled)
    end
end

или я ошибаюсь? (сам пока пытаюсь методом тыка ...)

Также в классах:"net_alife_mounted_weapon" и "net_alife_object" ошибка с 'self.object', т.к. он нигде не определяется.

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

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

Monnoroch,

Не надо никаких сообщений в лог. Это сажает производительность

1. Для современных систем однострочные выводы в лог, как слону дробинка, тем более что ты не в update, надеюсь, ставишь подписки :)

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

 

П.С. malandrinus, прошу понять правильно, не критикую и не навязываю точку зрения, просто "разбор полетов" (feedback)

П.П.С. Про quit знаю, за fail спасибо, попробую

 

Изменено пользователем Andrey07071977
Ссылка на комментарий
Andrey07071977, Так ведь нет же, я как раз и пытаюсь протолкнуть идею, что двойные подписки - совершенно нормальное явление. Потому и отлавливать их сомнительный профит, разве только если в конкретных случаях.
Ссылка на комментарий
xStream, в писалке '_w_skeleton' все же не добавила: pk:w_u16(#val.bones) --/< bones_count

Блииииииин, точно. Надо снова проверять.

Также в классах:"net_alife_mounted_weapon" и "net_alife_object" ошибка с 'self.object', т.к. он нигде не определяется.

Прошу обратить на super(obj) во всех конструкторах! Это вызов родительского конструктора, то есть net_base:__init(obj), туда передаается и там и присваивается.

Так что ошибки нет: self.object - есть ВСЕГДА. Мы же должны помнить объект, из которого читаем / в который пишем.

ЗЫ Там же и self.skip задается, не смутило, что тоже не "определен"?

 

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

Гыгы, а про передачу skip я и забыла совсем...

http://dl.dropbox.com/u/46539648/xs_netpk.script вот здесь подправленный по идее скелет и везде добавлена передача скипа

Update. Скелет заработал, вылетов не обнаружилось...

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

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

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

Monnoroch,

двойные подписки - совершенно нормальное явление

На мой взгляд, при правильной организации кода, таких случаев не должно возникать вообще. Приведенный тобой пример, может произойти при неаккуратном совмещении модов с одноименными глобальными (_G) функциями подписчиками, но это опять-же проблема девелопера, на этапе совмещения

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

xStream, в "net_base" запоминается под self.obj, а не self.object ...

А по self.skip не стал ничего писать, т.к. в своем варианте (в моде) использую управляемое получение пакета, т.е. если НЕ потребно читать весь (c update) - то и читается только state. Это и чуть ускоряет и экономит ресурсы и снижает вероятность ошибок при полной перезаписи пакетов, тогда, когда нужно порою всего-то задать типа story_id. Т.о. флаг 'skip' передаю (если требуется) уже в вызов при получении пакета (полного или частичного) и соответственно протрассировал его по классам.

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

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

Monnoroch,

Хранить счетчик подписываний, а не подписывать каждый раз.

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

 

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

 

А пример я приводил: вот у меня класс хелпер какой-то. Мне присылают обьект и список эвентов на которые подписать.

Извини, так в общем нельзя. Что за хелпер, что за объект и кто может так присылать, что вы не в состоянии договориться, кто и что подписывает? Для объектов я выше приводил стратегию работы с ними. Помести подписывание в их конструктор, он с гарантией отработает один раз за всё время жизни объекта.

 

Допустим код одной схемы вызывает:

helper.subscribe_all(mgr, {s1,s2,s3})

А второй:

helper.subscribe_all(mgr, {s2,s3,s4})

Вон как все красиво. Да, можно всунуть if, но как-то уже не то.

Хотя вот я тут подумал - если просто в сам класс подписывателя инкапсулировать этот subscribe_all с if-ом, то будет все хорошо в плане использования.

Я признаюсь, не понял ничего. Можешь привести упрощённую, но конкретную ситуацию. В терминах реальной задачи, пожалуйста.

 

Я думал обо всей системе, как о библиотеке, у которой есть интерфейс а реализация меня волновать не должна.

Тогда мирись с дизайном автора =)

 

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

Очень даже ошибка в логике. Как совершенно верно сказал Andrey07071977, кривое совмещение модов. В начале этого поста я привёл несколько ситуаций, которые это иллюстрируют.

 

мне показался странным подобный дизайн модуля, который потенциально может использоваться кучей народу в куче модификаций :)

Я почему-то считаю ровно наоборот. Это дизайн, который призван помочь в совмещении, позволяя раннее обнаружение конфликтов. Чтобы не быть голословным: совмещаю два мода, так уж получилось, что в обоих вдруг идёт подписка на некую функцию. Я совместил, запустил, получил вылет с предупреждением и указанием, какой слот пытается подписаться повторно. Пошёл и исправил. Всё, конфликтной ситуации нет, исправлена на ранней стадии вместо отложенного выискивания багов к примеру из-за в два раза быстрее тикающего счётчика или невесть откуда появляющихся лишних объектов.

 

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

 

Во, кстати, я еще досмотрел и такой вопрос: почему сигнал встраивается в слот? Можно же было бы сделать как-то так:

obj:subscribe(signal,slot), тогда один слот мог бы быть подпинан на множество сигналов в разное время разным кодом.

Так ведь наоборот, в сигнале хранятся ссылки на слоты, точнее на их дескрипторы. И вообще-то никто не мешает подписать одну функцию/метод класса на другой сигнал. Нельзя на один сигнал. Вообще-то, можно даже на один сигнал, но по одному в разноприоритетные очереди.

 

Andrey07071977,

В релизе нет необходимости вывода в лог и уж тем более крэшить игру

Это не совсем так. Логи игры при вылетах - результат работы именно таких ассертов. Без них было бы совсем кисло =)

(в разработке, кстати тоже нет смысла крэшить)

Именно крэшить, в этом вся суть! Ассерт проверяет условие, нарушение которого приводит систему в неопределённое состояние. После того, как это условие нарушено, продолжать работу нельзя, надо остановиться и разбираться.

 

И кстати, ассерты удаляют из релиза не потому, что бы они не крэшили релиз, а просто чтобы не занимали ресурсы на проверки. Подразумевается, что в релизе эти ассерты уже никогда-никогда не сработают =) Если по-серьёзному, то в нормально отлаженной программе именно так и происходит.

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

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

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

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

 

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

malandrinus,

Логи игры при вылетах - результат работы именно таких ассертов. Без них было бы совсем кисло =)
Про ассерты писал исключительно в контексте проверки двойной подписки, т.е. если даже каким то образом это проникло в релиз, то просто return, без ассерта, без лога, и без приведения системы в поломанный стэйт.

 

Именно крэшить, в этом вся суть!
Дело в том что у меня на втором мониторе всегда открыта дополнительная консоль из luacap от alpet - очень рекомендую при отладке, сразу видно что и где происходит. Т.О. в моем случае крэшить смысла нет, и так сразу видно что произошла ошибка. В штатных же ситуациях, наверное ты прав, лучше сразу дать знать что произошла ошибка.

 

Подразумевается, что в релизе эти ассерты уже никогда-никогда не сработают =)
Ну и шуточки у вас, гражданин прокурор :D

 

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

П.С. Ладно, из'ездили уже тему про ассерты, пора завязывать :). Итог, кому мешают - не сложно изменить.

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

Возможно не по теме топика пишу, но все же...

1. К примеру, уважаемая xStream, вот почему ты ограничиваешь себя стандартным обьемом для сохраняемых данных, в смысле почему бы не написать что-то на подобии лаунчера от Алпета: написать функции, которые бы сохраняли/загружали/удаляли данные в отдельный файл, в итоге не пришлось бы себя ограничивать тем мизерным обьемом для сохранения данных...

ЗЫ это касается не только xStream :ny_smile:

2. malandrinus, по поводу x-ray extensions, не знаю говорили ли тебе про это или нет, но если врезать: "0x1022398D 5 global_space_ext" в xrGame.dll, то оно приводит к битым сейвам...

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

malandrinus, Ситуация простая: некий мододел захотел на непися повесить поведение при неком скриптовом событии - например при выбросе. А выброс, так как наша система работает вокруг слот-сигнальной модели, генерирует сигнал. Ну что делает мододел - подписывает слот на сигнал.

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

Слот при этом бутет выглядеть как-то, например, так:

slt = {signal = "blowout", self = obj, fun = obj.hit}

и так для всех сталкеров.

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

А если разрешить двойное подписание, то все разруливается: второе подписывание элементарно игнорируется, вернее увеличивается счетчик подписываний до двух. А когда кто-то отписывает его, счетчик уменьшается до одного. А потом до нуля - и тут физически происходит отписывание.

А сигнал, сколько бы раз на него слот не подписался, вызывает его, естественно только один раз, зачем ему больше? Хотя тут можно даже параметр сделать, это дело настраивающий: один или несколько.

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

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

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

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

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

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

Войти

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

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

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

AMK-Team.ru

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