Это популярное сообщение. Malandrinus 615 Опубликовано 8 Июля 2009 Это популярное сообщение. Поделиться Опубликовано 8 Июля 2009 (изменено) В данной теме собраны сведения по скриптовой модели сталкера: функции и классы, методы и свойства, взаимосвязь классов и последовательность работы с ними, связь работы классов и файлов конфигураций. К наполнению темы приглашаются все желающие. В наполнении темы непосредственно участвовали и существенно мне помогли: @Monnoroch, @Kolmogor, @Unnamed Black Wolf, @меченый(стрелок), @IQDDD, @Kirag, @Taroz, @dan, @7.9, @Garry_Galler, @AKKK1, @Bak и много других людей. Скрытый текст Скрытый текст класс alife_simulator. Базовые операции с серверными объектами. Пространства имён. Глобальные функции для большого числа задач. "Создание своего класса" и "Наследование от экспортированных классов". Базовые сведения об объектно-ориентированном программировании для сталкера. Необходимо прочитать, для понимания темы про биндер и некоторых других. В одном посте: Общие слова об архитектуре и скриптовой модели сталкера "Класс object_binder" расширение онлайновых объектов, колбеки, сохранение состояния. "Класс net_packet" Регистрация скриптовых классов с помощью object_factory Серверные классы. Часть 1 Иерархия серверных классов, описание не закончено. Серверные классы. Часть 2 Картинка структуры наследования и несколько заключительных слов Клиентские классы Скрытый текст Класс game_object Интерфейс ко всем онлайновым (клиентским объектам) Класс hit для нанесения урона скриптом и другая информация (IQDDD) Некоторая информация по управлению путями патрулирования здесь (Kirag) и здесь (Taroz) Неполная информация по управлению памятью неписей с примером здесь (Bak) Физическая оболочка объектов (Garry_Galler) Пост о выборе (подборе) оружия НПС и стрельбы (*Shoker*) Скрытый текст Управление заданиями Класс CGameTask и другие вспомогательные классы и функции. Управление инфопорциями Функции, колбеки, форматы файлов Список специальных системных инфопорций (Unnamed Black Wolf) Система профилей и алгоритм генерации имён. Форматы файлов, функции Дополнительная информация по параметрам профилей terrain_sect (Kolmogor) Диалоги. Часть 1 Форматы файлов, базовые сведения Диалоги. Часть 2 Скриптовые диалоги Диалоги. Часть 3 Тематическая подборка функций управления диалогами Скрытый текст Функции времени Тематическая подборка функций, связанных с управлением игровым временем. Класс CTime Вспомогательный класс для управления игровым временем Полезная скриптовая функция с использованием CTime (Garry_Galler) Скрытый текст class ini_file (меченый(стрелок)) Класс FS и CSavedGameWrapper Бинарный доступ к файлам, в том числе в игровых архивах, управление сохранёнными играми. Скрипт уровня. Забытая фишка с колбеком на заход на уровень Класс vector Некоторая полезная информация о разных вещах (меченый(стрелок)) "Класс render_device" Направление и положение камеры, характеристики экрана, программная пауза игры и др. Некоторая информация о различиях между ТЧ и ЗП в системе оконных классов и колбеков. (lekzd) Неплохо бы развить эту тему! Некоторая полезная информация о скриптовых функциях из модуля _g.script. (lekzd) Также требует развития! Полезные функции для работы с графом игры (Garry_Galler) В одном посте: Класс profile_timer Отладочные измерения скорости работы фрагментов программы Класс client_spawn_manager Колбек на выход в онлайн без использования биндера. Работа с консолью. Класс CConsole Анимации цвета. Класс color_animator Всякие моргающие элементы в окнах и пр. Управление постэффектами. Скриптовые постэффекты. Класс effector Класс sound_object. Проигрывание звуков в игре в произвольном месте, от произвольного объекта, в голове актора. (Shadows) Пост удалён автором (прим. Kirgudu) Скрытый текст Оконные классы Некоторая общая информация о создании окон Список методов, XML-тегов и событий для оконных классов (ТЧ/ЧН/ЗП) (Norman Eisenherz) Представление материала в моих статьях оптимизировано для онлайнового просмотра. Если кому не хочется лазить по спойлерам, а нужно просмотреть текст какого-либо поста "потоком", то могу рекомендовать просмотр в режиме "текстовая версия". В этом же режиме удобно сохранять содержимое темы на диск. (прим. Malandrinus) Изменено 30 Июля пользователем Kirgudu 5 5 16 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
AndreySol 215 Опубликовано 11 Апреля 2012 Поделиться Опубликовано 11 Апреля 2012 для ТЧ v 1.0005 drop_item(game_object*); mark_item_dropped(game_object*); Проверил их для обычной аптечки в инвентаре, результат для обоих - аптечка выбрасывается из инвентаря актера. В чем тогда разница между этими ф-циями ? В оригинальных скриптах игры есть несколько примеров для drop_item, а вот для mark_item_dropped ни одного. Добавлено через 19 мин.: для ТЧ v 1.0005 CUIStatic: void SetHeading(float angle); float GetHeading(); Для чего эти ф-ции ? В оригинальных скриптах ни одного примера использования. Ссылка на комментарий
Malandrinus 615 Опубликовано 11 Апреля 2012 Автор Поделиться Опубликовано 11 Апреля 2012 AndreySol, drop_item(game_object*); mark_item_dropped(game_object*); Проверил их для обычной аптечки в инвентаре, результат для обоих - аптечка выбрасывается из инвентаря актера. В чем тогда разница между этими ф-циями ? Краем уха слышал где-то, что вторая маркирует предмет, чтобы не удалялся менеджером смерти. Реализовано ли это удаление/игнорирование скриптами (есть функция проверки этого маркера) или это влияет на некое движковое удаление предметов я понятия не имею, как не уверен и в точности этой информации. Мне кажется, что я всё-таки видел в оригинальных скриптах использование второй функции. void SetHeading(float angle); float GetHeading(); Для чего эти ф-ции ? Это вращение текстуры окна. Вот кстати показательный пример невероятной заботы пыс о сообществе. Сами они эту функцию нигде не использовали. Как только у них дошли руки (в ЗП) - тут же и вырезали, несмотря на то, что функция используется в модах. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
AndreySol 215 Опубликовано 11 Апреля 2012 Поделиться Опубликовано 11 Апреля 2012 MalandrinusЭто вращение текстуры окна А можно подробнее ? Или пример использования какой-нить ? Ссылка на комментарий
Malandrinus 615 Опубликовано 11 Апреля 2012 Автор Поделиться Опубликовано 11 Апреля 2012 Здесь использовалось Реальные географические координаты Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 11 Апреля 2012 Поделиться Опубликовано 11 Апреля 2012 (изменено) Информация по 'mark_item_dropped', полученная на практике: В оригинале разработчики практически не используют метод mark_item_dropped и остался только рудимент в death_manager.script проверки marked_dropped. Почему - можно только предполагать ..., возможно из-за возможности возникновения фатальных ошибок при некорректных применениях 'задежки', которую дает метод mark_item_dropped. В отличии от метода drop_item, при использовании которого 'выброшенный' объектом предмет становится безхозным (item:parent() == nil) уже в следующем апдейте (~20ms) и, если был в слоте - то слот освобождается, то при использовании mark_item_dropped предмет некоторое время (следующий период апдейта) остается 'принадлежать' выбросившему его объекту, а если был в слоте - то даже как бы лежать в слоте, занимая слот (проверка слота показывает его наличие!). При этом метод marked_dropped возвращает (показывает) это состояние временной принадлежности выброшенного предмета, т.е. как бы метки "предмет выброшен, но принадлежит хозяину". Иными словами: drop_item - безусловное выбрасывание предмета из инвентаря НПС. Предмет становится безхозным уже в следующем апдейте НПС. mark_item_dropped - выбрасывание предмета из инвентаря НПС с сохранением признака 'владения' и наличия предмета у НПС в текущем и последующем апдейте НПС. marked_dropped - проверка метки "НПС выбросил данный предмет" в течении текущего и следующего апдейта НПС. А может быть правильнее трактовать метод mark_item_dropped как: пометить и выбросить с задержкой, т.е. в следующем апдейте, а не текущем ... Наличие метки может помочь в синхронизации между различными схемами/функциями, которые манипулируют предметами. Однако, при некорректном применении этих методов, при различных манипуляциях с предметом (например, удалении предмета из игры) - возможны вылеты, т.к. объект (клиентский!) как бы присутствует у его хозяина и проверки на его наличие возвращают 'истину' ... -------------------------------------------------------- Добавлено через 11 мин.: По ранее упоминавшейся ошибке в класса game.CTime: Наличие бага, при котором сбрасываются в 0 старшие разряды счетчика - подтвердилось. При чем, необязательно долго играть. В моем случае достаточно было при старте новой игры телепортировать актора на Армейсие склады и через ~ пару минут последовал вылет по этой ошибке. При запуске автосэйва перехода на Склады - последовал аналогичный вылет. Системы пока не выяснил, общее пока - патч 1.0006 и использование во всех случаях 'zone_informer' (рассматривал строение перехода на Бар). Т.о. коды игры все же следует пересмотреть и возможно исправить. Пока поставил ловушки и на другие места (не только на апдейт), возможно статистика поможет понять причину. Изменено 12 Апреля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 12 Апреля 2012 Автор Поделиться Опубликовано 12 Апреля 2012 (изменено) Artos, В моем случае достаточно было при старте новой игры телепортировать актора на Армейсие склады и через ~ пару минут последовал вылет по этой ошибке. Эффект устойчивый? в качестве оффтопа. Когда я перехожу на Склады читовым телепортом (т.е. не по сюжету), то почти всегда вылетаю как раз через пару минут. Может конечно и случайное совпадение. Однако ж, теперь выходит надо переделывать весь код, завязаный на Ctime Изменено 12 Апреля 2012 пользователем malandrinus Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 12 Апреля 2012 Поделиться Опубликовано 12 Апреля 2012 (изменено) Эффект не устойчивый, но из ~10 запусков (пока не было достаточного времени) проявился 3 раза. Время до ошибки варьировалось от 2...10 минут. У меня в моде достаточно много изменений и завязки на сюжет не приводят к фатальным ошибкам, хотя конечно логика объектов хромает. Как писал выше, закономерности пока не понял. Все вылеты были если не убивал никого из НПС (только монстров) и после использования zone_informer'а около перехода на Бар (хотя тоже может быть совпадение). По крайней мере точно то, что именно объект счетчика изменяется, т.к. у себя прерываю игру только после повторного апдейта и "испорченный" (он не забывается) оба раза показывает нулевые старшие байты. Править же похоже немало придется, хотя ... в основном подобные сбои приводят к нефатальным логическим ошибкам и могут быть малозаметны. Типа: респавнер раньше времени сработал или артефакт раньше времени заспанится аномалией, иль работа гулага закончится/освободится не вовремя ... Вот в таких местах сейчас понаставил "ловушек" и попробую оценить частоту/критичность и может быть причину или иную особенность/локальность. Изменено 12 Апреля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 12 Апреля 2012 Автор Поделиться Опубликовано 12 Апреля 2012 Тестовый код, который я приводил выше, взят из реального компонента, где использовался для обновления состояния актора на основе артов на поясе. Там обновлялся параметр, который при значении минус два миллиона не сразу приводил к смерти, но если бы это было здоровье (что я также планирую включить в этот алгоритм), то это была бы просто моментальная смерть актора. Как-бы критично выходит. А может приводить и к не очень очевидным, но достаточно неприятным вещам типа времени до следующего события в пару миллионов лет (и это может запомниться в сейве) и т.п. Неприятных вариантов масса. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 12 Апреля 2012 Поделиться Опубликовано 12 Апреля 2012 (изменено) malandrinus, Верояно дальнейший обмен информацией более уместно вести где-то в топике по ковырялкам или по скриптам ... Замечу, что не очень ясно зачем в твоем случае использовать такой вариант определения интервала между апдейтами, когда метод(ы) update уже имеют входным аргументом delta, т.е. как раз разницу времени с момента предыдущего апдейта. Хотя эта разница в единицах глобального времени, но ведь достаточно просто перевести в игровое применив поправку на level.get_time_factor(). Ну да это уже вариации и субъективности ... Еще не приступил к сбору статистики по появлению ошибки, т.к. и ловушки асставляю и коды заодно анализирую ... Словил пока еще один раз (из 5 запусков) при тех же условиях. Думаю сегодня-завта приступлю к отлову, пока же доп.ловушки безмолствуют. :-) Под правкой же кодов в основном подразумевал исходные оригинальные коды, а не "из мода", т.к. это само-собою разумеющееся. Вообще анализ оригинальных кодов (касательно CTime) поражает наличием мусота и неоптимальностей. То ли разрабы подозревали о каких-то сбоях, то ли данный сбой не единичен и имеются иные типы сбоев ...? Пока сильно критичных моментов (как тобою описано) не заметил в оригинальных алгоитмах. В основном возможны сбросы интервалов, что приводит к сбою логики (возврат к началным значениям). Вот неполный список (что успел проанализировать) где вообще применяется использование объектов CTime: - db.storage[idActor].disable_input_time --/ _g.script #x#- не критично - db.storage[idObj].activation_game_time --/ xr_logic.script - actor_detector.init_time --/ xr_detector.script #x#- не критично или отключено - tTask.last_task_time --/ task_manager.script - se_respawn.respawn_time --/ se_respawn.script - se_zone.last_spawn_time --/ se_zone.script #x#- не критично - gulag.stateBegin --/ xr_gulag.script - se_smart_terrain.check_time --/ smart_terrain.script #x#- не критично - se_smart_terrain.duration_end --/ smart_terrain.script #?#- не используется - se_smart_terrain.idle_end --/ smart_terrain.script #?#- не используется - tInfoNPC.stay_end --/ smart_terrain.script #?#- не используется Похоже правка кодов только на пользу пойдет, разгрузив от мусора и обезопасив логику. ;-) Изменено 12 Апреля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 12 Апреля 2012 Автор Поделиться Опубликовано 12 Апреля 2012 Artos, Верояно дальнейший обмен информацией более уместно вести где-то в топике по ковырялкам или по скриптам ... Пока это обсуждение идёт в контексте использования и проблем использования движковых функций. Если руки дойдут, почищу позже тему и оставлю тезисно обсуждение под спойлером. не очень ясно зачем в твоем случае использовать такой вариант определения интервала между апдейтами, когда метод(ы) update уже имеют входным аргументом delta, т.е. как раз разницу времени с момента предыдущего апдейта. Хотя эта разница в единицах глобального времени, но ведь достаточно просто перевести в игровое применив поправку на level.get_time_factor(). 1. аргумент delta неточен в том смысле, что при превышении одной секунды всегда возвращает одну секунду и уже будет фактически врать. Для апдейта актора это может и не критично, а если эта проверка стоит где-то в другом клиентском объекте, то уже будет потенциальный косяк. 2. Я могу захотеть делать некие вычисления не каждый апдейт, а с большим периодом. Это в частности актуально, когда я вешаю проверку на событие с низким приоритетом. В этом случае период вообще заранее не известен. Заниматься в этом случае суммированием аргументов выглядит уже не проще, чем использование CTime. 3. Это будет крайне плохо работать в случае прокрутки времени во время сна, когда фактор ускорения времени выставляется на значения десятки тысяч, а апдейты идут со всё той же скоростью. Погрешность вычислений может быть колоссальная, в то время как используя CTime об этом можно вообще не думать. Может что-то ещё забыл упомянуть, но выглядит так, что мотивация использовать CTime есть. Конечно, именно для вычисления времени между обновлениями можно было бы использовать 32-х разрядный счётчик игрового времени game.time(), поскольку здесь нам вроде как и не нужно абсолютное время. Но он зараза переполняется каждый игровой месяц. Вот из-за этого единственного проблемного момента, который случается так редко, но таки случается, нам и приходится использовать вычисления на основе CTime. Это не считая тех случаев, когда нам собственно надо сохранить некий абсолютный момент времени (для генерации события, выброса скажем или чего угодно другого). В этом последнем случае альтернативы CTime просто нет. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 12 Апреля 2012 Поделиться Опубликовано 12 Апреля 2012 Ни в коей мере не оспариваю отсутствие или даже незначительность использования класса CTime. Как уже писал ранее, именно с ним стала возможна игра и после месяца нахождения в ней. И именно то, что за все время применения этого варианта ни разу не отмечал у себя или других игроков в мод сбоек хоть как-то ставящих под подозрение объект этого класса. Ведь подобный сбой времени не мог бы остаться незамеченным ... Согласен и с аргументами, что замена класса на аналоги далеко не везде возможна, и высказал сомненеия в целесообразности именно применительно к описанному тобою случаю (зависимось акторских параметров от некоего "арта" на поясе). Ну да не суть ... Но, то что имеем в игре (в оригинальных кодах с использованием CTime) все же изумляет. Берем тот же smart_terrain.script, в котором помимо трех невосстребованных случаев использования CTime в дальнейших алгоритмах (duration_end,idle_end,stay_end), а это по объекту для каждого смарта/гулага/НПС, даже check_time, который используется для по-минутной (примено) проверке - сделан на CTime!(?) Нафига?!? Учитывая, что поминутно этот счечик обновляется текущим временем - самое разумное сделать его или на game.time() или даже не глобальном. Ведь всего-то проверка освобождения неписей от гулага на этом завязана, ничего страшного, если такая проверка будет давать "раз в месяц" сбой периода в минуту. А задание времени отключения худа у акторра на секунды (в игре не более пол-минуты игрового времени)? Тут подстраховка на совпадение с "месячной" сменой выглядит более чем натянуто на фоне возможного бага сброса в нули, но зато и в сэйв пишем CTime ... ;-) ИМХО, стОит хоя бы ради устранения мусора и чрезмерностей заменить использование объектов CTime не на их числовые эквивалентя, а на более оптимальные варианты. Ну а там, где не обойтись - вполне можно и подстраховаться, используя не один объект, а, например, и его эталонный дублер ... иль иное решение. Сбоев CTime в ловушках в разных скриптах пока не зарегистрировно, хотя контролируются десятки (за сотню) объектов. "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 12 Апреля 2012 Автор Поделиться Опубликовано 12 Апреля 2012 Artos, самое разумное сделать его или на game.time() или даже не глобальном. Ведь всего-то проверка освобождения неписей от гулага на этом завязана, ничего страшного, если такая проверка будет давать "раз в месяц" сбой периода в минуту. Вот не соглашусь. Рассмотрим типичный сценарий проверки наступления некоего события через N единиц времени после последнего срабатывания. Код может выглядеть как-то так: local last_event_time = 0 function update() if game.time() > last_event_time + events_interval then last_event_time = game.time() -- здесь делаем работу, связанную с событием end end Теперь смотрим, что происходит после переполнения счётчика. Текущее значение времени становится почти нулевым, а мы хотим дождаться момента, когда оно станет больше последнего значения + <время между событиями>. Но последнее значение осталось очень большим, близким к переполнению. Выходит, что мы попросту не дождёмся следующего события никогда. В приведённом выше примере ситуация выправится только после перезагрузки игры, когда значение предыдущего времени вновь обнулится. Т.е. несрабатывание продлится как минимум до перехода на другой уровень, перестанут спавниться монстры, не сработает какой-то квест и т.п. А если мы рассмотрим этот же алгоритм, но уже в варианте с сохранением счётчика в сохранёнке, то всё становится совсем грустно. local last_event_time function load(packet) last_event_time = packet:r_u32() end function update() if game.time() > last_event_time + events_interval then last_event_time = game.time() -- здесь делаем работу, связанную с событием end end function save(packet) packet:w_u32(last_event_time) end Код приведён с некоторой степенью условности, но проблему иллюстрирует: предыдущее значение сохранится и зафиксируется. Ни загрузка сейва заново, ни переход на другой уровень уже не исправят ситуацию. стОит хоя бы ради устранения мусора и чрезмерностей заменить использование объектов CTime не на их числовые эквивалентя, а на более оптимальные варианты. Вообще-то, за исключением вот этой проблемы с багом, я не вижу никаких иных проблем в использовании этого класса. Он достаточно легковесный, внутри всего 8 байт, и вычисления на его основе весьма быстрые, поскольку производятся в своей основе на целочисленной арифметике с 64-х разрядным счётчиком. Этот класс позволяет в большинстве случаев обходиться без разложения его на компоненты и использовать его самого для сравнений больше/меньше и приращений значений. Очень удобная штука и вдобавок даёт сервис получения дней, месяцев и т.д. Сбоев CTime в ловушках в разных скриптах пока не зарегистрировно, хотя контролируются десятки (за сотню) объектов. Здесь, увы, уже полагаться на случай нельзя. Тем более, что система появления бага непонятна, его можно ожидать когда угодно и где угодно. Лично я буду теперь ставить заплатки везде, где найду. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 12 Апреля 2012 Поделиться Опубликовано 12 Апреля 2012 (изменено) Стоп. первый процитированный кусок отнисотся к check_time, который является обновляемым счетчиком периода апдейтов. На нем висит ТОЛЬКО периоическая проверка "не освободился ли кто из НПС" в гулаге и ничего более. По сути весь наворот исходных кодов можно выразить этим: local oCTime_0 = game.CTime() --/ эталонное 'нулевое' игровое время (константа) local oCheckPeriod = game.CTime() --/ 'нулевое' игровое время local oCheckPeriod:setHMSms(0,0,50, 0) --/ продолжительность паузы (50 game-seconds) (константа) function se_smart_terrain:update() ... if ( self.check_time or oCTime_0 ) < game.get_game_time() then self.check_time = game.get_game_time() + oCheckPeriod --/ установка времени следующей поверки (+50 game-sec) --/ проверка: не собрался ли кто-то к этому времени уже уходить ... end ... end его почти полный эквивалент на базе game.time() выглядит так: local iCheckPeriod = 50*1000 --/ 50sec == 50000 ms function se_smart_terrain:update() ... local iDiffSec = ( self.check_time or 0 ) - game.time() if iDiffSec < 0 or iDiffSec > iCheckPeriod then self.check_time = game.time() + iCheckPeriod --/ установка времени следующей поверки (+50 game-sec) --/ проверка: не собрался ли кто-то к этому времени уже уходить ... end ... end т.е. при старте тут же происходит проверка (как и в оригинале) и далее задается время следующей поверки относительно текущего времени. В ситуации когда разрядность счетчика (game.time) переполняется имеем срабатывание по условию превышения задаваемого интервала времени (iCheckPeriod) и переустановку на новую разрядность счетчика. В итоге - только раз в месяц несколько раньше периода проверок будет произведена такая проверка. Итого, код ИМХО эквивалентен по результату и даже более оптимален, т.к. исключены операции сравнения двух объектов счетчиков (u64), а работаем только со считыванием одного (u32) и операциями с числами. Заодно и чуть ресурсы экономим на объекте CTime, который не требуется задавать каждому смарту и хранить его, т.е. храним только числа (u32). Если же в аналогичной ситуации потребуется сохранять - то нет никаких проблем сохранить u32 и восстановить. Обращаю внмание, что единого алги=оритма конечно же не может быть, но немало мест, где CTime именно чрезмерен из-за незначительности последствий из-за применения его аналогов, хотя бы и приводящих к переполнению раз в игровой месяц. Как ты заметил, восстебованность CTime именно при необходимости ставит 'базовые' точки времени, которые могут "перешагивать" месячные рубежи времени. В остальном, навскидку почти везде можно заменить эквивалентами и при необходимоси при "переходах" или игнорировать один период апдейта или делать поправку на "месячность". Лично я буду теперь ставить заплатки везде, где найду.Аналогично, но ... скорее из-за того, что пишу коды по сути мультиплатформенно и если на 1.0006 встречается - то и для остальных патчей и даже CS|CoP не помешают (не делать же разовую работу). ;-) Однако, основное где встречается - установленная база времени (из alife.ltx) от которой скрипты берут отсчет, врядли возможно заплаткой закрыть. Тут что-то посерьезнее потребуется, если обнаружится сбой. Добавлено через 35 мин.: P.S. Вообще-то, за исключением вот этой проблемы с багом, я не вижу никаких иных проблем в использовании этого класса.Готов так же присоединиться к сказаному, но ... Гложет вопрос без ответа: "Почему везде вместо задания времени через часы,минуты,секунды - разработчики используют только установку через милисекунды?" Что это, тяга к большому количеству нулей или им известен некий баг при установках иным способом? Ну и ... если доверие к инструменту не 100% и требует перепроверок - удобства от применения такого инструмента уже далеко не ... Тут как раз уместно изречение: "Ложка дегтя бочку меда портит." Изменено 12 Апреля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 12 Апреля 2012 Автор Поделиться Опубликовано 12 Апреля 2012 Мне, признаться, вообще непонятно зачем в примере выше используется игровое время. Если я верно понимаю смысл этого фрагмента, то речь не идёт о "событии", а просто о тупой периодической проверке, смысл которой просто делать одно и тоже каждые 50 секунд. С какой стати это должны быть именно игровые секунды - непонятно. Это даже в каком-то смысле неправильно, поскольку выходит, что у тех, кто поставил таймфактор побольше, попросту вырастает нагрузка на процессор без всякой видимой разницы в геймплее. На мой взгляд, здесь надо не заморачиваясь поставить проверку по реальному времени, выбрав интервал этой проверки примерно в 5-10 реальных секунд. Что же касается общей идеи, то да, выглядит так, что при таком построении ошибка проявляется в преждевременном срабатывании при переходе через месяц. На мой взгляд, это всё равно ошибка и я постарался бы от такого кода избавиться. Просто принципа ради. Гложет вопрос без ответа: "Почему везде вместо задания времени через часы,минуты,секунды - разработчики используют только установку через милисекунды?" Что это, тяга к большому количеству нулей или им известен некий баг при установках иным способом? По-моему, всё логично. Если ты хочешь выставить объект CTime в некое значение времени, которое позже будешь добавлять к другому объекту как смещение, то как иначе это сделать? Какой смысл раскладывать твою временную добавку на дни,часы,минуты и т.д., если можно просто взять миллисекунды и задать их единственным параметром. Так же проще! Или ты о чём-то другом? Ну и ... если доверие к инструменту не 100% и требует перепроверок - удобства от применения такого инструмента уже далеко не ... Тут как раз уместно изречение: "Ложка дегтя бочку меда портит." При использовании game.time() выходит тоже надо делать проверку. Проверка там, проверка здесь - баш на баш выходит. Я при этом предпочту вариант, который смотрится более общим, т.е. использование 64-х разрядного game.CTime и исправлю его недостатки, нежели исходно кривой вариант с 32-х разрядным game.time() и буду исправлять его врождённую кривизну. Короче, моя персональная стратегия такая: 1. Не пользоваться game.time() вообще ни при каких условиях. 2. Там где можно, заменять его на счётчик реального времени 3. Там где нельзя, использовать CTime с соответствующими предосторожностями насчёт его хранения. Шаблон для этого я уже наработал и особенных затрат это не составит. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 13 Апреля 2012 Поделиться Опубликовано 13 Апреля 2012 (изменено) Ну так я изначально и написал: "... самое разумное сделать его или на game.time() или даже неа глобальном.". Просто поверхностно анализируя, не вдаваясь в глубинные смыслы, которые могут иметь ответвления ... malandrinus: Или ты о чём-то другом? Да, несколько об ином. Смотрим в коды и видим: idle_time:setHMSms( 0, 0, 0, math.random(self.idle_spawn_min, self.idle_spawn_max)*1000) Спрашивается, а почему не так(?): idle_time:setHMSms( 0, 0, math.random(self.idle_spawn_min, self.idle_spawn_max), 0) , учитывая что везде (почти) целочисленные числа и в конфигах заданы именно секундами. И так почти везде. Ведь логичнее - заданы часы => ставим именно часами, заданы секунды - => ну так раз доступен вариант установки именно секундами, то зачем манипулировать нулями? И не ставлю знака равентства или какого-то соответствия между CTime и game.time() ... Моя стратегия была и будет: "Каждому инструменту свое место использования", т.е. работать по контексту в зависимости от задачи. И пока основное - избегать абсолютного времени (от базы CTime) там, где можно задавать и важен только интервал. А уж чем его обрабатывать - зависит от алгоритма и вполне может быть и глобальное или 32-х разрядное игровое время. Важно отсутствие критичных последствий с точки зрения именно игры. В конце концов игра не точный часовой алгоритм и порой именно рандомные коллизии/случайности добавляют в нее реалистичности. В жизни тоже мало что идет по расписанным безошибочным алгоритмам. Добавлено через 57 мин.: P.S. Вот припомнился вариант для подводного камня при вроде как простой периодичности обновления: Если работа в гулаге должна иметь синхронизацию с неким событием из логики "уходящего" НПС, то завязка именно на гейм-секунды может быть важна. Например, многое в логике задается именно игровым временем (секундами) и тот же звук (кстати game.time()) отыгрывается ... Так что при переходе на реальное время именно при смене игроком или скриптом тайм-фактора - поизойдет рассинхронизация прерывания работы иль еще чего. Изменено 13 Апреля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 13 Апреля 2012 Автор Поделиться Опубликовано 13 Апреля 2012 Смотрим в коды и видим:idle_time:setHMSms( 0, 0, 0, math.random(self.idle_spawn_min, self.idle_spawn_max)*1000) Спрашивается, а почему не так(?): idle_time:setHMSms( 0, 0, math.random(self.idle_spawn_min, self.idle_spawn_max), 0) , учитывая что везде (почти) целочисленные числа и в конфигах заданы именно секундами. Потому, что во втором варианте теряются миллисекунды. Аргумент функции целочисленный и дробная часть, которая содержит миллисекунды, будет отброшена. Кстати, я делаю несколько иначе: local s,ms = math.modf(time_sec) local t = game.CTime() t:setHMSms(0,0,s,ms*1000) Причина, почему не просто всё в миллисекунды, потому что теоретически возможно задать добавку времени больше того самого месяца и тогда количество миллисекунд не влезет в 32 бита. Понятное дело, что разработчики об этом не думали, всякие фриплеи им и в страшном сне не снились, а оригинальный сюжет неторопливо проходится за гораздо меньшее время при максимальном таймфакторе. Конечно, ситуация, когда я добавляю интервал времени больше месяца, маловероятна, но моему параноидальному сознанию так спокойнее =) Я даже сделал функцию на этот счёт: function seconds2ctime(time_sec) local s,ms = math.modf(time_sec) local t = game.CTime() t:setHMSms(0,0,s,ms*1000) return t end и теперь везде её использую. И не ставлю знака равентства или какого-то соответствия между CTime и game.time() ... Моя стратегия была и будет: "Каждому инструменту свое место использования" Ну вот к примеру, ещё в предрелизном билде 2947 CTime был 32-х разрядным. На тот момент между ними очевидно можно было поставить знак равенства. В какой-то момент времени ему таки повысили разрядность, а вот функция game.time() так и осталась 32-х разрядной и просто возвращает младшие 4 байта. Так что на мой взгляд game.time() - это просто рудимент, невыпиленный только по причине лени/спешки разработчиков. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 13 Апреля 2012 Поделиться Опубликовано 13 Апреля 2012 (изменено) Ну как может быть отброшено то, чего нет??? Какая дробная часть? Ведь речь о целочисленных значениях и никаких операций деления нет! Жаль пожадничал и дал только одну строку из исходного кода se_respawn.script ... Берем простенький пример: self.idle_spawn_min = 15*60 --/ 15 минут, заданные в конфиге как 900 self.idle_spawn_max = 30*60 --/ 30 минут, заданные в конфиге как 1800 math.random(900, 1800) - даст нам только целочисленное число, например 1234 Ну и что потеряется при(?): idle_time:setHMSms( 0,0,1234, 0) В большинсте исходных кодов устанавливаемый счетчик (в данном случае idle_time) вначале берется нулевым ( = game.CTime() ). Какие тут милисекунды? Ведь разрабы манипулируют именно секундами и только при установке счетчика переводят в мили- ... добавляя нули и меняя разряд для изменения счетчика. И, даже если были бы в idle_time какие-то милисекунды - они затираются, т.к. идет именно установка заданных значений, а не их добавление. Пример дан из se_respawn.script, где дробными частями или милисекундами не пахнет и в основном часами-сутками манипуляция идет. И все это все одно задается именно милисекундами. И что интересно, наличие метода setHMS нигде не востребовано, что так же непонятно. Причем в твоем варианте опять же используется разряд секунд для установки времени, а я про то, что разработчики НЕ используют никакие разрыды, кроме как последнего разряда милисекунд! Так же, ты заведомо предполагаешь возможность задания в качестве аргумента нецелочисленных значений (что конечно может быть востребовано), но в кодах о которых веду речь (исходных в игре) такое не востребовано. Причина, почему не просто всё в миллисекунды, потому что теоретически возможно задать добавку времени больше того самого месяца и тогда количество миллисекунд не влезет в 32 бита. Понятное дело, что разработчики об этом не думали ...А вот это как раз и мой вопрос: Почему же имея потенциальную возможность переполнения в счетчике младших разрядов разработчики все свои установки в исходных кодах производят ТОЛЬКО через именно милисекунды, когда тот же результат можно получать без вероятного переполнения через секунды (иль минуты/часы)? И, кстати "месяц" тут несколько меньше: всего 49/2 = 24 дня 11 часов 59 минут 59 секунд. При попытке установить через милисекунды бОльшего значения поисходит переполнение ... чего нет если устанавливать секундами. (это если кто-то из читающих наш оффтопик не очень понял о чем речь) Изменено 13 Апреля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 13 Апреля 2012 Автор Поделиться Опубликовано 13 Апреля 2012 Ведь речь о целочисленных значениях и никаких операций деления нет! ок. Я не разглядел вид math.random и не понял сразу, что он в данной форме даёт только целые числа. Ну да, в этом случае можно было бы не напрягаться с миллисекундами. Там внутри движка стоит простой алгоритм на этот счёт. В целом выглядит примерно так: long long int time = <вычисляем часть от годов, месяцев, дней> time = time*24 + hours; time = time*60 + min; time = time*60 + sec; time = time*1000 + ms; Т.е. ничего неожиданного, кто угодно бы так вычислял. Как видно, нет разницы, задаёшь ли ты через секунды или миллисекунды. Внутри всё равно секунды будут умножены на 1000. А вот это как раз и мой вопрос: Почему же имея потенциальную возможность переполнения в счетчике младших разрядов разработчики все свои установки в исходных кодах производят ТОЛЬКО через именно милисекунды, когда тот же результат можно получать без вероятного переполнения через секунды (иль минуты/часы)? Да не думали они об этом. Я уже сколько раз говорил, не боги горшки обжигают. Как можно ожидать идеального кода от создателей игры, которая стала мало-мальски стабильной только к шестому патчу? Кроме того, неприятная правда состоит в том, что в промышленном геймдеве в скриптёры берут далеко не элиту разработчиков. Я неоднократно сталкивался со свидетельствами того, что скриптёры оригинала (по крайней мере некоторые) очевидно не знали в полной мере как возможности Lua так и движка. Это также могло быть вызвано известным бардаком в команде на поздней стадии разработки. Не надо забывать также и того, что в реальном мире есть план, сроки, давление начальства и прочие радости в этом роде. Мы здесь имеем роскошь исследовать всякие тонкости и рассуждать до бесконечности о деталях, но программисту в реальном мире часто просто не этого. Сделал что-то, что работает как-то - всё, забыл и занялся другими делами. Вот эта потенциальная ситуация с переполнением миллисекунд в производстве игры с заданным сюжетом возникнуть просто не могла, так что вполне логично, что об этом и не думали. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
AndreySol 215 Опубликовано 16 Апреля 2012 Поделиться Опубликовано 16 Апреля 2012 ТЧ v 1.0005 Такие вопросы накопились, касаемо справочных данных UI-классов: 1). По классу CUIWindow и CUIStatic - ф-ции для получения координат окна похоже отсутствуют ? Получить высоту и ширину окна можно, а координаты - нихт ? 2). CUIStatic и многострочное отображение текста - в самом классе ф-ции, устанавливающей такой режим, похоже нет. А вот через описание в xml-файле с указанием complex_mode="1", и последующей инициализацией окна типа: local xml = CScriptXmlInit() xml:ParseFile("файл_описания.xml") xml:InitStatic("имя_статика", self) можно получить многострочное отображение текста. Вроде-бы и ладно - используй xml-описание и все в ажуре, но к примеру такая ситуевина - мне нужен класс, наследованный от CUIStatic, соответственно в нем многострочное отображение недоступно. А использовать этот унаследованный класс через xml-описание - я способа не знаю. Если есть - подскажите, пожалуйста ! Ссылка на комментарий
Malandrinus 615 Опубликовано 16 Апреля 2012 Автор Поделиться Опубликовано 16 Апреля 2012 AndreySol, Посмотри пост в этой теме про оконные классы. Там и чуть дальше есть ответы на оба и может на другие подобные вопросы. Получить высоту и ширину окна можно, а координаты - нихт ? GetWndPos но это только в ЗП. А использовать этот унаследованный класс через xml-описание - я способа не знаю. Если есть - подскажите, пожалуйста ! Упомянутый выше xml:InitStatic("имя_статика", self) собственно и возвращает объект статика. Потом делай с ним что хочешь скриптами. Добавлено через 2 мин.: Насчёт второго вопроса я неправ, CScriptXmlInit я выходит так и не расписал, руки не дошли. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти