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

Malandrinus

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

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

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

  • Дней в топе

    13
  • AMKoin

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

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

  1. Gun12, Сборщик мусора удаляет не ссылки, а значения. Между тем в таблице собственно хранятся именно ссылки. Будет ли удалена из таблицы ссылка зависит от ряда обстоятельств. Вот пример различного поведения: t = {1, nil, 2, 3} -- завёл массив с "дыркой посередине" print(#t) -- его длина 4 t[3] = nil -- занулил элемент со значением 2 print(#t) -- длина по прежнему 4 t = {} -- исходно пустая таблица t[3] = 1 -- заполняем с конца, здесь оператор длины даёт 0 t[2] = 2 -- здесь тоже 0 t[1] = 3 print(#t) -- имеем 3 -- типа получили массив? Как бы не так! t[2] = nil -- зануляем элемент посередине print(#t) -- получаем 1 Я хочу обратить внимание, что эта разница поведения не имеет ни малейшего отношения к сборщику мусора. Сборщик ссылки не трогает, а собирает только значения. Ссылки же существуют либо в стеке функции и удаляются по завершении работы функции или блока или они существуют в таблице (и ещё раз, в таблице физически есть только ссылки). И вот здесь и имеем неприятность, поскольку, как видим, в этом случае поведение зависит от истории создания. В одном случае (массив с самого начала) ссылка не удаляется, в другом (исходно таблица общего вида) - ссылка удаляется, как и положено удалять элементы ассоциативного массива. Это бы всё не стоило обсуждения (работает так и работает, надо только знать), но в документации Lua декларируется, что таблицы - это всегда ассоциативные массивы, а разница массив/не массив - это только детали внутренней оптимизации, которые как-бы не должны сказываться на поведении. К примеру, то, что там внутри используется для реализации ассоциативного массива, в принципе не должно быть важно. Хеш, ну и хорошо. Могло быть и что-то другое, бинарные деревья к примеру. А в реальности выходит, что детали реализации и подробности внутренней оптимизации очень даже важны и заслуживают обсуждения. Возьмём iраirs. В соответствии с мануалом перебор с её помощью и оператор взятия длины работают по одному алгоритму. Если почитать, то в документации на язык таки упоминается, что оператор длины должен измерять длину не только до первой дырки в целочисленных ключах, но и до первого значения nil. Так что выходит, что пример выше показывает фактически ошибочное или нестандартное поведение. Но опять же если думать над этим дальше, то такое нестандартное поведение - единственная возможность перебирать по порядку аргументы функции, используя конструкцию args = {...}. Если всё будет работать в точном соответствии со спецификацией языка, то дальше первого пустого аргумента мы не уйдём. Добавлено через 12 мин.: Artos, Рискну заметить, что это не самая лучшая идея сама по себе, а в соединении с теми, кто "порой не понимает разницы в типах таблиц" идея приобретает совершенно разрушительную мощь. Если функция заранее знает, какого типа аргумент будет на входе, то можно будет сделать проверки и остановить работу с вразумительным сообщением. Если на входе возможные варианты, то функция уже никак не сможет сказать, а что на самом деле хотел сделать программист. Здесь ошибка в данных, или это неправильно использованный вызов или что-то ещё? Вариантов становится слишком много, проверить их все уже невозможно. Остаётся только довериться программисту, но ведь программист подразумевается такой, что "порой не понимает разницы в типах таблиц". Такому только доверься... Это готовый бардак в коде, испорченные сейвы, практическая невозможность отлаживать и вообще сопровождать код. IMHO, не стоит этого делать.
  2. Gun12, Увы, здесь не учитывается тот случай, когда в таблице произвольного вида имеются несколько ключей, отвечающих правилам "массивов". Т.е. если есть хотя бы ключ [1], то так таблица будет распознана как массив, хотя там может быть что угодно ещё. Я думаю, что здесь без полного перебора не обойтись. Что же касается исходно заявленной задачи сохранения таблиц/массивов, то очевидным решением будет делать отдельную функцию сохранения на разные варианты и не пытаться определять "тип" автоматически. В конечном счёте, конкретная таблица как правило в силу дизайна либо является массивом, либо имеет произвольный вид. Ключевой момент "в силу дизайна", т.е. программист заранее знает природу этой таблицы. Это означает, что в подавляющем большинстве случаев задача определять тип не стоит вовсе, поскольку тип и так известен заранее. Если же вдруг такая задача возникла, то лучше потратить силы не на ёё решение, а на редизайн кода с тем, чтобы привести его в состояние, когда вы таки знаете определённо, что из себя представляет эта таблица.
  3. Недавно нарвался на интересную и неприятную особенность метода перебора с ipairs. Я хотел парсить список аргументов переменной длины и написал что-то вроде такого function fun(...) for _,arg in ipairs({...}) do -- ... end end Так вот выяснилось, что перебор останавливается на первом же аргументе со значением nil даже если за ним имеются ещё аргументы. Это, вообще говоря, идёт вразрез с официальной документацией. Вот цитата (на русском, но на английском написано тоже самое): Т.е. по идее значение имеют только ключи. Они должны быть целые, непрерывные от единицы. Ну вы понимаете. Это здесь естественно выполняется автоматом, но ipairs тем не мене не работает. Точнее ipairs то может и работает, но не работает алгоритм перебора, основанный на нём. Пришлось делать так: function fun(...) local args = {...} for k=1,#args do local arg = args[k] -- ... end end Самое интересное, что оператор получения длины # (решётка) при наличии значений nil работает без проблем и показывает полную длину, а не до ближайшего nil. Именно с этим столкнулся, когда делал безопасный универсальный вывод в лог-файл вместо штатного 'printf()', и пришел к аналогичному решению. Вот и в данном случае, в отношении таблиц-списков, интересует - как выделить такие 'args = {...}' даже с 'nil'-ами, отделив их от иных типов массивов/матриц. --/ Artos
  4. Artos, Совсем я запутался. Таки надо определить, массивом или таблицей общего вида является таблица? Или что-то другое?
  5. Artos, На мой взгляд нет такого способа. Ведь между этими таблицами нет никакой принципиальной разницы. Только в типе ключей и в наличии/отсутствии разрывов в последовательности ключей.
  6. Malandrinus

    [SoC] Вопросы по SDK

    Clayman, Потому что надо не делать уровень заново, а добавлять объекты на уже существующий и заселённый уровень. Но сейчас речь не об этом. Мне как раз надо для ТЧ, а в SDK для ТЧ компилятора спавна нет (если я правильно понимаю). Поэтому и вопрос, какими утилитами и в какой последовательности можно достичь похожего результата. Мне хотя бы понять, какие утилиты использовать. Ещё раз, исходное состояние - в SDK для ТЧ есть разобранный уровень с сеткой. Надо сделать так, чтобы можно было пересобирать аллспавн и только его. Сетку же пересобирать не только не нужно, но и нельзя в принципе, поскольку при этом потеряется ей совместимость с уже существующими объектами.
  7. Malandrinus

    [SoC] Вопросы по SDK

    Имеется задача такого плана. Есть готовая сингловая карта, с геометрией и сетками, а также аллспавн со всеми объектами. Хочется использовать редактор уровня для добавления предметов. Процесс представляется в следующем виде. - декомпилирую уровень - размещаю объекты на существующей сетке - собираю аллспавн, где имеются только добавленные объекты - разбираю его с помощью acdc - переношу в текстовом виде размещённые объекты в декомпилированный же мой, полный аллспавн - собираю мой полный аллспавн обратно уже с новыми объектами. Собственно вопрос. Реальна ли такая стратегия без перекомпиляции всего уровня каждый раз, как надо скомпилировать аллспавн? Если реально, то не подскажете ли последовательность утилит, которые надо для этого использовать. Смысл вопроса в том, что редактировать надо много и часто. Крайне важно избежать лишних действий. На мой взгляд, необходимым и достаточным является только компиляция аллспавна, посольку сетка и геометрия не меняются. Но возможно ли это, и что для этого надо сделать?
  8. Ранее я рекомендовал не использовать операцию конкатенации цепочкой в стиле s1..s2..s3..s4, поскольку счёл из общих соображений, что должен происходить степенной рост времени выполнения и резкое замедление работы при росте числа соединяемых строк. Я это утверждение не проверил, и выяснилось, что я ошибался. RvP провёл тесты и сравнил три разных метода конкатенации. Привожу его тест для чистого Lua в сокращённом виде с результатами измерений в комментариях.
  9. Для принудительной загрузки модуля есть специальная движковая функция prefetch(<имя модуля без расширения>)
  10. Kirag, Нет никакого нулевого окончания файлов. ОС Windows к файлам ничего не дописывает. Поэтому так делать нельзя. Если читаете текстовый файл с помощью FS, то надо читать его побайтно, потом собрать строку из байтов. Для этого есть функция string.char(<список символов>), а для получения списка из таблицы (куда читали) есть функция unpack(<таблица>). По байту конечно медленнее, чем разом файл. Но файлы здесь небольшие, это терпимо, а главное - так правильно.
  11. Artos, К чему исходники? Есть такая штука Утиный тест. Т.е. если нечто выглядит как утка, плавает как утка и крякает как утка, то это вероятно утка и есть. Так вот есть функция, написанная программистами на C, на языке С, с именем r_sringZ и вылетающая на файлах, не содержащих нулей. ок, язык конечно С++, но это мало что меняет. Не достаточно?
  12. Artos, Здесь трактовкам не место. Я изложил факт. Так работают строки в языке СИ. Z-строки - это другое название СИ-строк, или C-строк. Это структура данных вполне однозначная и означает последовательность символов с нулём в конце. Прочитайте внимательно. Природа сбоя в том, что при попытке чтения нуль-терминированной строки из обычного текстового файла происходит гарантированный выход позиции чтения за пределы файла. Это происходит в любом случае, находится файл в архиве или нет. Разница только в том, как реагирует система на это внештатную ситуацию. В одном случае (файл в архиве) происходит вылет, в другом (на диске), по какой-то причине вылета не происходит. Но это ошибка в обоих случаях! Скорее всего, при чтении другими методами проблем не будет ни в каком варианте, поскольку все остальные функции читают известное количество байт (1, 2, 4 и т.д.). Естественно, будет такая же ошибка, если я и другими функциями дойду до конца файла и попытаюсь оттуда прочитать. Признаться, вести дебаты о применимости того или иного метода в ситуации выхода за границы файла - это просто бред, уж извините. Этой ситуации просто не надо допускать. При грамотном создании программы её и не будет. Может да, может нет. В своей старой статье я ранее написал только то, что иногда файл не открывается, если он в архиве. Я уже давно к теме FS не прикасался, но если проблема открытия на самом деле есть (а не была мною придумана по неопытности на тот момент) и может быть решена копированием, то вы правы. Но это ещё до чтения. Если открыли хоть в архиве, хоть на диске, то дальше проблем при чтении быть не должно с любыми методами, просто не пытайтесь выйти за границы файла.
  13. Artos, Давайте по порядку. Метод r_stringZ своим названием говорит, что читается с-строка или иначе строка с нулевым терминатором. Это однозначно и безо всяких исключений означает, что в конце этой строки стоит нулевой байт. Текстовые же файлы - это файлы, имеющие достаточно определённую структуру: 1. Принимается соглашение, называемое кодировкой символов, относительно того, какой код символа какой букве соответствует. Часть кодов резервируется для специальных нужд и не соответствует никаким символам. В широко распространённой кодировке ASCII, которая по сути является основной для почти всех современных кодировок, отображаемые символы - это символы начиная с кода 32 (пробел). Ниже расположены управляющие символы. Их 32 и сейчас большая часть не имеет смысла (типа коды управления перфоратором). В том числе не имеет смысла код 0. Имеют смысл только некоторые типа табуляции или символов конца строки. 2. Принимается дополнительное соглашение о делении на строки. Сейчас используется один из двух символов "перевод строки", "возврат каретки" или их комбинация. Всё. Относительно символа с кодом "ноль". С самых доисторических времён этот символ имел значение "в строке не встречается". Именно поэтому в языке СИ этот символ был выбран как терминальный символ для хранения строк в памяти, потому что его ну никак не может быть в строке. В таком контексте под "строкой" разумеется не строка текстового файла, а "текст" в самом общем смысле, содержащий любые допустимые форматирующие элементы. Это может быть и текстовый файл целиком. Вот к примеру именно как z-строка хранится кастомдата в нетпакете - целиком файл конфигурации, содержащий несколько строк. Так что вполне очевидно, что метод чтения с-строки ну никак не может быть предназначен для работы с текстовыми файлами. Я сейчас взглянул на пример из поста Kirag-а. Ошибка очевидна. Естественно эта функция вызывает сбой при чтении из текстового файла. Там же нет ни одного физического нуля. Как этот сбой будет проявляться, зависит от того, где находится этот файл. Если на диске, то возможно внутренние механизмы чтения обрабатывают ситуацию иначе, нежели при ситуации нахождения его в архиве. В одном случае тихо завершает чтение, в другом - генерирует вылет. Но и в том и в другом случае - это ошибка. Ещё раз, FS предназначен для работы с бинарными файлами. Если есть желание читать с его помощью из текстовых, то это будет означать, что придётся делать всю работу, которую делает любой текстовый редактор: читать по байту, искать переносы строк и т.д. и относительно вот этого: Это откуда? Я такого не писал - это точно. Подчёркнутое - неправильно. При отсутствии терминатора будет классический выход за границу массива со всеми вытекающими. Собственно, все ранее приведённые примеры это и показывают. Кстати, поэтому z-строки считаются небезопасными в использовании. Народ, я сейчас лишён полноценного выхода в интернет и вообще времени маловато. Сложно держать руку на пульсе. Если кому не отвечаю сразу или не отвечаю вообще, то заранее прошу меня извинить. Если не отвечаю в личку, то не стесняйтесь написать ещё раз до тех пор, пока не отвечу. Я вполне серъёзно.
  14. Artos, Это не так совершенно. И класс FS и все его методы принципиально предназначены для работы с бинарными файлами. Метод типа readZ никак не может быть для текстовых файлов, поскольку в текстовых файлах строки не разделяются физическими нулями, а разделяются специальными символами. Почему может не работать, это другой вопрос. Насчёт наличия в ЧН/ЗП пространства имён io, это вполне может быть недосмотр разработчиков. Сами они его не использовали, а о сообществе они не думали никогда даже в малейшей степени.
  15. Artos, в качестве дополнения. В ЗП функция error_log рушит систему (как вероятно и задумывалось). Да и незачем заморачиваться в ЧН или ЗП с игровым логом, который тормозной и с ограничениями, при том, что можно выводить в произвольный файл через пространство имён io.
  16. Можете мне объяснить, зачем вам какие-то ранние билды, если есть 2947 с отладочной информацией? Это же фактически релизный билд. Чего там ловить в ранних билдах кроме багов?
  17. Kirag, Так и есть, поэтому использовать это надо только в отладочных целях, ни в коем случае в релизной версии.
  18. Что касается технологии Luabind, то там нет функции которая "всё биндит". Это потому, что Luabind построен на шаблонах C++ и для каждого экспорта при компиляции создаётся своя функция, причём не одна, а целая куча, и имён у них нет, поскольку они не выходят за пределы xrgame, да ещё и оптимизатор зачастую превращает это всё в малопонятную мешанину. Это кстати чертовски усложняет доэкспорт существующих в движке функций. Например, я совсем не представляю, как можно экспортировать некий класс. Я даже не понимаю, как можно экспортировать метод уже экспортированного класса, если нет экспортированного метода с таким же прототипом. В некоторых случаях удаётся это сделать, втиснув метод в другой, с похожим прототипом, оставив часть аргументов неиспользуемыми. Вот примерно на таком уровне удаётся работать, и лично мне дальше продвинуться не удалось. А о чём вы здесь беседы ведёте - я просто не понимаю. Видимо мне мозгов не хватает.
  19. amik, Смотреться будет ужасно (какие-то пятна вместо гладкой картинки), а уж ресурсов отожрёт страшно даже подумать. Это не считая мороки с реализацией. Подумай сам, какие расходы нужны, чтобы просчитать проекцию каждого пикселя из прямоугольной картинки светящегося экрана на произвольный объект. Это ж трассировка лучей в реальном времени. Не думаю, что на это способен даже топовый персональный компьютер. Максимум может обсчитать сцену с несколькими точечными источниками света и то по упрощённым алгоритмам. Точно, это оно.
  20. amik, Уф, да не помню я подробностей. Даже не помню, на каком форуме это было. С проектором то всё понятно, партикл расходящегося луча нужен, источник света и собственно стена с движущимся изображением. Там правда автор темы хотел, чтобы изображение проецировалось на все объекты между проектором и экраном, но тут уж ясен пёс не сделать ничего. Но по твоему вопросу там вроде как было ясное утверждение, что на любую стенку можно натянуть текстуру в виде видеоролика. И даже вроде был скрин с такой стеной. Что-то ещё говорили, что дефолтовые настройки шейдеров или чего то там ещё надо подправить, чтобы цвета нормально работали. Больше ничего не помню, хоть режь =) Ну и я этого не проверял, так что за достоверность не отвечаю.
  21. amik, Насчёт именно телевизора не знаю. Помню однако некую тему про создание проектора. Там проходило утверждение, что как раз такое возможно, и даже вроде выкладывали скрины со стеной, на которую выводился видеоролик. Что-то говорили о том, что это по умолчанию работает кривовато, но всё лечится в SDK. Вот только в упор не помню, на каком форуме я эту тему видел.
  22. KD87, Kirag, Это функция, которая возвращает разброс в радианах при стрельбе из текущего ствола. Работает для сталкеров и актора, но по-разному. Для актора зависит от режима прицеливания, положения тела, скорости перемещения и точности текущего ствола. Для сталкеров зависит от ранга, положения тела, состояния прицеливания и состояния (не скорости) перемещения и НЕ зависит от оружия. P.S.: Это кстати не значит, что точность сталкеров при стрельбе не зависит от точности оружия. Просто эта функция так работает, а где и в комбинации с чем используется - я не знаю.
  23. amik, Откуда такая информация? По моему они честно всё отдали вместе с последним SDK для ЗП.
  24. Kirag, не ошибаешься, там стоит цикл перебора. Это конечно движковый перебор, а не скриптовый, и не всех объектов на уровне, а только инвентаря, так что не столь уж и смертельно, но в самом деле лишнее. Тем более ты приводил и другие соображения и они также совершенно верны. Но ты сейчас интересную тему поднял. Есть некоторые функции, которые вроде и движковые, но достаточно тормозные. Достаточно небезобидный вызов - это к примеру вот это alife():object(id). Когда я ещё не лазил внутрь, то думал, что там сделано по уму - заведён массив указателей на 65536 позиций и объект берётся по индексу. А оказалось нет, тупой перебор. Опять же, это перебор движковый в линейном массиве, происходит относительно быстро. Но именно что относительно, поэтому крайне желательно избегать этого вызова и следовать стратегии "хранить объект и не искать его заново". Вызов level.object_by_id(id) в общем примерно такой же, просто перебирает клиентские объекты, а их естественно меньше. Но тоже желательно избегать.
  25. karavan, Сетку там можно выставить любого размера
×
×
  • Создать...