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

Курилка программистов


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

Я бы сказал, что таймеры АМК, при всём к ним уважении, не подлежат никакому рефакторингу. Для своего времени это был прорыв, но сейчас это уже позавчерашний день. К использованию рекомендуются интегрированные системы компонентов, включающие систему хранения, событий и таймеры. Именно все три, поскольку они друг на друге строятся. Из готовых к применению я лично знаю набор от xStream и мой. Оба имеются в составе OGSE и существенной доработки не требуют. При желании можно перенести в любой мод. Собственно мои таймеры - даже не таймеры, а универсальные сохраняемые объекты с задаваемым условием времени жизни. Одно из условий - по времени, и тогда это таймер. А так можно задать любое условие и сделать объект, ждущий какого-то условия, чтобы совершить некое заданное действие. Используется это в массе разных компонент.

 

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

 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

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

 

update:

Написал и после уже подумал, что даже и это не будет 2015 г., поскольку движок на данный момент мягко говоря подустарел, и реально шагом вперёд было бы активное обсуждение, чего там можно улучшить. Хотя это конечно совсем другая история.

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

 

 

легким движением руки максимальный объем расширяется до бесконечности

Каким образом?

 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

 

 

Так что "активное обсуждение" - простите, чепуха. Это будет куча маленьких группок каждая будет вещать со своей колокольни и не слушая других, делать то что ей самой надо.

Я возможно слишком формально выразился. Я вовсе не имел в виду создание "контактной группы на высшем уровне" и "обсуждение в определённом формате". Подобные инициативы как не работали, так и не будут работать. Имелось в виду, чтобы вообще начать использовать сырцы и собираемый движок. Тогда начнутся и разговоры, а что там внутри и как это изменить. Сами собой начнутся.

Есть скриптовые наработки, которые можно оставить, хотя бы как отправной пункт. Но вещам типа нетпакетов или x-ray extensions место на свалке. Я просто не понимаю, как можно тратить на них время сейчас, когда есть такие возможности.
  • Согласен 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

 

 

По сну - меня в принципе вполне устроило то, что выложено здесь. Секунды ловить - неинтересно. Да и в общем-то не планируется ни каких таких "длинных" апдейтов во время "промотки".

Как-то я это пропустил.

 

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

 

Твою позицию по таймерам я, признаться, не понял. Они совсем не нужны, по твоему? Мне просто интересно понять, а чем их можно заменить.

  • Согласен 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

это можно назвать удобством

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

инкапсуляции и полиморфизма нет

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

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

@Viнt@rь, по себе могу сказать, что мне пришлось поработать над собой, чтобы проникнуться идеями ООП и кое-какими азами ОО дизайна. Это потребовало усилий, а помог в этом дедушка Borland Pascal ещё под DOS. Чем-то меня тогда заворожил и язык и идея ООП.

 

=) Тем более мои усилия были героическими, что начинал я программировать на диалектах Basic-а, где можно было из программы манипулировать текстом программы и переходить по номеру строки. Лепота. Как вспомню, так вздрогну.

  • Нравится 1
  • Согласен 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

у нас принято писать self-explaining код

self explained код - это миф. Что можно сделать, чтобы код говорил сам за себя, кроме того, что красиво его отформатировать и дать переменными и функциям внятные имена? Я это в обязательном порядке делаю, но этого совершенно недостаточно.

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

-- ищем ближайшего бандита

Второй тип - декларация состояния. Я поясняю себе, в каком состоянии находится (должна находиться) моя программа в этой точке. Что я ожидаю в такой-то переменной и т.д. Переменная то может и иметь говорящее название, но её значение может быть не определено, и она может быть не одна. Кроме того, я выделяю так подмножество важных именно в данный момент данных Например (после какого-то вызова):

-- имеем в массиве xxx список таких-то объектов yyy или код ошибки в zzz

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

Третий тип комментариев, возможно спорный, - описание моего кода литературным языком. Часто я считаю необходимым дать внятное описание того, что детально делает этот фрагмент. К примеру:

for id=0,65535 do -- среди всех объектов на уровне
    local cobj = level.object_by_id(id)
    if cobj and cobj:is_stalker() and is_bandit(npc) then -- ищем сталкеров-бандитов
        if distance_between(db.actor, cobj) < 20.0 then -- в пределах 20 метров от актора
            -- и что-то ужасное с ними делаем
        end
    end
end

Соберите все эти комментарии во фразу и вы получите: "найти бандитов в радиусе 20 метров от актора и оторвать им бошки". Это в сущности декларация о намерениях, просто более подробная и размазанная уже по фрагменту кода. Я здесь конечно утрировал, поскольку вероятно не буду комментировать именно такой фрагмент, но идею надеюсь изложил. Хочу сказать, что подобные комментарии я часто пишу перед написанием собственно кода. Они мне помогают.

 

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

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

 

ЗЫ: Вероятно, пысы тоже думали что-то такое насчёт self explained кода, и потому не комментировали вообще, т.е. совсем никак. Насколько я могу понять, это вызвало у второго поколения разрабов лютую радость. Это когда первый состав свалил, а новые люди стали допиливать рендер и т.п. Мы все помним, чем это закончилось.

Изменено пользователем malandrinus
  • Нравится 1
  • Согласен 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

усложняет его модификацию. Увеличил радиус поиска бандитов - надо править в двух местах: в коде и в комментарии :)

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

 

Хорошо было бы конечно забацать итератор по онлайновым объектам... Тогда было бы вообще, как в комментариях:

 

аггрегаторы, итераторы

 

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

 

ЗЫ: шучу, где-то валяется, но уже конечно не найти.

ЗЗЫ: но переборы - действительно зло

  • Нравится 2
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

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

 

 

 

тут не комментарии нужны, а ртфм

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

  • Согласен 2
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

@KD87, код прекрасен. Замечательная иллюстрация ситуации "всё плохо". Более того, плохо не в этом коде, который только верхушка айсберга. Всё плохо там, откуда эта функция вызывается. Отложим в сторону несоответствие комментария и действия, предположим, что на самом деле надо удалить объект из инвентаря актора. Итак, здесь две проверки. Первая - что инвентарь актора содержит объект с заданной секцией. Здесь на самом деле неопределённость, и требуется знать контекст вызова: для чего и в какой ситуации вызывается эта функция. Сразу можно сказать, что комментарий - шлак, поскольку не объясняет, зачем выполняется действие. Он должен звучать как, скажем, "отдаём динамит" или "чистим инвентарь от динамита". Разница есть, поскольку такие разные варианты будут подразумевать разные проверки.
"Отдаём динамит" подразумевает, что функция вызывается в конце диалога сдачи квеста. Динамит при этом обязан быть в инвентаре, а иначе просто недоступна будет ветка. Тогда и проверка на самом деле не нужна. Если же мы имеем этот вариант, и такая ошибка вдруг возникает, то значит криво сделан наш квест. На этот случай надо ставить отладочную проверку:
ASSERT(dynamite, "[delete_dynamite_box] no 'dynamite' object in actor's inventory")
Если вывалится, то надо не в эту функцию заплатки ставить, а чинить структуру квеста или диалогов.
Вариант "чистим" может скажем вызываться по любому окончанию квеста, провальному или нет, для очистки инвентаря (странный вариант, ну вдруг нам так захотелось). Тогда и впрямь возможна ситуация, что динамит есть/динамита нет. Тогда и проверка нужна, причём именно такая: если нет, то ничего не делаем. Это на самом деле был бы плохой дизайн, поскольку одна функция совмещает в себе две.
Следующая проверка на наличие серверного объекта - вот это реальное зло. А как вообще может так случиться, что онлайновый объект есть, а серверного нет? Такого в принципе быть не должно. Ну один вариант есть - если мы умудрились вызвать эту функцию два раза подряд. И опять, не в этой функции должны быть такие уродские заплатки, а вызывающий код надо чинить. Здесь же опять надо поставить отладочную проверку:
ASSERT(delete_dynamite, "[delete_dynamite_box] found no server object for dynamite! Check the caller code.")

Итого, допустим функция "отдаёт" динамит Волку. Тогда, включая внятные комментарии, я бы написал так:

-- Отдать динамит Волку (удалить из инвентаря)
function delete_dynamite_box(npc, actor)
    local dynamite = db.actor:object("dynamite")
    ASSERT(dynamite, "[delete_dynamite_box] no 'dynamite' object in actor's inventory")
    local sdynamite = alife():object(dynamite:id())
    ASSERT(sdynamite, "[delete_dynamite_box] found no server object for dynamite! Check the caller code.")
    alife():release(sdynamite, true)
end

А в релизе и вовсе можно написать просто

-- Отдать динамит Волку (удалить из инвентаря)
function delete_dynamite_box(npc, actor)
    local sim = alife()
    sim:release(sim:object(db.actor:object("dynamite"):id()), true)
end

Ведь в релизе уже не должно возникнуть ситуации, когда этот код может сбоить.

Изменено пользователем malandrinus
  • Согласен 1
  • Полезно 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

Есть. Во-первых, болт, серверного объекта просто не имеющий.

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

 

А вот это

подряд отрабатывают два скприпта, удаляющих один и тот же объект,

и это

игровым объектом, полученным через drop-коллбэк.

Вот типичная ситуация. Биндер и в нём колбек на дроп, или юзание, или ещё какую операцию с предметом в инвентаре. Наблюдаем нечто вроде:

 

function actor_binder:on_item_drop(obj)
    ...
    supermod1.item_drop(obj)
    supermod2.drop_item(obj)
    ...
    supermod9001.on_item_drop(obj) -- здесь взяли и удалили предмет
    ...
    supermod100500.on_drop(obj)
    ...
end

 

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

 

А теперь как это решается при использовании ивентов (на примере моей системы с некоторыми упрощениями).

 

function actor_binder:on_item_drop(obj)
    self.am:call("on_drop", obj, sim:object(obj:id())) -- посылаем сигнал (генерим событие)
end

 

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

 

function attach(sm)
    -- подпишем функцию на событие
    sm:subscribe({signal = "on_drop", fun = this.on_drop_item})
end
 
function on_drop(item, sitem) -- эта функция вызовется автоматически
    -- делаем здесь что-то с предметом
end

Аналогично в системе xStream:

 

-- в биндере
function actor_binder:on_item_drop(obj)
    event("actor_item_drop"):trigger({what = obj}) -- посылаем сигнал (генерим событие)
end

-- в модуле
function init()
    event("actor_item_drop"):register(on_drop_item)
end
function on_drop(e) -- эта функция вызовется автоматически
    -- делаем здесь что-то с предметом, передаваемым через поле таблицы e.what
end

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

 

function on_use(item, sitem)
    if is_item_of_some_type(item) then -- определяем тип предмета
        -- здесь делаем что-то, что хотели сделать по юзанию этого предмета
        -- и удаляем его, поскольку он одноразовый
        alife():release(sitem, true)
        return true -- сигналим, что остальным колбэкам срабатывать не надо
    end
end

Как видим, правильная организация кода исключает необходимость лишних проверок.

 

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

 

У ивентов/событий есть и другие плюшки: централизованная отладка зависших колбэков, возможность динамического отписывания для экономии и т.п. Однако главное, что я выше уже говорил - для подписывания не нужно лезть в биндер (или вообще туда, откуда генерится событие). Код становится намного более модульным, существенно снижаются затраты на интеграцию и отладку новых модулей.

 

 

  • Спасибо 1
  • Согласен 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
почему не имеют ? Серверного объекта нет, а вызовы вполне могут и иметь.

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

 

На самом деле - вот эта ограничительная мера при использовании ивентов - это тоже элемент дисциплины. Я же не могу никак заставить следовать этому требованию всех пользователей системы событий. Если разработчик удалит объект, а return true не поставит, то всем остальным придётся по старинке проверять на наличие серверного объекта. Разница однако в том, что при старом подходе все обязаны были это делать за неимением другого выхода, а при использовании ивентов/событий есть альтернатива: вместо того, чтобы всем проверять, проверить только одному. Опять же - это в итоге экономит время разработки.

 

 

 

становится возможным быстрое манипулирование модулями

В точку! Я так использую свою систему для создания отладочных модулей. Такой модуль - это вообще отдельный файл, кидаешь его в папку со скриптами и он подключается сам (без единой строчки кода где-либо ещё). Страшно удобно. Можно сделать какой-то инструмент отладки, мониторинга каких-то объектов и т.п., который потом можно убрать, просто удалив файл. У нас так сделаны читовый телепорт, редактор/визуализатор зон и т.п. Я делал тулзу для настройки прожекторов: наводишь на прожектор, нажимаешь сочетание клавиш, объект запоминается, потом наводишь куда прожектор должен смотреть, нажимаешь другую клавишу - прожектор поворачивается, а в лог идёт точка направления для настройки логики. В репозитарий я этот файл не включаю, поэтому в игре его нет. При этом, нигде он не прописан, так что его невключение ничего не рушит.

 

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

 

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

Изменено пользователем malandrinus
  • Согласен 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

 

 

локальность подключения к системе ивентов

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

 

 

 

гарантированность доставки события
С какой стати? Если предмет удалён, то потерян смысл доставлять сообщение дальше. Ещё раз хочу подчеркнуть. Огрызок объекта в виде его онлайновой части - это не повод думать, что объект ещё существует. Его уже нет, а значит нечего дальше передавать. Рассмотри это с такой точки зрения. Событие с объектом - это нечто вроде доставки почты. Мы идём по комнатам и спрашиваем "это для тебя?" Как нашли адресата, то отдали ему предмет. На этом всё, дальше доставлять нечего и некому.
  • Согласен 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

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

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

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

Вести коллекции предметов - плохая идея по многим причинам. Например потому, что вообще желательно не запоминать предметы между скриптовыми вызовами. Удаление движкового объекта не ведёт к автоматическому удалению его скриптовой оболочки. Другой скрипт может удалить предмет из коллекции, и мы получим скриптовую оболочку с мёртвым указателем внутри. Кроме того сама постановка вопроса: "что если в коллекции будут предметы, которые другой колбек удалит?" уже означает заведомые проблемы с управлением разделяемыми ресурсами. Я должен исключить такую ситуацию на уровне дизайна.

Мысль о том, что "все должны получить сообщение" тоже неверна. Сообщения могут быть и широковещательные, но вообще-то они как правило адресные и предназначены конкретному адресату. Здесь нет никакой дилеммы. Если сообщение по сути адресное, то оно и должно закончиться на его адресате. Остальным оно не предназначено. Если же сообщение широковещательное, то вряд ли один из адресатов будет заниматься безобразиями с данными, предназначенными всем. Если же будет, к примеру будет удалять объект, предназначенный всем, то это не проблема системы ивентов, а проблема либо дизайна либо исполнения моей системы. Это и надо лечить.

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

Вернусь к начальному тезису. Я могу выдумать много примеров, как не надо делать. Вариантов, как делать не надо, гораздо больше, чем правильных. Это чистая комбинаторика. Я могу рассыпать детали от механизма во многих сочетаниях, но работать будет только если они будут собраны определённым образом. Ну так и определитесь уже наконец, чего хотите. Хотите детальки красивыми стопочками выкладывать или всё-таки собрать нечто рабочее?

  • Согласен 3
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

У нас толпа модулей подписалась на то чтоб быть в курсе этого события. С какой стати, если кто-то из модулей взял и удалил сам объект, то остальным модулям нет смысла знать об этом событии?

 

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

 

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

 

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

 

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

 

 

Попробуйте отследить, кто слушает конкретный ивент

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

 

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

 

Список подписчиков на сигнал тоже получить не проблема, если сильно надо. Но чаще всего не надо, поскольку место зависания локализуется сразу.

 

 

Предмета нет - это не повод прекращать информационные действия связанные с ним.

Мне одному это утверждение представляется из области некромантии?

 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

@Zander_driver,

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

 

self.am:call("on_drop_"..obj:section(), obj, sobj)

--Тогда для в модуле для подписки на событие дропа аптечки сделаем так:
function attach(sm)
    sm:subscribe({signal = "on_drop_medkit", fun = this.on_medkit_drop})
end
function on_medkit_drop(obj, sobj)
end
Аналогично в системе Анны:

 

event("actor_item_drop_"..obj:section()):trigger({what = obj})

-- в модуле
function init()
    event("actor_item_drop_medkit"):register(on_medkit_drop)
end

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

 

self.am:call("on_drop_"..obj:section(), obj, sobj) -- дроп предмета с секцией
self.am:call("on_drop_"..obj:clsid(), obj, sobj) -- дроп предмета какого-то класса
self.am:call("on_drop_"..(obj:mass() > 10 and "heavy" or "lightweight"), obj, sobj) -- дроп предмета массой больше/меньше 10-и кг
и т.д. Пока не могу сходу сформулировать точный критерий, где надо остановится, но мне видится, что где-то надо.
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение

размножение ивентов в биндере актора - не с той стороны вообще, от сути вопроса.

Вот тогда я тебя не понимаю. А что тогда ты имел в виду и какое именно использование ивентов/событий на твой взгляд самое кошерное?

 

 

 

Позволю себе несколько пофилософствовать. Ивенты/события/сигналы/колбеки - это вообще реализация шаблона проектирования "наблюдатель". Суть идеи в том, что есть некий процесс, который идёт как идёт, и есть некое неопределённое заранее количество наблюдателей, других процессов, которые хотят что-то делать по факту происходящего в наблюдаемом процессе. Ключевое понятие здесь - "наблюдение", поскольку наблюдение неинтрузивно. Это означает, что наблюдаемый процесс ничего не делает для того, чтобы вызвать некие действия со стороны наблюдателя. И вообще не знает про наблюдателя, или наблюдателей, или их отсутствие, или что они собираются делать.

 

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

function actor_binder:on_drop(obj)
    ...
    if is_vodka(obj) and rainy_day() and self.object:money() > 100500 then
        some_module.gopstop_mi_podoshli_is_sa_ugla()
    end
    ...
end

будет явным нарушением этого принципа. С какой стати актору выдумывать себе неприятности? Иными словами, засорение логики актора не относящейся к нему логикой не есть хорошо. В биндере актора должно быть только то, что относится к актору (по-хорошему, почти ничего там быть не должно). Другой аспект - надо стараться уменьшать связи между разными компонентами, а здесь мы такую связь установили. Связи делают компоненты зависимыми друг от друга. Простейшая зависимость - удаление одного компонента приводит к неработоспособности другого. Я удалю модуль some_module.script и биндер актора будет валиться в месте обращения к нему.

 

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

 

function actor_binder:on_drop(obj)
    ...
    self.sm:call("on_drop", obj)
    ...
end

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

Изменено пользователем malandrinus
  • Нравится 1
  • Согласен 1
  • Полезно 1
 

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

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

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

 

Поделиться этим сообщением


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

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