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

Malandrinus

Жители
  • Число публикаций

    1 930
  • Регистрация

  • Последнее посещение

  • Дней в топе

    13
  • AMKoin

    160 [Подарить AMKoin]

Весь контент пользователя Malandrinus

  1. Malandrinus

    X-Ray extensions

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

    OGSE - Обсуждение и прохождение

    Чтобы такое говорить, надо объективно знать, какую именно часть скриптовой базы составляют скрипты от АМК, или хотя бы отдалённо унаследованные от АМК. Можешь дать такую статистику?
  3. А критерий эффективности какой?
  4. На мой взгляд, автор данной статьи попросту оправдывает свою лень, призывая в помощь Силу Самодокументированного Кода. Как там Аня сказала? "он не мифический, но недостижимый". Признаться, не хочется спорить с этим перцем. Я просто предлагаю поглубже познакомиться с исходниками x-ray. Там неплохой код в смысле самодокументации. Адекватные названия переменных и функций, отформатировано... Желаю удачи с разбором. Понадобится. ну как же? По большому счёту, низкоприоритетные - это почти все периодические. Исключением являются те, что подразумевают некую визуально заметную реакцию, и ещё некоторые. Т.е. если к примеру мне надо сделать что-то с предметом по факту его создания в инвентаре, но при этом надо дождаться появления его клиентской части по таймеру с условием на выход объекта в онлайн, то здесь ждать полсекунды наверное нехорошо. Почти во всех остальных случаях можно не заморачиваться на точность срабатывания в четверть-пол секунды. На самом деле надо задавать прямо противоположный вопрос, вот этот конкретный обработчик апдейта должен ли быть высокоприоритетным? С заданным периодом проверки - тоже неплохая опция. Более того, можно было бы предложить развитие этой идеи скажем в плане рандомизации времени срабатывания. Вот выбросы или что-то ещё в игре можно было бы повесить на такие события. Наверное было бы неплохо как-то объединить разработки. Я давно не трогал свою реализацию, а там очевидно можно что-то изменить и наверное немало.
  5. Поскольку я повёрнут на ООП, то я исходно сделал менеджер сигналов как класс, а не модуль. Таким образом, можно завести несколько объектов такого класса =) Помнится, была идея использовать отдельный экземпляр менеджера событий для системы управления лампочками. Дело в том, что там я активно использовал очерёдность выполнения АКА низкоприоритетные апдейты (их тут называли поллингом, но по-моему не совсем подходит под определение). Смысл использования отдельного менеджера событий был в том, чтобы изолировать очереди из десятков лампочек от остальных очерёдных событий. Но потом отказался от этой мысли и все лампочки навесил на глобальный менеджер. Задержка срабатывания получилась практически незаметная.
  6. @Andrey07071977, Общее правило: чем локальнее данные, тем лучше. Чаще всего это лучше с точки зрения ограничения видимости, дабы уменьшить вероятность конфликтов. В случае Lua ещё и быстрее работает. PS: на самом деле в данном случае нет особой разницы, просто я уже рефлекторно следую этим правилам.
  7. Нет, это я просто не до конца цитирую код. self.sm - это ссылка на глобальный менеджер событий, которую я сохранил ранее для экономии. self.sm = ogse_signals.get_mgr() для того, чтобы каждый раз не писать полный вызов, который выглядел бы так: ogse_signals.get_mgr():call("on_drop", obj)
  8. Особенность Lua в том, что переменная ищется начиная от текущего уровня вложенности и вплоть до глобального, поэтому к глобальным переменным на самом деле обращение идёт медленнее. Но, на мой взгляд, это не самое важное. Важнее то, что глобальная область одна, и сваливать туда все переменные - чревато конфликтами по именам. Действительно лучше ограничивать область видимости, ну и как следствие время жизни, переменных. Народ, как-то реально стало заметно некое напряжение в этой теме. Некоторые комрады, не будем тыкать пальцами, определённо перегибают палку и практически переходят на личности. Это недопустимо. Прошу всех критически отнестись к своему стилю общения и при необходимости пересмотреть.
  9. Вот тогда я тебя не понимаю. А что тогда ты имел в виду и какое именно использование ивентов/событий на твой взгляд самое кошерное? Позволю себе несколько пофилософствовать. Ивенты/события/сигналы/колбеки - это вообще реализация шаблона проектирования "наблюдатель". Суть идеи в том, что есть некий процесс, который идёт как идёт, и есть некое неопределённое заранее количество наблюдателей, других процессов, которые хотят что-то делать по факту происходящего в наблюдаемом процессе. Ключевое понятие здесь - "наблюдение", поскольку наблюдение неинтрузивно. Это означает, что наблюдаемый процесс ничего не делает для того, чтобы вызвать некие действия со стороны наблюдателя. И вообще не знает про наблюдателя, или наблюдателей, или их отсутствие, или что они собираются делать. В самом деле, вот идёт актор по Свалке весь уставший в дождливый день после удачной сделки с Сидором и решает хлопнуть пузырь водки. Ясное дело, актору невдомёк, что за ним наблюдает кто-то, кто ждёт именно этого события. И уж тем более невдомёк, что этот злоумышленник собирается сделать. А вот вызов из колбека на дроп в биндере функции типа такого: 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 Собственно и всё. В этом весь смысл идеи событий. Всё остальное - всякие дополнительные плюшки, оптимизация, и пр. - это уже детали. Я просто очередной раз пытаюсь донести, чем именно вызван такой подход, какая мотивация за ним стоит. Вот эти два аспекта, разделение логики и снижение зависимости между частями системы, - самые главные. А над ними стоит наша сверх цель - снижение сложности системы.
  10. @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-и кги т.д. Пока не могу сходу сформулировать точный критерий, где надо остановится, но мне видится, что где-то надо.
  11. Не знаю, какую "формулу" вы хотите для просчёта пули. Траектория полёта не просчитывается по формуле, а моделируется численно. На каждом временном шаге просчитывается новое положение пули как смещение от предыдущего с учётом текущей скорости, гравитации, сопротивления воздуха. Получается ломаная, которая не описывается простой формулой.
  12. Рассмотри это с другой стороны. Толпа модулей подписалась каждый на своё событие: дроп аптечки, дроп спальника, дроп артефакта и т.д. Дроп спальника (не просто дроп) - это событие, адресованное вполне конкретному обработчику. Остальным оно не интересно. Если же событие "дроп такого-то объекта" вдруг интересно не одному, а нескольким обработчикам, то у них должно быть соглашение о взаимодействии, которое включает неудаление объекта. Тут заходила речь о том, чтобы явно расставлять очерёдность выполнения обработчиков в цепочке срабатываний. Лично моё мнение - это категорически противопоказано делать. Я у себя не делаю допущений о последовательности срабатываний подписанных обработчиков (даже при том, что таковая имеет место быть в силу реализации). Поделюсь сразу идеей оптимизации системы событий, прямо противоречащей явному заданию очередности. Допустим, у нас есть некое событие и цепочка обработчиков. Допустим также, что какие-то из них иногда на себе завершают цепочку срабатываний. Допустим также, что это всё происходит довольно часто. Ну скажем что-то там создаётся/удаляется в инвентаре или мы нажимаем часто какие-то сочетания клавиш и т.д. Что имеем. Есть некий список обработчиков и они при вызове все срабатывают в некой очерёдности. Было бы оптимально, чтобы как можно чаще тот обработчик, что заканчивает на себе цепочку, стоял бы ближе к началу очереди (в идеале самым первым). Как это сделать? Можно было бы ввести у каждого обработчика счётчик, считать количество вызовов в случае завершения цепочки на этом обработчике. Ну и сортировать пузырьком по количеству таких прерываний, т.е. просто переставлять местами соседние, сдвигая наиболее часто прерывающий в начало очереди. Тогда можно сэкономить на времени вызова и соответствующих проверок в остальных обработчиках, тех, что стоят позже по очереди. Ну и понятно, что тогда мы никак заранее не можем делать допущений об очередности срабатывания. По мне так напротив, с сигналами стало проще. Все помним заморочки с зависанием колбеков биндера. Если навешивать свою обработку через сигналы, то там есть отладочная обвязка, которая в случае подвисания колбека это дело отлавливает и сообщает. А вот если через прямые вызовы, то в этом случае ловля этих зависаний становится уже заботой авторов каждого конкретного прямого вызова по отдельности. Ну или того несчастного, кому эта лапша досталась для сопровождения. Конечно делали также отладочную систему и для прямых вызовов. Расставляли маркеры-счётчики между такими вызовами, потом ловили момент, когда он не нулевой и по номеру определяли какой из вызовов подвис. Но с сигналами и этого не надо делать, поскольку это автоматически делается для каждого вызова. Список подписчиков на сигнал тоже получить не проблема, если сильно надо. Но чаще всего не надо, поскольку место зависания локализуется сразу. Мне одному это утверждение представляется из области некромантии?
  13. Хотел бы задать вопрос, а что требуется в итоге? Требуется создать надёжную систему или напротив, сломать систему? По-моему не секрет, что движок сталкера, как система, мягко говоря не отличается надёжностью. На этом фоне стоило бы стремиться к использованию практик, повышающих надёжность, а не выдумывать намеренно ситуации, являющиеся прямо-таки рассадниками феерических багов. Предлагаемая система ивентов/событий позволяет использовать подходы, реально повышающие надёжность системы. Такой подход уже используется (и повышает надёжность) в существующей системе (надеюсь, не одной). Пример я приводил, и мог бы привести ещё несколько. Реальных примеров, не выдуманных. В ответ мне приводят несколько гипотетических ситуаций, когда предлагаемый подход мешал бы реализации. Вся проблема в том, что это выдуманные ситуации и более того - яркие примеры порочных практик. Вести коллекции предметов - плохая идея по многим причинам. Например потому, что вообще желательно не запоминать предметы между скриптовыми вызовами. Удаление движкового объекта не ведёт к автоматическому удалению его скриптовой оболочки. Другой скрипт может удалить предмет из коллекции, и мы получим скриптовую оболочку с мёртвым указателем внутри. Кроме того сама постановка вопроса: "что если в коллекции будут предметы, которые другой колбек удалит?" уже означает заведомые проблемы с управлением разделяемыми ресурсами. Я должен исключить такую ситуацию на уровне дизайна. Мысль о том, что "все должны получить сообщение" тоже неверна. Сообщения могут быть и широковещательные, но вообще-то они как правило адресные и предназначены конкретному адресату. Здесь нет никакой дилеммы. Если сообщение по сути адресное, то оно и должно закончиться на его адресате. Остальным оно не предназначено. Если же сообщение широковещательное, то вряд ли один из адресатов будет заниматься безобразиями с данными, предназначенными всем. Если же будет, к примеру будет удалять объект, предназначенный всем, то это не проблема системы ивентов, а проблема либо дизайна либо исполнения моей системы. Это и надо лечить. Аналогично с лебедями и балетом. Как по мне, так это плохой дизайн изначально. Два совершенно разных скрипта пытаются удалить один и тот-же объект. Это драка за разделяемый ресурс, и надо эту драку и изжить. К примеру, удалять предмет в одном месте и там выдавать инфопорцию, по которой уже и начнётся балет с лебедями. Не вижу проблемы. Вернусь к начальному тезису. Я могу выдумать много примеров, как не надо делать. Вариантов, как делать не надо, гораздо больше, чем правильных. Это чистая комбинаторика. Я могу рассыпать детали от механизма во многих сочетаниях, но работать будет только если они будут собраны определённым образом. Ну так и определитесь уже наконец, чего хотите. Хотите детальки красивыми стопочками выкладывать или всё-таки собрать нечто рабочее?
  14. Это так и есть. В наличии имеется глобальный менеджер событий, через который и происходит подписывание/отписывание. Ну ещё разумеется надо расставить вызовы событий. С какой стати? Если предмет удалён, то потерян смысл доставлять сообщение дальше. Ещё раз хочу подчеркнуть. Огрызок объекта в виде его онлайновой части - это не повод думать, что объект ещё существует. Его уже нет, а значит нечего дальше передавать. Рассмотри это с такой точки зрения. Событие с объектом - это нечто вроде доставки почты. Мы идём по комнатам и спрашиваем "это для тебя?" Как нашли адресата, то отдали ему предмет. На этом всё, дальше доставлять нечего и некому.
  15. Malandrinus

    OGSE: КБ разработчиков

    Насчёт настройки схемы смены аддонов на оружии. Огромная благодарность всем, кто сделал мне рабочие модели! Теперь у меня есть две разные модели, сделанные немного по-разному. Это хорошо, я смогу настроить надёжную схему переключения. Думаю, скоро будет рабочий скрипт.
  16. Ну нет! Сам же говорил насчёт дисциплины, а вот это допущение - нарушение всех возможных норм безопасности. Мы удалили объект - всё, от этого момента его нет. Огрызок от объекта в виде его онлайновой части - это проклятие движка. Никаких дел с ним иметь нельзя, чего мы и добиваемся, предотвращая все последующие вызовы. На самом деле - вот эта ограничительная мера при использовании ивентов - это тоже элемент дисциплины. Я же не могу никак заставить следовать этому требованию всех пользователей системы событий. Если разработчик удалит объект, а return true не поставит, то всем остальным придётся по старинке проверять на наличие серверного объекта. Разница однако в том, что при старом подходе все обязаны были это делать за неимением другого выхода, а при использовании ивентов/событий есть альтернатива: вместо того, чтобы всем проверять, проверить только одному. Опять же - это в итоге экономит время разработки. В точку! Я так использую свою систему для создания отладочных модулей. Такой модуль - это вообще отдельный файл, кидаешь его в папку со скриптами и он подключается сам (без единой строчки кода где-либо ещё). Страшно удобно. Можно сделать какой-то инструмент отладки, мониторинга каких-то объектов и т.п., который потом можно убрать, просто удалив файл. У нас так сделаны читовый телепорт, редактор/визуализатор зон и т.п. Я делал тулзу для настройки прожекторов: наводишь на прожектор, нажимаешь сочетание клавиш, объект запоминается, потом наводишь куда прожектор должен смотреть, нажимаешь другую клавишу - прожектор поворачивается, а в лог идёт точка направления для настройки логики. В репозитарий я этот файл не включаю, поэтому в игре его нет. При этом, нигде он не прописан, так что его невключение ничего не рушит. @abramcumner, хороший вопрос. Может получиться, согласен. Однако колбеки вообще не должны пересекаться "по интересам", т.е. по множествам предметов с которыми они работают. Т.е. если какой-то колбек занимается коллекционированием предметов с каким-то целями, то он должен при этом фильтровать их дабы не затесались "не его". Ну и я бы старался избегать практики ведения коллекций предметов, особенно в инвентаре. =) На самом деле всегда можно найти способ сломать любую, даже самую надёжную, систему. В Сталкере со всеми его заморочками для этого требуются лишь минимальные усилия. Надёжность в таком контексте - вещь сугубо относительная. Я думаю, что использование ивентов/событий позволяет строить в целом более надёжную систему, нежели традиционный подход.
  17. Но здесь то не болт. И не событие дропа, о чём я ниже ещё скажу. В этом конкретном случае отсутствие серверного объекта может означать только косяк в месте вызова, который надо лечить, а не маскировать такими заплатками. Чего мы добились, поставив те проверки? Что нет вылета? Но проблему то не решили. Почему функция вызвалась второй раз? Почему в инвентаре не оказалось нужного объекта? Мы может и заткнём эти косяки здесь, но источник проблем мы так не убираем, и это может сказаться где-то ещё. Вообще странно, что надо доказывать настолько банальную истину: лечить надо болезнь, а не симптомы. А вот это и это
  18. @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 Ведь в релизе уже не должно возникнуть ситуации, когда этот код может сбоить.
  19. Ай-яй, мне два человека ткнули фрагментом, про который я явно сказал, что это утрированный пример, и скорее всего я бы не стал его так уж расписывать. Алгоритмы, знаете ли, бывают и посложнее тривиального перебора. Кроме того, мнемоничные идентификаторы имеют и оборотную сторону. Зачастую, чтобы сделать имя сущности полностью соответствующим его сути, надо сделать его километровым. Неизбежно сокращаешь, поскольку использование километровых идентификаторов само по себе усложняет читаемость. Шутки шутками, а я остаюсь при своём - никогда ещё не видел, чтобы комментарий казался бы мне лишним. Вот и вся суть. Написать комментарий - секундное дело для человека, который этот код написал. Искать документацию - на порядки большее время для многих людей, которые будут читать этот код. Впрочем, как я уже говорил, комменты я пишу для себя. Я из себя супер мозг не строю. Через неделю я с гарантией забуду детали того, что ваяю сейчас. А с комментами вспомню сразу. И не устану, не отвлекусь. Я себе же время сэкономлю, написав эти несколько фраз.
  20. =) именно потому комменты и не пишут. Ну типа программу же написал, зачем дублировать. Я даже как-то что-то такое начал. Замутил универсальный алгоритм перебора с подачей на вход функтора, задающего что именно ищем: онлайновые объекты, серверные объекты, кастомные. Можно было в цепочку выстраивать и последовательно фильтровать, а на выходе - массив результатов поиска. Потом понял, что увеличиваю количество зла, ибо переборы объектов - зло =) Стёр, отформатировал винт. ЗЫ: шучу, где-то валяется, но уже конечно не найти. ЗЗЫ: но переборы - действительно зло
  21. Malandrinus

    X-Ray extensions

    @abramcumner, если так подумать, то самом деле вообще не верится в силу сообщества в данном случае. Равно как не видно особенного смысла в буквальном переносе правок из x-ray extensions. В GSC за модуль xrGame отвечал по большей части один человек (Ясенев). Думаю, что один-два грамотных человека потянули бы и поддержку/некое минимальное развитие этого модуля. Но только под конкретный проект. Всё иное обречено на заведомый провал. Суть косяка примерно такая. Когда рендерится карта теней (Shadow Map = SMAP) конкретного источника света, то там задаётся форма усечённой пирамиды проекции, где угол - задаётся собственно углом источника света, дальняя поверхность (far plane) - дальностью источника света, а ближняя (near plane) - это как раз искомый размер, называемый SMAP_near_plane, он же поле virtual size в серверном классе. Тень отбрасывает всё, что попадает внутрь этой пирамиды, и иногда что-то близкое к центру проекции не влазит, что и даёт отсечение части теней. Чтобы это пофиксить, надо уменьшить этот виртуальный радиус. Ну вот в серверном классе это поле есть, как есть и в acdc, а до источника света оно почему-то не доходит, и там просто ставится число 0.1. Чтобы это пофиксить надо решить две проблемы: (1) в клиентском классе нет поля для хранения этого значения, (2) значение 0.1 вкомпилено в код. Правка делается в нескольких местах: Во-первых, организуется место, где храним данные. Где-то, уже в упор не помню где, увеличивается размер объекта при создании. Кажется это размер объекта источника света, но где это делалось - уже не помню. Далее в файле hanging_lamp_fix.asm проекта xrGame (врезка CHangingLamp__net_Spawn_fix) значение из серверного объекта копируется в организованное место в клиентском. Далее уже в рендере в файле light_fix.asm (врезка CLight_Compute_XFORM_and_VIS__compute_xf_spot_fix) заменяется довольно большой кусок, где вместо вкомпилированного 0.1 используется правильное значение. В сырцах это место находится в файле Light_Render_Direct_ComputeXFS.cpp, функция CLight_Compute_XFORM_and_VIS::compute_xf_spot. Разумеется в сырцах и меньше проблем будет с тем, где хранить данные.
  22. Malandrinus

    X-Ray extensions

    Это как? Бранч - это отдельный проект. Собирается отдельно и приводит к созданию отдельной библиотеки. DLL - это тяжёлая артиллерия. Вот рендер вынести в dll - это можно, сетевой сервер, модуль звука - можно. А вот кусок xrGame так просто вынести - ни малейшего смысла нет. Что есть смысл сделать - это вынести весь оконный интерфейс в скрипты, а в движке оставить только базовые классы и контролы + полно и аккуратно экспортировать их скриптовые интерфейсы. Тогда можно будет лепить окна скриптами как душа пожелает. И это вполне реально, как показывает пример полностью написанного скриптами главного меню и многочисленных и довольно сложных оконных наработок в модах.
  23. Malandrinus

    X-Ray extensions

    Замечу не без гордости, что на самом деле около 420-и. Я своей рукой поубивал почти 300 мусорных скриптов. Насчёт сборки исходников. Ну конечно так и надо, кто бы спорил. Но я выше перечислил причины, почему этого не происходит. Если сообщество сможет эти проблемы преодолеть, да и здорово! Но я думаю, что сможет далеко не сразу, особенно по п.1 и дополнению к п.1.
  24. 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 кода, и потому не комментировали вообще, т.е. совсем никак. Насколько я могу понять, это вызвало у второго поколения разрабов лютую радость. Это когда первый состав свалил, а новые люди стали допиливать рендер и т.п. Мы все помним, чем это закончилось.
  25. Увы, мой собственный опыт говорит об обратном. Самодисциплина нужна вообще всегда, используем мы ООП или нет, но при использовании процедурно-ориентированного подхода в большом проекте мы создаём код, более чувствительный к ошибкам. Дисциплина или нет, а ошибки мы делаем всегда. Среди причин такой бо'льшей чувствительности навскидку можно назвать: 1. Слабое управление областями видимости, засорение глобальной области. Отсюда конфликты имён, трудности с поиском нужных данных. 2. Удалённость кода и данных. Это одна из причин, по которой мне так и не удалось избавиться от таймеров АМК. Вызываются они в одном месте, а их проверка и срабатывание - совершенно в другом. Поэтому тяжело понять по таймеру, где он реально используется. 3. Затруднение повторного использования кода, что приводит к копипасте и дублированию кода. При дальнейшем изменении возникают несогласованности. Здесь речь больше не о написании, а о сопровождении кода. Стоимость сопровождения процедурного кода многократно больше, чем спроектированного в ОО стиле. Однако, вопреки расхожему мнению, сопровождать свой код программист начинает сразу после написания. Хороший код никогда не пишется раз и навсегда. Написав что-то, я же буду переписывать это снова и снова неделю, месяц спустя. Кто-то другой будет этим кодом пользоваться, и трудность понимания замедлит уже его действия. ООП здесь реально помогает. В качестве лирического отступления. Именно для упрощения сопровождения я очень густо комментирую свой код. Почти буквально каждую строку. Я делаю это не для кого-то, а для себя. Здорово упрощает жизнь неделю спустя, когда смотришь на свою функцию, как баран на новые ворота, и думаешь: "Это я что-ли написал? А это вообще что?"
×
×
  • Создать...