Nazgool 250 Опубликовано 12 Сентября 2011 Поделиться Опубликовано 12 Сентября 2011 (изменено) Полагаю что это уже мелочи, учитывая сказанное мною слово "набросок". Тем не менее ты прав в плане сравнения с нулем. Проверку с nехt делать всё равно прийдется. Например если есть и индексы от 1 до ... и не натуральные индексы. Изменено 12 Сентября 2011 пользователем Gun12 Ссылка на комментарий
RvP 1 Опубликовано 12 Сентября 2011 Поделиться Опубликовано 12 Сентября 2011 Gun12, насчет проверки да а вот насчет ключей думаю надо помнить. Тут недавно обсуждался ipairs, который тоже реагирует лишь на натуральные ключи, а в таблицах часто встречаются и другие, так что тут все же pairs... Vita sine libertate, nihil Vita sine litteris - mors est Ссылка на комментарий
Malandrinus 615 Опубликовано 12 Сентября 2011 Поделиться Опубликовано 12 Сентября 2011 Gun12, По поводу определения типа таблиц. Набросок без итераторов. Проверку на пустую таблицу и скорость не делал. function GetTabStatus(tab) if #tab ~= 0 and not next(tab, #tab) then return 'index' -- индексирудмый массив else return 'hash' -- всё подряд end end Увы, здесь не учитывается тот случай, когда в таблице произвольного вида имеются несколько ключей, отвечающих правилам "массивов". Т.е. если есть хотя бы ключ [1], то так таблица будет распознана как массив, хотя там может быть что угодно ещё. Я думаю, что здесь без полного перебора не обойтись. Что же касается исходно заявленной задачи сохранения таблиц/массивов, то очевидным решением будет делать отдельную функцию сохранения на разные варианты и не пытаться определять "тип" автоматически. В конечном счёте, конкретная таблица как правило в силу дизайна либо является массивом, либо имеет произвольный вид. Ключевой момент "в силу дизайна", т.е. программист заранее знает природу этой таблицы. Это означает, что в подавляющем большинстве случаев задача определять тип не стоит вовсе, поскольку тип и так известен заранее. Если же вдруг такая задача возникла, то лучше потратить силы не на ёё решение, а на редизайн кода с тем, чтобы привести его в состояние, когда вы таки знаете определённо, что из себя представляет эта таблица. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Nazgool 250 Опубликовано 12 Сентября 2011 Поделиться Опубликовано 12 Сентября 2011 После if-а #t определит наличие/отсутствие ключей, начинающихся с 1 и до последнего целочисленного поступательно нарастающего. Если есть такие, то проверяем, есть ли ещё какие-либо записи (nехt). Если есть - значит хэш, если нет, то индексный массив. Прогоните код в SсiТЕ. Не пойму, чего вы хотите от iраirs? Всё происходит так как и положено. Какому-то индексу присвоили значение nil. Сборщик мусора удалил это поле. Осталось дырка в индексах. Вот он и тормознул на этом поле. Совершенно правильно работает. Ссылка на комментарий
Artos 99 Опубликовано 12 Сентября 2011 Поделиться Опубликовано 12 Сентября 2011 (изменено) Не стал, чтобы не навешивать шоры, ранее давать свой двух-недельной давности набросок: local iIdx = next(tTbl) if iIdx then --/ проверка: таблица не пуста? --/#+# проверка: таблица типа 'список'? local bList = iIdx == 1 and #tTbl > 0 --/ флаг списка if bList then --/ предпроверка: может быть списком? --/ основная проверка: это 'список'? for i=1,#tTbl-1 do bList = next(tTbl,i) ~= nil if not bList then break end --/ прерываем - НЕ список! end end --/ тело основного кода end - набросок пока имеет изъяны ... Перебор как видно присутствует, но прерываемый. Остается внутри перебора максимально добиться 100% идентификации списков. malandrinus Тут два момента. 1. Елиная функция, которая обрабатывает (рас)паковывает двумя методами. Для надежности (и скорости) - предусматривается ключ-фргумент на входе, который диктует выбор метода. 2. Делаю все же универсальный вариант и для тех кто порой не понимает разницы в типах таблиц (т.е. не готов определять директивный аргумент) и ... порой таблицы могут иметь различный тип ... упаковка 't должна быть всегда и по возможности максимально экономная. Примечание: Один раз написанная, достаточно универсальная функция упаковки/распаковки (AMK-мод), используется до сих пор многими модами по сути 'в слепую'. Есть намерение и заменить ее на более актуальный и экономичный вариант и заодно избавить многие моды от ее ошибки (не дающей упаковывать некоторые типы таблиц): function pack_new(tbl) --/ ... elseif type(v)=="boolean" then ret=ret..string.char(pack_type_bool)..v --/<!?! конкатенация строки и булева значения!?! --/ ... end Исправленный вариант: ret=ret..string.char(pack_type_bool)..((v and "1") or "0") Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Nazgool 250 Опубликовано 12 Сентября 2011 Поделиться Опубликовано 12 Сентября 2011 Может я не так понял. Может вы имеете в виду таблицы с целочисленными индексами, но произвольными? Но ведь в условии были даны варианты таблиц именно индексируемые с 1 и по порядку.Все другие варианты рассматриваются как хэш-ы. С ними нельзя работать ни оператором #, ни table.concat. Также в условии стояла задача узнать тип таблицы. Я предложил вариант не только без пайрсов, но и без for i=1,#t. Условия выполнены. Что не так? Не пойму. Ссылка на комментарий
Artos 99 Опубликовано 12 Сентября 2011 Поделиться Опубликовано 12 Сентября 2011 (изменено) Gun12 В условии были приведены два возможных варианта таблиц-списков (а не или-или). Да, именно с индекса 1 они должны иль могут начинаться и по порядку. Да, именно с целочисленными, но НЕ произвольными. Порядок индексов может быть нарушен (иметь 'дырки'), если элемет(ы) списка были занилен(ы) (=nil). Такие 'дырки' просто исключаются из списка (таблица-список чистится). В твоем варианте непонятно, ведь 'next(tab, #tab)' для списка всегда должен возвращать 'nil', т.е. после последнего нет следующего элемента списка просто по определению. Может имелось ввиду 'next(tab, #tab-1) > 0', т.е. имеется последний элемент в списке => длина всей таблицы не ноль Изменено 12 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
=VENOM= 50 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 Artos, это я тебе возражу (и докажу) - т.к. в своё время всё то, о чём я говорил, неоднократно проверял, и всё это у меня и сейчас вполне так ничего себе работает . Начнём сначала: Файл 'smart_terrain_presets.ltx' является конфигом, который предназначен только для универсальных гулагов типа 'general_lager' Для универсальных гулагов предназначен файл-пресет general_lager.ltx - а для "именных", как ты выразился , предназначен тот, который я указал. Далее: респонеры NPC никаким местом не привязаны к каким-либо "гулагам". Поясняю ещё раз: любой респонер может плодить неписей для любых смарттеррейнов (или, если угодно, для "гулагов" ), если он подходит по праметрам (повторюсь: в общем случае - группировка, ранг). Чтобы убедиться в этом, достаточно через некоторое время после начала игры (после срабатывания респонеров, это дело в разных модах реализовано совершенно по-разному) просмотреть глобальную карту (следует включить отображение всех NPC (или только сталкеров) на глобальной карте - как это сделать, пояснять, надеюсь, не потребуется?). В ярлычках неписей видно, что смарты наполнены совершенно разными респоновыми сталкерами, я имею в виду сталкерами, рождёнными совершенно разными респонерами. И не надо валить всё на алайф, "перегоняющий" сталкеров из одних смартов в другие, повторюсь: отслеживая путь любого NPC от респонера до смарта, становится очевидно, что никакие респонеры не привязаны ни к каким смартам. Единственные респонеры, срабатывающие в момент взятия NPC под гулаг (при условии, что условие для них прописано в смарте) - это респонеры, наполняющие ящики с барахлом в смартах. То есть, поменяв в конкретной секции "опытных" на "мастеров", ничего глобального ты не совершишь, т.к. в оллспоне есть куча других респонеров, которые наплодят и "опытных", и "новичков" в предостаточном количестве. Далее: человеку явно было нужно всего лишь заспонить и поселить на Кордоне бандоса-мастера, и для этого совершенно не хотелось лезть в оллспон (да это и ни к чему, я уже пояснял, что нужно для этого сделать, причём двумя способами ). А если уж ты решил создать дополнительные секции спона (отдельные, для "мастеров" и т.п.), то, чтобы прописать их в существующий респонер, потребуется править оллспон. Далее: ты, видимо, не знаком с тем фактом, что для любой работы в скриптовых файлах гулагов (тот же gulag_escape.script) можно создать условия "приёма на работу" (я частично уже отписывал, по каким параметрам это можно сделать - там ещё много чего можно "наусловить" ). Эти условия называются predicate, и их, повторяю, можно применить к любой работе гулага. Если нужен конкретный пример - не поленись, загляни в этот файл (gulag_escape.script), и посмотри, как это реализовано для работы Волка или Фаната в смарте "лагерь новичков" (esc_lager). Таким образом, на работу "пахана" в смарте бандитов на АТП можно поставить условие, человеческим языком описываемое как "на работу принимается (назначается) только NPC с группировкой "бандит", с именем профиля "sim_bandit_master", и с рангом, равным 900 и более". Интересный факт: таким образом, кстати, можно обойти ограничение, выставленное для смартов Кордона в ltx-файле (конкретно smart_terrain_presets.ltx). Можно ещё кучу условий добавить . Заключение: в указанный смарт (esc_fabrika_bandit) в оригинальной игре принимаются NPC с группировкой "бандит", с рангами "новичок" (ранг менее 300), и "опытный" (ранг более 300 и менее 600). Никакие "мастера", заспоненные каким-то "назначенным именно для этого гулага" респонером под гулаг в смарте браться не будут. Чтобы изменить статус-кво, см. мой предыдущий пост - там всё для этого есть. Ссылка на комментарий
Artos 99 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 (изменено) =VENOM= Для чего такой запал? Нередко в пылу запала пропускается суть иль видятся фантомы ... 1. Если уж сначала - то так и давай с самого начала не употреблять по возможности частный жаргонарий, а оперировать общепонятными терпинами. Мне, например, не ясно, что ты подразумеваешь под 'респонеры NPC'? Если под ним подразумеваешь объект класса 'respawn', то почему именно NPC? Респавнеры могут и монстров и оружие и пр. объекты респавнть! 2. Где-то мною упомянута что респавнеры плодят объекты привязанные к гулагам? Само собою, отреспавненные объекты подчиняются тем конфигам, которые определены их секцией. Это кому-то нужно доказывать? Слова о связи этого конкретного респавнера 'esc2_respawn_bandits_fabrika' упомянута именно из-за его закомментированной логики связанной с конкретным гулагом. И его близким расположением - ведь работа выбирается от ближайшей ... Стоило ли оспаривать то, что не требуется оспаривать и доказывать то, что не требует доказательств? 3. С чего ты решил, что спрашивающий не хочет лезть в алл.спавн? С чего ты решил, что ему только один 'бандос-мастер' понадобился? Только потому что ты так решил и предложил? Перепрочти вопрос ... дабы освежить память и сознание. Если в вопросе автор только о правке конфига секции пояснил 'намерение заспавнить' и упомянуто 'заспавнить бандюков' - можно с большой долей вероятности сказать о неопытности спрашивающего в скриптовом спавне с правкой пакетов и тем более НЕ об одном бандюке ... Не находишь, между твоими разъяснениями и навязываниями - нечто схожее на объяснения 'как взять интеграл' на вопрос: "а сколько будет 2х2"? 4. Я то как раз не советовал 'создавать дополнительные секции' - а использовать уже готовые. Простая правка строки респавнера, добавляющая иль заменяющая секции - и после перекомпиляции алл.спавна - АТП заматереит (после смерти существующих). 5. Спасибо что просвятил нас сИрых и несмышленых насчет предикатов и возможности вносить свои условия для гулагов. Чоб мы делали ... Вот только одна загвоздка, а зачем растолковывать то, о чем НЕ спрашивается и апприори предполагать у опонентов незнание данной темы? Но спасибо и за то, что вдруг кому-то это потребно и прочитает. Лишним не будет. 6. Твое замечание о необходимости править 'smart_terrain_presets.ltx' для включения в гулаги на локациях дефолтных НПС соответствуещих рангов, принимаю и не оспариваю. Запамятовал, давненько не копал этот файл, давно уже доведенный до потребной кондиции. Итого: 1:5 в чью то пользу ;-) Читай написанное, а не выдумывай то, что только в твоей голове возникло и что сам же "доказываешь". Изменено 13 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 Gun12, Не пойму, чего вы хотите от iраirs? Всё происходит так как и положено. Какому-то индексу присвоили значение nil. Сборщик мусора удалил это поле. Осталось дырка в индексах. Вот он и тормознул на этом поле. Совершенно правильно работает. Сборщик мусора удаляет не ссылки, а значения. Между тем в таблице собственно хранятся именно ссылки. Будет ли удалена из таблицы ссылка зависит от ряда обстоятельств. Вот пример различного поведения: 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, 2. Делаю все же универсальный вариант и для тех кто порой не понимает разницы в типах таблиц (т.е. не готов определять директивный аргумент) и ... порой таблицы могут иметь различный тип ... упаковка 't должна быть всегда и по возможности максимально экономная. Рискну заметить, что это не самая лучшая идея сама по себе, а в соединении с теми, кто "порой не понимает разницы в типах таблиц" идея приобретает совершенно разрушительную мощь. Если функция заранее знает, какого типа аргумент будет на входе, то можно будет сделать проверки и остановить работу с вразумительным сообщением. Если на входе возможные варианты, то функция уже никак не сможет сказать, а что на самом деле хотел сделать программист. Здесь ошибка в данных, или это неправильно использованный вызов или что-то ещё? Вариантов становится слишком много, проверить их все уже невозможно. Остаётся только довериться программисту, но ведь программист подразумевается такой, что "порой не понимает разницы в типах таблиц". Такому только доверься... Это готовый бардак в коде, испорченные сейвы, практическая невозможность отлаживать и вообще сопровождать код. IMHO, не стоит этого делать. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 (изменено) Выкладываю предварительный (рабочий!) вариант упаковки и распаковки таблиц в строку и обратно. Предназначен для модернизации/замены варианта из АМК-мода v.1.4.x (pack_array_to_string/unpack_array_from_string) при сохранении в сэйвы. Совместим с прежними сэйвами из модов, использовавшими прежний формат упаковки. 1. Исправлена ошибка AMK-варианта при упаковке таблиц с булевыми значениями в полях. 2. При упаковке таблиц типа 'список': {v1,v2,v3,v4} в пак-строку для каждого элемента записывается только однобайтный признак, и опускаются сами индексы. Экономия: Общее кол-во байт всех индексов таблицы, каждый разряд которых - 1 байт. Т.е. для списка в 100 элементов - экономия 1х9 + 2х90 + 3х1 = 192 байта. 3. При упаковке целочисленных чисел > 9, число упаковывается не как десятичная строка, а как хекс-строка. Пример: 12345 == 0х3039 => "12345" ~= "3039" и экономится как минимум 1 байт на каждом числе. При сохранении чисел большой разрадности (до 14) - экономится болше: до 3 байта на каждое значение. 4. Совместим в прежними вариантами упаковки в модах и сэйвами. Обратной совместивости нет. --/------------------------------------------------------------------ --/ Внимание! Строки в структуре не должны содержать символов с кодами 0-31. --[[----------------------------------------------------------------- Формат упаковки: table ::= ( list | subtable ) list ::= keytype nil valuetype ( value | subtable 0x5 ) subtable ::= keytype key valuetype ( value | subtable 0x5 ) keytype ::= ( 0x1 | 0x2 | 0x3 | 0x4 | 0x6 ) valuetype ::= ( 0x1 | 0x2 | 0x3 | 0x4 ) --]]----------------------------------------------------------------- local tPackChar = { n = {1, string.char(1)}, --/ 'number' s = {2, string.char(2)}, --/ 'string' b = {3, string.char(3)}, --/ 'boolean' t = {4, string.char(4)}, --/ 'table' e = {5, string.char(5)}, --/ table-end l = {6, string.char(6)}, --/#+# table-list h = {7, string.char(7)} --/#+# number-hex } --/------------------------------------------------------------------ --/ Паковка таблицы в строку (и обратная распаковка) --/------------------------------------------------------------------ --/ упаковка таблицы в строку - функция синоним прежней function pack_array_to_string(tTbl,bList) --/< таблица [, (true|false) -флаг: список или нет] return Pack_Tbl(tTbl,bList) --/> String end --/ распаковка 'упакованной' строки в таблицу - функция синоним прежней function unpack_array_from_string(sStr) --/< строка упакованной таблицы if sStr and sStr ~= '' then --/ защита от отсутствия или пустой строки if sStr:sub(1,1) == string.char(1) then --/ старый формат упаковки был тэгирован символом c кодом '1' if sStr:sub(2,2) == tPackChar.n[2] or sStr:sub(2,2) == tPackChar.s[2] then --/ 2-м символом начинается упакованная строка sStr = sStr:sub(2,-1) --/ отрезаем 1-ый символ end end return Parse_Str(sStr) or {} --/> Table end return {} --/> ZeroTable end --/------------------------------------------------------------------ --/ упаковка таблицы 'tTbl' в строку 'sStr' function Pack_Tbl(tTbl,bList) --/< таблица [, флаг: список или нет] if type(tTbl) ~= 'table' then return "" --/> не таблица end local sStr = "" local iIdx = next(tTbl) if iIdx then --/ проверка: таблица не пуста? if bList == nil then --/ если не задан тип таблицы - определяем 'список' или нет --/#+# проверка: таблица типа 'список'? bList = iIdx == 1 and #tTbl > 0 if bList then --/ предпроверка: может быть списком? --/ основная проверка: этот 'список'? for i=1,#tTbl-1 do bList = next(tTbl,i) ~= nil if not bList then break end --/ Не список?! end end end --/< ---------------------------------- local key,value,sType = nil,nil,nil for key,value in pairs(tTbl) do if bList then --/ упаковывается список? sStr = sStr .. tPackChar.l[2] --/ маркер списка (list) else sType = type(key) if sType == 'number' then if key > 9 and key <= 65535 and key == math.floor(key) then --/ 2-х байтовое или более и целочисленное? sStr = sStr .. tPackChar.h[2] .. string.format('%X', key) --/ метка хекс-строки else sStr = sStr .. tPackChar.n[2] .. key end elseif sType == 'string' then sStr = sStr .. tPackChar.s[2] .. key else abort("Pack_Tbl:UnSupported_KeyType=" .. tostring(sType) end end sType = type(value) if sType == 'number' then if value > 9 and value <= 65535 and value == math.floor(value) then --/ 2-х байтовое или более и целочисленное? sStr = sStr .. tPackChar.h[2] .. string.format('%X', value) --/ метка хекс-строки else sStr = sStr .. tPackChar.n[2] .. value end elseif sType == 'string' then sStr = sStr .. tPackChar.s[2] .. value elseif sType == 'boolean' then sStr = sStr .. tPackChar.b[2] .. ((value and "1") or "0") elseif sType == 'table' then sStr = sStr .. tPackChar.t[2] .. this.Pack_Tbl(value) .. tPackChar.e[2] --/> рекурсивный вызов else abort("%s:Pack_Tbl:UnSupported_ValueType=" .. tostring(sType) end end end return sStr --/> end --/ распаковка в таблицу 'tTbl' строки 'sStr' (или части строки от 'at') function Parse_Str(sStr,at) local tTbl,iLen = {},sStr:len() local key,value,iByte if not at then at = 1 end --/ парсинг с начала строки while at < iLen do --/ (суб)строка не закончилась? --/ парсинг 1-го символа (суб)строки iByte = string.byte(sStr:sub(at,at))--/ численное значение at-го символа в строке at = at +1 --/ переход к следующему символу строки if iByte == tPackChar.l[1] then --/ таблица типа 'список'? (table-list) key = nil --/ для элементов списка индексы формируются автоматически --/ парсинг 'key' elseif iByte == tPackChar.n[1] then --/ 'number'? key,at = Get_Num(sStr,at) elseif iByte == tPackChar.h[1] then --/#+# 'number-hex'? key,at = Get_Dec(sStr,at) elseif iByte == tPackChar.s[1] then --/ 'string'? key,at = Get_Str(sStr,at) elseif iByte == tPackChar.e[1] then --/ конец (суб)таблицы? (table-end) return tTbl,at --/> субстрока субтаблицы закончилась - выход из функции else abort("Parse_Str:(%s):UnSupported_TypeKey=" .. tostring(iByte) end --/ парсинг 'value' iByte = string.byte(sStr:sub(at,at))--/ численное значение at-го символа в строке at = at +1 --/ переход к следующему символу строки if iByte == tPackChar.b[1] then --/ 'boolean'? value,at = Get_Bool(sStr,at) elseif iByte == tPackChar.n[1] then --/ 'number'? value,at = Get_Num(sStr,at) elseif iByte == tPackChar.h[1] then --/#+# 'number-hex'? value,at = Get_Dec(sStr,at) elseif iByte == tPackChar.s[1] then --/ 'string'? value,at = Get_Str(sStr,at) elseif iByte == tPackChar.t[1] then --/ 'table'? value,at = this.Parse_Str(sStr,at) --/ рекурсивный вызов для 'табличных субстрок' else abort("Parse_Str:(%s):UnSupported_TypeValue=" .. tostring(iByte) end if key == nil then --/ элемент списка? table.insert(tTbl, value) --/ добавляем в таблицу типа 'список' (table-list) else tTbl[key] = value end end return tTbl, at --/> end --/------------------------------------------------------------------ --/ по-символьный парсер строки 'sStr' от at до первого 'управляющего' символа function Get_Str(sStr,at) --/< стринг(строка) и начальный индекс в нем local iLen = sStr:len() --/ индекс конца стринга(строки) for i=at,iLen do if string.byte(sStr:sub(i,i)) < 32 then --/ 'управляющий' символ? if i == at then --/ 1-ым символом идет управляющий? (пустая строка) return "", i --/> zero-string,iNext end --/ начальная часть (суб)строки и индекс первого упр.символа return sStr:sub(at,i-1), i --/> sSubStr,iNext end end --/ вся (суб)строка до конца и индекс 'после конца' return sStr:sub(at,iLen), iLen+1 --/> sSubStr,iNext end --/ перевод части (от 'at') строки 'sStr' в число (десятичное) function Get_Num(sStr,at) local sSubStr,iNext = Get_Str(sStr,at) local iNum = tonumber(sSubStr) if iNum then return iNum, iNext --/> elseif sSubStr == "" then --/ пустая субстрока? return 0, iNext --/> #?# не будем пока прерывать else abort("Get_Num:SubStr=" .. tostring(sSubStr) end end --/ перевод части (от 'at') строки 'sStr' в десятичное число function Get_Dec(sStr,at) local sSubStr,iNext = Get_Str(sStr,at) local iDec = tonumber(sSubStr, 16) --/ переводим хекс-строку в десятичное число if iDec then return iDec, iNext --/> elseif sSubStr == "" then --/ пустая субстрока? return 0, iNext --/> #?# не будем пока прерывать else abort("Get_Dec:SubStr=" .. tostring(sSubStr) end end --/ перевод части (от 'at') части строки 'sStr' в булево значение (true/false) function Get_Bool(sStr,at) local sSubStr,iNext = Get_Str(sStr,at) return (sSubStr == "1" or string.lower(sSubStr:match('^%s*(%S*)')) == 'true'), iNext --/> end --/------------------------------------------------------------------ Критика, предложения, возражения и вопросы - приветствуются! ===================================================== Еще один вариант оптимизации для 'штатных' функций игры: Файл 'xr_logic.script', функции чтения/записи в 'pstor' объектов и функции чтения/записи в нет-пакеты объектов (для сэйвов). Особенности: 1. Дополнительная стабильность при возможных некорректных вызовах функций. 2. Защита от некоторых 'битых' сэйвов. Кол-во записей для не актора ограничено 48-мью переменными (опционально). 3. Оптимизация скорости отдельных операций. 4. Экономичность расходования байтов: Целые числа из диапазона -32768...65535 записываются в 2 байта, вместо прежних 4-х. 5. Совместимость с прежними сэйвами. Обратная совместимость отсутствует. local tPstorFunc_r = { [0] = "r_float", [1] = "r_stringZ", [2] = "r_bool", [3] = "r_u16", --/#+# [4] = "r_s16" --/#+# } local tPstorFunc_w = { ["number"] = {0, "w_float"}, ["string"] = {1, "w_stringZ"}, ["boolean"] = {2, "w_bool"}, ["u16"] = {3, "w_u16"}, --/#+# ["s16"] = {4, "w_s16"} --/#+# } function pstor_is_registered_type(sType) if sType == 'number' or sType == 'string' or sType == 'boolean' then return true --/> end return false --/> end --/ запись в storage объекта function pstor_store(oObj, sVarName, Value) local idObj = oObj and oObj:id() if idObj and sVarName then --/#fix# local tStor = db.storage[idObj] if Value == nil then --/ стираем if tStor.pstor then tStor.pstor[sVarName] = nil end return --/> elseif tPstorFunc_w[type(Value)] then if not tStor.pstor then tStor.pstor = {} end tStor.pstor[sVarName] = Value return --/> end printf("pstor_store:VarName=[%s],value=[%s]<~Not_Registered_Type=[%s]:<%s>", sVarName, Value, type(Value), "Warning!") end printf("pstor_store:Obj=[%s],varname(%s)=[%s]:<%s>", oObj and oObj:name(), sVarName, Value, "Warning!") abort("pstor_store:Obj=[%s],varname=[%s]:<%s>", oObj and oObj:name(), sVarName, "Error!") end --/ чтение из storage объекта function pstor_retrieve(oObj, sVarName, DefValue) local idObj = oObj and oObj:id() --/#fix# local tStor = idObj and db.storage[idObj] if tStor and tStor.pstor then local Value = tStor.pstor[sVarName] if Value ~= nil then return Value --/> end end return DefValue --/> end function save_logic(oObj, packet) --/ не изменяем local tStor = db.storage[oObj:id()] packet:w_s32( (tStor.activation_time or 0) - time_global() ) utils.w_CTime(packet, tStor.activation_game_time) --/ GAMETIME added by Stohe. end function load_logic(oObj, reader) --/ не изменяем local tStor = db.storage[oObj:id()] tStor.activation_time = reader:r_s32() + time_global() tStor.activation_game_time = utils.r_CTime(reader) --/ GAMETIME added by Stohe. end function pstor_save_all(oObj, packet) local idObj = oObj:id() local tPstor = db.storage[idObj].pstor if not tPstor then db.storage[idObj].pstor = {} packet:w_u32(0) --/< count variables return --/> elseif next(tPstor) == nil then --/ пустой? packet:w_u32(0) --/< count variables return --/> end local iCnt = 0 for k,v in pairs(tPstor) do iCnt = iCnt +1 end packet:w_u32(iCnt) --/< count variables local tFunc,oFunc --/#+# for k,v in pairs(tPstor) do tFunc = tPstorFunc_w[type(v)] if tFunc then --/#+# если число ("number") - проверим: достаточно ли 2-х байт? if tFunc[1] == 0 and v < 65535 and v > -32768 and v == math.floor(v) then --/ проверяем целочисленное число на знак if v >= 0 then tFunc = tPstorFunc_w["u16"] --/ положительное else tFunc = tPstorFunc_w["s16"] --/ отрицательное (требуется сохранить и знак) end end packet:w_stringZ(k) --/< name variable packet:w_u8(tFunc[1]) --/< type variable oFunc = packet[tFunc[2]] --/ write-function oFunc(packet,v) --/< value variable else printf("pstor_save_all:obj=[%s],id=[%s],type=[%s]<~?:k=[%s],v=[%s],:(%s):<%s>", oObj:name(), idObj, type(v), k, v, iCnt, "Error!") abort("pstor_save_all:not_registered_type=["..type(v).."]<~encountered") return --/> end end end function pstor_load_all(oObj, reader) local idObj = oObj:id() local tPstor = db.storage[idObj].pstor if not tPstor then tPstor = {} db.storage[idObj].pstor = tPstor end local iCnt = reader:r_u32() --/< count variables if iCnt == 0 then return --/> elseif iCnt > 48 and idObj ~= db.actor:id() then --/#fix# для не актора допустимо не более 48 переменных printf("pstor_load_all:Obj=[%s],Cnt=[%s]>32:<%s>", oObj:name(), iCnt, "Warning!") iCnt = 32 end local sVarName,iType,sFunc,oFunc for i=1, iCnt do sVarName = reader:r_stringZ() --/< name variable iType = reader:r_u8() --/< type variable sFunc = tPstorFunc_r[iType] --/ name read-function if sFunc then oFunc = reader[sFunc] --/ read-function tPstor[sVarName] = oFunc(reader) --/< value variable else printf("pstor_load_all:Obj=[%s],id=[%s],var(%s)=[%s],type=[%s]<-?:(%s/%s):<%s>", oObj:name(), idObj, sVarName, tPstor[sVarName], iType, i, iCnt, "Error!") tPstor[sVarName] = nil end end end ===================================================== Добавлено через 14 мин.: malandrinus И согласен с твоими доводами и нет. Если скальпель предназначен для тех, кому это требуется (хирургам) и в то же время его может купить любой и им воспользоваться - это не означает, чтобы скальпель лишить его 'опасных' свойств (дабы не порезались). Под универсальностью и применимостью для использования всеми все же понимаю наличие элементарной осторожности и начальной информированности у тех, для кого предназначается. Если модмейкер не уверен в себе и в своих табличных 'познаниях' - не должен вообще задавать второй аргумент. Т.е. использует 'в слепую', доверившись программисту, написавшему сею поделку. Если же уверен, и не хочет зависеть от причуд кода определения типов таблиц - берет ответственность за ошибки на себя. Если же и не уверен в себе и не доверяет стороннему программеру - флагом 'false' запрещает работу по экономии для списков. Согласись, набор правил применения довольно прост и достаточен. Если можно так выразиться: "Если ты в скриптах 'нуб' - используй по дефолту, не трогая руками." ;-) Замечу, прежний вариант, используют далеко не знатоки таблиц и кодов в своих модах ... И даже наличие фатальной ошибки для булевых полей - не сподвигнуло ни отказаться от паковки таблиц, ни к исправлению понимающими этой ошибки, дабы использовать и этот вариант полей. И последний аргумент(ы): Проверено и протестировано на себе во всех режимах - все же чего-то стОит. Если нечто неудобно и вредно, то пройдет довольно мало времени и от нее все откажутся ... Возможная пара лишних шишек (потеряного времени) - небольшая плата за 'пощупать' и воспользоваться тем, что может быть полезным. ИМХО. Спички детям не игрушка, но запретить их нельзя и запирать каждый раз в железный сэйф - не решение от детской глупости. Изменено 13 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Nazgool 250 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 Artos Что-то мы говорим на разных языках. Попробую ещё раз. Вопрос-1: Как оптимально определить тип таблицы 'список' (list), т.е. типа: {"a","b","c","d"} или {1,5,2,4,3}? По типу построения между этими таблицами нет никакой разницы. На самом деле они же выглядят так : {[1]='a', [2]='b', [3]='c', [4]='d'} и {[1]=1, [2]=5, [3]=2, [4]=4, [5]=3} Т.е. оператор # определит ВСЕ! поля этих таблиц. ...определение именно списка, а не иного типа массива. Если добавить в любой из списков поле, не соответствующее продолжению "списка", например {[1]='a', [2]='b', [3]='c', [4]='d', [7]='e'}, либо 'занилить' значение, после чего поле удалиться из списка {[1]='a', [2]='b', [3]=nil, [4]='d'}, то список перестанет быть таковым и станет хэшем на общих основаниях. Теперь оператор # определит в первом случае из 6-ти фактических полей только 5 (от индекса 1 до индекса 5). Во втором случае только 2 (т.к. поле с индексом 3 исчезло, и следующий индекс после 2 будет индекс 4), вместо фактических 3-х. !!! Для того, чтобы не нарушать строение таблицы как списка (который работает быстрее чем любой другой тип таблиц) нужно пользоваться специально созданными для этого функциями table.insert и table.remove, а не заниливанием и непонятно каким добавлением. ...и не полный перебор списка на поиск 'дыр' в индексах Теперь о 'дырах' и собственно моем варианте без перебора. Имеется 3 типа таблиц : t1 = {a='p', k=6, [12]='s'} t2 = {'a', 'b', k=6} t3 = {'a', 8, 'c'} Хотя в таблице t2 и есть элеметы списка, но и она, и таблица t1 не являются списками. Это комбинированые хэш-таблицы. Получается что для списка нужно определить, равна ли длина таблицы фактическому количеству полей. Вот я и пошел таким путем. if #tаb~=0 Определяю, есть ли в таблице элементы списка (индексов от 1 и до упора). Если нет, то это уже не список. Ответ - хэш. Если есть такие элементы, то последний из них по любому найдется. И теперь проверяя nехt(tаb,#tа смотрю, есть ли за этим последним элементом списка ещё поля, характерные для хэша? Если нет (nil), то фактическая длина таблицы равна количеству полей, что означает одно - таблица является списком. Ну а если есть ещё поля, то это смешанная таблица, а по сути НЕ список. Добавлено через 48 мин.: В варианте с заниливанием внутри таблицы ([3]=nil) допустил неточность. Читать 'заниливанием извне', например t[3] = nil Ссылка на комментарий
Кивач 5 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 (изменено) Как сделать так, чтоб другие Сталкеры курили? Там по моему, нужен такой код: kurit sidya_ 0 ? Изменено 13 Сентября 2011 пользователем Кивач Ссылка на комментарий
Artos 99 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 (изменено) Кивач Для ответа на твой вопрос потребно написать очень о многом. Это и добавление вырезанных анимаций в коды игры и о редактировании/добавлении события "курит" в нужные схемы (например для посиделок у костра), и о добавлении нужных моделей текстур, иконок для сигарет и их пачек ... Если имеешь опыт редактирования конфигов и скриптов - не проще ли поиском отыскать готовые наработки по этой теме и взглянуть как и что там сделано? Gun12 Или я никак не могу пояснить что хотелось бы или ты читаешь мои попытки прояснить по диагонали ... Попробуем иначе: 1. Обе таблицы: {"a","b","c","d"} И {1,5,2,4,3} - это в контексте вопроса именно списки, но с разным наполнением одним типом значений, и даны как примеры списков. 2. Эти два варианта списков могут иметь и вариации на тему: смешанные типы элементов: строки+числа , числа многоразрядные и не по порядку и т.д. Т.е. вариаций может быть много. 3. Выше твоего поста приведены коды упаковки и распаковки таблиц в строку и обратно. Именно в контексте этих кодов и требуется решение для определения 'список иль нет'. Как заявлено: для списков при упаковке НЕ сохраняются индексы элеменов списка, а только их значаения. Т.о. требуется определить именно те типы таблиц (выставить флаг 'bList'), для которых подобная 'экономная' упаковка не приведет к потере данных после распаковки. Примечание: То, что в таблице-списке могут быть заниленные (пустые) элементы и такие элемены не попадут в упаковку - считаетс плюсом, т.е. упаковка с одновременной чисткой структуры списка. P.S. Про 'nехt( tаb, #tаb )' - понял, но т.к. это все же точечная проверка - и не допонял ее цели. 'Точечная' - если в таблице будет после 'списочного' фрагмента идти 'nil' и продолжение с хеш-элементами - то такая проверка, вернет 'nil'? наткнувшись на него после списочного фрагмента и НЕ увидит все остальное. Т.е. это все же частный случай. Изменено 13 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Кивач 5 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 (изменено) Artos, я добавил сигареты и попытался на основе колбасы сделать. Но они упорно отказываются курить (Может не хотят начинать курить? ). Но вылетов нет. Там по моему нужны (для добавления события "курит") фалы скриптов: state_lib, state_mgr_animation, state_mgr_animation_list и xr_kamp. Модель и в стандартном игре есть. Топик все же не для погадалок ... Так можно гадать до бесконечности. Если что-то сделал и не получается - поясняешь и/или выкладываешь сделанное - и можно будет искать ошибки или подсказать недоделки. Если же ничего не сделано, а только похотелка - читаем шапк темы, читаем статьи на вика сайтах по теме, ... и тогда задаем конкретные вопросы о недопонятом и/или ненайденном. --/ Artos Изменено 13 Сентября 2011 пользователем Artos Ссылка на комментарий
Malandrinus 615 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 Gun12, Теперь о 'дырах' и собственно моем варианте без перебора... ... Вот я и пошел таким путем. if #tаb~=0 Определяю, есть ли в таблице элементы списка (индексов от 1 и до упора). Если нет, то это уже не список. Ответ - хэш. Если есть такие элементы, то последний из них по любому найдется. И теперь проверяя nехt(tаb,#tа Вообще говоря, перебор там таки есть, хотя и внутри операции размера. Эта операция для получения размера как раз и перебирает все элементы. Где-то я об этом в мануале читал. Хотя это разумеется не скриптовый перебор и не так уж затратно. Дальше, первая часть алгоритма так и не сможет определить наличие дырок в массиве в случае, как я приводил выше. В принципе не критично. Надо только не пользоваться ipairs и проверять на эти дырки при сохранении. Ну и наконец, идея с next целиком полагается на допущение, что при индексации часть таблицы с хешем проверяется после проверки части с линейным массивом. Иначе, как можно утверждать, что после последнего элемента массива идут элементы с хешем? А вдруг они в общей последовательности идут впереди? Понятно, что из общих соображения код сделан так, что сначала проверяется менее затратный линейный массив. Однако, при переборе ассоциативного массива в общем случае нельзя делать допущения о порядке элементов. Я обратил внимание, что модостроение вообще поощряет развитие хакерского склада мышления. Чтобы вы не обольщались этим утверждением, я считаю, что это совершенно губительно для нормального развития программиста. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Artos 99 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 malandrinus (хотя и оффтопик, но все же касательно темы) 'Модостроение' (в моем понимании и касательно именно игры): Создание неких модифицированных кодов или изменение существующих кодов, которыми что-либо удаляетсяю, изменяется или добавляется в игру. Этим может заниматься программист (или команда таковых) или одиночка (НЕ програмист), который решил по какой-то причине внести изменения в игру. Например, я, не считаю что как то отношусь к программистам и занимаюсь модмейкерством в качестве хобби. Развиваться в себе прграммиста? Если это помогает мне достигнуть своей похотелки - да, готов грызть мануалы и пр. Если моя похотелка достижима доступными мне путями и не требует углубления познаний и навыков - то, не забывая что это все же хобби, не считаю интересным тратить время на поиски классических решений. Согласен, со стороны подобный подход для тех, кто явлчется иль считает себя программистами - рассуждения и подход порочен. Но(!) мне и не нужны именно классические и правильные решения. Мне, а) интересно решить задачу по достижению своей похотелки и б) получить желаемое. Задачи стать программистом и развиваься - не имею. Смею предположить, что таких как я (и даже похуже в плане рассуждений) в числе занимающихся модмейкерстовом игры основная масса. Программистов наверное можно посчитать на пальцах ... О хакерском складе мышления: Ой, лучше бы ты не задевал эту тему. То, что в нынешнее время стало синономом 'взломщик программ иль иже ...' - далеко от изначального смысла слова 'хакер'. Но не будем отвлекаться ... Да, хакер (и в моем понимании) - тот, кто помимо использования ключа для открытия закрытой двери, может воспользоваться иным вариантом. Взломщик взломает, неумеха - будет торчать перед закрытой дверью, хакер - ... а почему не воспользоваться иным выходом, если таковой найдется, иль даже воспользоваться окном ... иль не ломать замок. а просто попросиь того кто с наружи - открыть ему дверь. Т.е. хакер - это тот, кто не обязательно использует классически правильный вариант, а тот, который ищет и находит вариант, который позволяет достичь необходимой цели. Плох ли такой склад мышления, а точнее подход при создании модификаций? Хм, если бы все руководствовались чисто программистским подходом - ИМХО, врядли так далеко бы зашло модифицирование игры! Поощрять ли подобный склад мышления? ИМХО, да! Большая часть нового, свежего, никем никогда не деланного - результат именно такого мышления. Большая часть тех же программистов, будучи 'чайниками', начинает именно с такого мышления. И уже позже, поняв необходимость основательной бызы знаний и навыков для создания полных и масштабных вещей иль экономии времени без метода 'тыка' - начинает мыслить иначе ... ИМХО, упрощенно: Программист - большей частью теоретик (практик в написании алгоритмов/кодов), Хакер - общий практик, с программистскими навыками, которые в чем-то более глубоки чем у первого, но в чем-то поверхностны. Для создания модификаций более адаптирован именно второй. "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Nazgool 250 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 (изменено) Иначе, как можно утверждать, что после последнего элемента массива идут элементы с хешем? А вдруг они в общей последовательности идут впереди? ...Однако, при переборе ассоциативного массива в общем случае нельзя делать допущения о порядке элементов. Изменено 13 Сентября 2011 пользователем Artos Ссылка на комментарий
Artos 99 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 (изменено) Gun12 Что же в итоге? Такой вариант достаточен для селекции в игре таблиц, для которых не требуется сохранять индексы элементов?: local iCnt = #tTbl --/ длина индексированной части таблицы local bList = iCnt > 0 and iIdx == 1 and not next(tTbl,iCnt) --/ флаг: tTbl является списком (true) - т.е. проверка: имеются элементы 'списка', 1-й индекс начинается с 1 и за 'списком' отсутствует хеш-элемент Изменено 13 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Malandrinus 615 Опубликовано 13 Сентября 2011 Поделиться Опубликовано 13 Сентября 2011 Gun12, Наверное ты всё-таки прав. Не нужно высовываться Где я такое сказал? Artos, мне интересно, что ты будешь делать с этой функцией дальше. Вот ты проверил, что это массив. Но ведь это только говорит об упорядоченности ключей. А что насчёт значений? Ведь в качестве значений может быть что угодно: числа, строки, таблицы, пользовательские объекты. До какой степени ты будешь пытаться автоматизировать обработку? Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти