Artos 99 Опубликовано 21 Сентября 2011 (изменено) malandrinus Может быть кто-то знает такой инструмент по форматированию LUA контента и посоветует, но мне готовое не известно, но ... Мысль в слух: Подобное форматирование вероятнее, если и реализуемо, то в виде макроса(ов). Если найти или написать требуемый макрос - то уже упоминавшиеся SciTE или UltraEdit позволяют подключать макросы и пользоваться ими. Обычно около 'крупных' редакторов для программеров формируется комьюнити, которое пишет что-то и на темы форматирований текстов, и выкладывает в депозитории. Могу ошибаться, т.к. давно не занимался такими поисками - но что-то на тему форматирования текстов на уровне макросов попадалось пару лет назад. Изменено 21 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Сентября 2011 (изменено) malandrinus Странно, слова твоей критики больше подходят к пользователям Винды и меньше к программерам. 1. По умолчанию программистский софт ставится или с административными правами или с расширенными. "Lua for Windows", частью которого и является SciTE, требует при установке именно прав админа. 2. Следствие некорректной/частичной установки. 3. Есть два подхода: Windows - "Запрещено все, что неразрешено" и UNUX - "Разрешено все, что не запрешено". Ты просто привык к первому. 4. Это все же продукт для тех, кто знает что ставит и чем пользуется, а не для тех, кто сам не помнит что поставил и зачем. Минимальная инфа есть: версия, ссылки ... 5. согласен, но возможно можно настроить. 6. согласен, хотя опять таки тут конфликт взглядов. Если я сижу и обеими руками стучу по клаве, то мне более чем достаточно хоткея (Ctrl+W), для закрытия окон, чем искать мышь позиционировать на крестик и кликать ... На первый взгляд и первое время мало какой софт воспринимается удобным, если человек уже привык к чему-то иному. Все же везде требуется адаптационный период. Ну а если что-то остается неудобным или недостаточным - поэтому я ранее и писал о том, что в основном сижу в UE, а SciTE - тогда, когда именно занимаюсь около-"языковыми" вопросами. Изменено 21 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Сентября 2011 (изменено) Вопрос: Каковы возможности в LUA (применительно к Сталкеру) по работе из скриптов со стеком? В мануале по LUA достаточно куцая информация и практически только для внешнего использования (применительно к С/С+) ... Помнится в том же Perl'е из скриптов работа со стеком хотя и редко, но используется и нередко позволяет сильно ускорить отдельные операции. Есть ли какие мануалы/статьи/примеры по стековым методам/операциям для LUA? Добавлено через 33 мин.: Gun12, поправлю: ищем строку : command.go.*.lua=lua -e "io.stdout:setvbuf 'no'" "$(FileNameExt)" (и если есть строку command.go.subsystem.*.lua;*.macro=3) Тоже комментим её(их), и ниже пишем новую строку : command.go.*.lua=lua "$(FilePath)" Этим самым настраивается интерпретатор для работы с lua. строка для возможности запускать скрипты из Сталкера (*.script) должна быть: command.go.*.lua;*.macro;*.script=dofile $(FilePath) Изменено 21 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Сентября 2011 (изменено) abramcumner: содержимое lua_help.script не экспортировано Ну, во-первых, это и не скрипт (lua_help), а считай напоминалка модмейкерам ... далеко не полная и нередко устаревшая. Во вторых, даже если ... то мы-то кто(?) - модмейкры, т.е. те кто сами пишет/добавляет и классы и пр. - значит "наше" самописное тоже добавлять потребовалось бы. Само собою, любой скрипт, в котором будут встречаться неопределенные переменные/классы/функции будет выполняться ровно настолько, насколько это все неизвестное определено/доступно. Мне, например достаточно проверять не полностью скрипты из игры, которые привык писать для нее, а автономные простенькие, в которых не используются внешние переменные и пр. Проверить работоспособность, скорость, сравнить, получить результат некоей вычислительной функции - все это и позволяет SciTE при его адаптации в игре (к файлам *.script). Ну и ... можно же и экспортировать нужное тебе (поэтому мод пишу в UE, но нередко обращаюсь к SciTE, когда вопрос по чистому LUA) Добавлено через 9 мин.: Gun12: Кстати выполнять выделенную часть кода можно комбинацией "Ctrl + 4" А вот если пояснишь, как это реализовать для скриптов игры (*.script) - был бы благодарен, т.к. как-то не нашел времени раскопать это ... А выдирать нужные фрагменты в отдельные фалы - утомительно порою. Изменено 21 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Сентября 2011 (изменено) Gun12, именно про это и говорю, но ... при моих настройках, в которых не правил данный момент отсутствует возможность выполнять выбранный фрагмент кода. Вероятно это настраивается тут: # Команда- Выполнить выделенный тект (Ctrl+4) command.name.4.$(file.patterns.lua)=Execute Selection command.4.$(file.patterns.lua)=dostring $(CurrentSelection) command.mode.4.$(file.patterns.lua)=subsystem:lua,savebefore:no - но при дефолтых строках именно с *.script "Открываю -> выделяю -> нажимаю ->" - только панельки тоолбаров подмигивают, консоль пуста. (версия SciTE та же , что и у тебя, хотя установлена 'параллельно' исходной) P.S. Выложи, если возможно, твои настройки или, как говорил 'портированный вариант для флешки'. Изменено 21 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Сентября 2011 (изменено) (алаверды) malandrinus, Пока говорим только про SciTE Cм. вопрос: #9 ... и тишина. :-) malandrinus, Не соглашусь по обоим пунктам В моей фразе три пункта, поэтому по трем: 1. не скрипт - кроме расширения к скриптам этот файл не имеет никакого отношения. 2.1. далеко не полная - использовано относительное определение. То, что кому-то 'близко', для кого-то 'далеко'. Согласен, что 'далеко' несколько утрированно, но суть 'не полноты' передает. 2.2. 'Полнота' не обозначена чего именно, и тобою же самим делается оговорка. И даже в том смысле что "там перечислено всё, что экспортировано" - не соглашусь с определением "все". Распечатка даже некоторых методов говорит о далеко не полном перечислении их функций и пр. в этом файле. 3. устаревшая - то, что в этом файле перечислены методы, которые может быть когда-то и работали в каких-то билдах, но не работают с теми релизами, в которых этот файл присутствует - иначе не называют. Остаюсь при своем мнении, что это всего лишь напоминалка, хотя и достаточно наполненная и даже в чем-то избыточная (на мой взгяд). Для новичков в модинге, я ее называю "азбука" Изменено 21 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Сентября 2011 (изменено) Вопрос: Возможно ли по аналогии с перловыми методами из языка Perl составить патерн в Lua для парсинга по нескольким кускам слов? Т.е. имеем несколько масок: Masks = ("one", "two", "three") Возможно ли составить единый патерн, а не использовать три: if string.match(s,"one") or string.match(s,"two") or string.match(s,"three") then --/< 's' - исходная строка P.S. По стеку - ясно, что практических возможностей никаких. (хорошо, раз бразильцы-разработчики настаивают - уважим, буду употреблять Lua, хотя и не всегда соответствует многим источника, откуда черпаю ... ;-)) Изменено 21 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Сентября 2011 (изменено) malandrinus, задаю вопросы не из теоретический изысканий иль любопытства ради, а все же с учетом практического применения. Мне не столь нужны вариации, что можно сделать иначе, а результат, т.е. оптимизиция как самого кода так и/или времени (второе первично). Просто при своих познаниях и навыках могу чего-то не знать, чего-то упустить/забыть, вот и задаю вопрос к тем, кто может быть нашел/знает решение или наоборот, знает что иначе невозможно. P.S. (подумав) Хотя ... ответ то не только мне предназначен, его и другие читают. Изменено 21 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 22 Сентября 2011 (изменено) Вопрос из категории "не очевидно, но факт": Возвращаясь в вопросу по способу определения типа таблицы: список или нет (#2765) Имеем две таблицы: local t1 = {[2]="222",[3]="333",[4]="444"} и вторую получаем из сторонней: local t0 = {[1]="111",[2]="222",[3]="333",[4]="444"} --/ сторонняя таблица local t2 = {} --/ заготовка 2-ой таблицаы for idx,v in ipairs(t0) do if idx ~= 1 then --/ пропускаем 1-й индекс t2[idx] = v --/ в таблицу запоминаются 2, 3 и 4 end end Распечатка обеих таблиц одинакова (по сути, хотя и различается порядком): 2=222 3=333 4=444 Собственно вопрос(!): Чем различаются t1 и t2 ? (попробуйте найти различие/ответ не заглядывая под спойлер) Пишем код: local t1 = {[2]="222",[3]="333",[4]="444"} for idx,v in pairs(t1) do print(idx.." = "..v) end print("#t1=", #t1) local is_list1 = #t1 > 0 and not next(t1,#t1) print("is_list1=", is_list1) local t0 = {[1]="111",[2]="222",[3]="333",[4]="444"} local t2 = {} for idx,v in ipairs(t0) do if idx ~= 1 then --/ пропускаем 1-й индекс t2[idx] = v --/ в таблицу запоминаются 2, 3 и 4 end end for idx,v in pairs(t2) do print(idx.." = "..v) end print("#t2=", #t2) --/ получаем 4 - странно это local is_list2 = #t2 > 0 and not next(t2,#t2) print("is_list2=", is_list2) --/> получаем 'true' - странно, получается что это список? --/ добавляем проверку на наличие элемента с индексом 1 local is_list = #t2 > 0 and not next(t1,#t2) and next(t2) == 1 print("is_list=", is_list) --/> получаем 'false' - оказывается это НЕ индесированный список!!! Результат: 3=333 2=222 4=444 #t1= 0 is_list1= false --/ т.е. хеш-таблица 2=222 3=333 4=444 #t2= 4 --/? is_list2= true --/ это список??? is_list= false --/ все же нет! Т.о. Gun12, излишняя подстраховка next(t2) == 1 - оказалась не лишней. Но вот в чем разница ответа не имею. Кто подскажет? Заранее спасибо. Добавлено через 6 мин.: Gun12, в вопросе по патерну для нескольких строк меня интересовал единый патерн и приоритет был - оптимизиция времени. Оптимизация кода была все же вторичной задачей. Предложенный вариант однозначно проиграет по времени последовательности проверок по каждой строке. :-( Изменено 22 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 22 Сентября 2011 (изменено) Gun12 Чтобы не углубляться, в вопросе не привел все иные варианты, когда оператор # возвращает не нулевую длину для таблиц с пустыми индексами. Скорее всего это связано с тем, что luа (как заявлено) старается любыми путями до последнего сохранять таблицу именно как список а не хэш В том то и дело, и с чем и связан вопрос выше - ведь заведомо таблица создается НЕ как список, а как хеш. Ведь начальный первый индекс с самого рождения таблицы не ракен единице. Ну а для исключения любых подобных неочевидностей сделал подстраховку так: iCntIdx = #tTbl --/ длина индексированной части таблицы bList = iCntIdx > 0 and not next(tTbl,iCntIdx) --/ флаг списка (true|false) if bList then for i=1,iCntIdx do --/ цикл проверки на отсутствие 'пустых' индексов if tTbl[i] == nil then --/ пустышка? bList = false --/ не список break --/ прерываем по первой пустышке end end end А оператор # возвращает номер последнего индекса в таблице, за которым отсутствуют как минимум два следующих индекса подряд. Изменено 22 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 23 Сентября 2011 (изменено) Итого, с точки зрения гарантированности, достаточности и оптимальности по производительности (по времени) и строк кода, похоже этот вариант наилучший? function is_List(tbl) local count = #tbl --/ длина индексированной части таблицы if count > 0 then --/ есть элементы списка? --/ индексы начинаются с 1 и за 'списком' отсутствует хеш-элемент if next(tbl) == 1 and not next(tbl,count) then for i=2,count-1 do --/ цикл перепроверки: 'отсутствуют пустые элементы?' if tbl[i] == nil then --/ пустышка? return false --/> не список end end return true --/> флаг: это список! end end return false --/> не список end -и или есть недостатки, оптимальнее? Изменено 23 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 23 Сентября 2011 (изменено) malandrinus В отличии от модераторов/кураторов у обычных пользователей отсутствует возможность вносить правки (редактировать) в свои посты спустя 15 мин после его опубликования. Моя фраза была уже опровергнута постом #30 Gun12, и то, что я не отреагировал на нее в своем ответном посте - означает согласие (молчание знак согласия). Там же, в моем ответе приведен вариант, качающийся исходного вопроса с учетом всех особенностей (включая и тобою упомянутых познее) применения оператора # в общем алгоритме. Вот критика всего конечного алгоритма способа опредеоения типа таблицы была бы полезна. Вероятно не стОит задаваться вопросом "Почему два?" по поводу некорректности/неправильности фразы, как уже один раз опровергнутой. Иначе придется каждый раз писать поправки/подтверждения/опровержения ранее сказанному по каждому поводу. По редактору: В самом начале мною было высказано мнение, что каждый выбирает себе инструмент "по руке и по задаче". Можно много на эту оффтопную тему высказывать мнений и пр., но ... большей частью все это будет в пустоту, потому что каждому из нас, твоими же словами, "как-то лениво" съезжать с насиженного и переезжать на малоизвестное и пока неудобное. Единственное все же хотелось бы тут упомянуть в дополнение к перечисленному тобою "перечню жизненно важных для программисткого редактора фишек": Достоинство SciTE для данного раздела "Школа модинга" в том, что он имеет встроенный русский мануал по Lua, который неплохо иметь под рукою любому модмейкеру и то, что не теряя времени на запуски/перезапуски целевой программы (той же игры), т.е. непосредственно в редакторе и даже не записывая чего-либо на диск, можно писать и перепроверять локальные небольшие куски функций/формул, чего не дают иные редакторы. ==================================================== Добавлено через 56 мин.: Вопрос: Нет ли каких "подводных камней" или иных "недокументированных фич" в применяемых ниже операторах/методах, которые могут повлиять не корректность результата? local Number = 9 --/ 10,16,100, ... local abs = Number == math.modf(Number) and math.abs(Number) --/ модуль целочисленного числа if abs then --/ целочисленное? local n = math.floor( math.log10( abs + 1) ) --/ степень ближайшего к модулю бОльшего числа кратного 10-ти if abs >= 10^n and abs < 2^(4*n) then return true --/> кол-во разрядов 'dec' > 'hex' end end Пояснение: Требуется определить, что десятичное число (любое - целое/дробное/отриыательное) лежит в диапазонах 10...15, 100...255, 1000...4095 и т.д. т.е. тогда, когда количество разрядов десятичного значения числа 'Number' больше кол-ва разрядов этого же числа в шестнадцатиричной форме. Изменено 23 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 23 Сентября 2011 (изменено) malandrinus: А почему не хочешь сделать по-простому, с перебором диапазонов и проверкой на вхождение в них?Потому что: - этот вопрос в первую очередь преследует цель получить информацию по применяемым функциям/операторам и возможным их 'закидонам' (по аналогии с оператором #) ... - в данном случае, даже если и имеется потеря некоторой доли скорости выполнения, то а) вычисления в игре производятся в моменты (в основном создание сэйвов), когда лишняя пара милисекунд не важны, и б) компактность кода и универсальность формул также немаловажные критерии. Учитывая диапазон разрядов применяемых в игре (float -> 10 разрядов), то использование проверок 2х9-ти диапазонов (считая отрицательные) далеко не всегда будет выигрышным по скорости; Вот это полная функция: --/ конвертер числа (number) в 'dec' или 'hex' строку (string) function Num2Str(iNum) --/< любое число, применяемое в игре Сталкер if iNum == math.modf(iNum) then --/ целочисленное? local iAbs = math.abs(iNum) --/ модуль числа if iAbs > 9 then --/ число 2-x и более разрядное? --/ степень ближайшего к модулю бОльшего числа кратного 10-ти local iLv = math.floor(math.log10(iAbs+1)) if iAbs >= 10^iLv and iAbs < 2^(4*iLv) then --/ разрядность 'dec' > 'hex'? if iNum >= 0 then --/ не отрицательное? return string.format('%X', iAbs) --/> 'hex'-строка end --/ отрицательное - упаковываем со знаком return "-" .. string.format('%X', iAbs) --/> 'hex'-строка со знаком end end end return tostring(iNum) --/> 'dec'-строка end malandrinus: ... себе давно наладил тестовый полигон для запуска скриптов в игре без перезгарузки1. Что мешает поделиться опытом, а не поверхностной 'частной' информацией? ;-) 2. Ну ... это как бы: malandrinus: ...сказал, что буду тереть бессмысленный флуд. То, что навалили здесь в самом начале, не содержало ни грамма информации, сплошное меряние ... редакторами.И хотя ранее никакого меряния ... редакторами не было (ИМХО), а некоторая дискуссия о предпочтениях и ненавязывании ... Предлагаешь померять(ся) ... чей/какой способ отладки 'на лету' лучшЕЕ? ;-). Повторю: "на вкус и цвет ..." и "инструмент выбирается по руке и по задаче". Могу привести немало примеров, когда отладка "из игры" невозможна и ... не даром упомянул о 'целевой программе'? которая может быть любой, а не только игрою Сталкер. Да и 90% читателей/модмейкеров портала не имеют ни того и ни другого. Скачав и установив оедактор (SciTE) - любой получает возможность писать и проверять коды "на лету" за минуты. 3. Полностью согласен, что запуск 'из игры' имеет свою ценность, о чем и говорил ранее в "бессмысленном флуде". 4. В данном топике мы говорим с ориентиром на Lua, а игра вторична или ... наоборот? ;-) Изменено 23 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 23 Сентября 2011 (изменено) malandrinus По п.4: Это все же НЕ ответ координатора (ИМХО). - или Lua первичен, остальное вторично - в этом случае дефолтно в вопросах/ответах подразумеваются общее именно для Lua контента и уже потом для конкретной частности; - или Игра первична - в этом случае вопрос/ответ подразумевает в первую очередь именно контент и особенности Игры, а не всевозможности/извраты/недоступности всего/чистого Lua; - или 'демократия' - каждый говорит о своем, кто про чистый Lua, кто про портированный кусок квази-Lua в Игре, кто просто про игру ... Каков ответ координатора о первичности? :-) По полигону: Для Игры (и то с тобою же высказанными ограничениями и частностями) вариант вполне возможен, но ... для вероятно 70% игроков/модмейкеров неприемлем/затруднителен (ЧН/ЗП, нет второго компа, коды именно 3-й категории ошибок, ...). Для изучения Lua - практически не пригоден (более схожь с "чесать ногою ухо"). ИМХО. Пока Gun12 не написал введение по SciTE, отвечу для тех, кто пока не понял: - Писать код (print('Hello Word')) нужно конечно же в документе. Однако, дефолтно открытое первичное окно ("Безымянный") не имеет расширения, что не дает интерпретировать какие команды допустимы ... Следует этот дефолтно открытый документ сохранить с требуемым расширением и тогда - доступны все свойства данного документа/файла. - Консоль открывается автоматически при наличии информации предназначенной для вывода из кодов в консоль. Добавлено через 27 мин.: malandrinus Из твоего описания "полигона": Путём экспериментов я выяснил, что текущим каталогом Lua считает папку bin Вероятно да. Однако, в том же NLC6 (использован Lua-перехватчик by aplet), фокус передается в корень игровой папки (т.е. на верхний уровень относительно 'bin'). Вопросы: (касательно Lua в игре Сталкер) Есть ли способ определить текущий каталог для Lua однозначно и быстро, т.е. не экспериментально? Есть ли способ изменять текущий каталог Lua и/или хотя бы организовать типа удобного линка на него? Изменено 23 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 24 Сентября 2011 (изменено) (по следам ранее опудликованного) "Упаковка таблицы в строку (стринг) и обратная распаковка" Доработанный вариант с возможностью проверки в SciTE: --[[ 1. При упаковке таблиц типа 'список': {v1,v2,v3,v4} в пак-строку для каждого элемента записывается только однобайтный признак, и опускаются сами индексы. Экономия: Общее кол-во байт всех индексов таблицы, каждый разряд которых 1 байт. Т.е. для списка в 100 элементов экономия: 1х9 + 2х90 + 3х1 - 1 = 191 байт. 2. При упаковке целочисленных чисел разрядность которых более разрядности этого же числа в 16-ти ричной форме, число упаковывается не как десятичная строка, а как хекс-строка. Пример: 12345 == 0х3039 => "12345" ~= "3039" и экономится как минимум 1 байт на каждом числе. При сохранении чисел большой разрадности (до 14) - экономия может составлять 3 байта на каждое число. 3. Таблицы должны содержать только элементы типов: строка, число, булево значение, субтаблицы и nil. Элементы типов: юзердата, функция, поток - запрещены для упаковки. 4. Исправлена ошибка AMK-варианта при упаковке таблиц с булевыми значениями в полях. 5. Совместимость со скриптами и сэйвами, использовавшими упаковку/распаковку из amk.script. Обратная совместимость отсутствует. Пример практичеккого применения: В игре в таблицу запоминаются игровые идентификаторы (ID) объектов. Для 198 объектов: Паковка таблицы амк-вариантом: 1925 байт Паковка таблицы 'экономным': 1056 байт --]] --/------------------------------------------------------------------ --/#!# Для отладки в SciTE (в кодах игры УДАЛИТЬ!) --/------------------------------------------------------------------ --/ пакуемая таблица local t0 = { ["a"]=true, ["b"]=false, ["c"]=3, ["d"]=-111 } --local t0 = {"a","b","c","d","e"} print("Count0=", #t0, ", First=", next(t0)) --/ создаем синонимы функций local printf = function(...) print(...) end local abort = function(...) print("ERROR:", ...) end --/------------------------------------------------------------------ --/ Упаковка таблицы в строку (стринг) и обратная распаковка --/------------------------------------------------------------------ --/ Внимание! Строки в структуре таблицы не должны содержать символов с кодами 0-31. --[[-------------------------------------------------------- Формат упаковки: table ::= ( listtable | hashtable ) subtable ::= ( listtable | hashtable ) listtable ::= 0x6 valuetype ( value | subtable 0x5 ) hashtable ::= keytype key valuetype ( value | subtable 0x5 ) keytype ::= ( 0x1 | 0x2 | 0x7 ) valuetype ::= ( 0x1 | 0x2 | 0x3 | 0x4 | 0x7 ) --]]-------------------------------------------------------- local tPackChar = { --/ служебная таблица маркеров упаковки dec = {string.char(1), 1}, --/ 0x1 (SOH) - 'number' (dec) str = {string.char(2), 2}, --/ 0x2 (STX) - 'string' bln = {string.char(3), 3}, --/ 0x3 (ETX) - 'boolean' tbl = {string.char(4), 4}, --/ 0x4 (EOT) - 'table' tbe = {string.char(5), 5}, --/ 0x5 (ENQ) - table-end tbi = {string.char(6), 6}, --/ 0x6 (ACK) - table-list hex = {string.char(7), 7} --/ 0x7 (BEL) - number-hex } --/-------------------------------------------------------- --/ функции для совместимости с сэйвами на базе amk.script --/-------------------------------------------------------- --/ упаковка таблицы в строку (стринг) function pack_array_to_string(tTbl,bList) printf("pack_array_to_string:>") return Pack_Tbl(tTbl,bList) --/> String end --/ распаковка 'упакованной' строки (стринга) в таблицу function unpack_array_from_string(sStr) if sStr and sStr ~= '' then --/ amk-format упаковки был тэгирован символом c кодом 1 (0x1) -- if sStr:sub(1,1) == string.char(1) then --/ amk-format? if (sStr:sub(2,2) == tPackChar.dec[1] or sStr:sub(2,2) == tPackChar.str[1]) then --/ со 2-го символа начинается упакованная строка sStr = sStr:sub(2,-1) --/ отрезаем 1-й символ end end --/< ------------------------------------------------------------ return Parse_Str(sStr) or {} --/> Table end return {} --/> Zero-Table end --/-------------------------------------------------------- --/ упаковка таблицы (списка) 'tTbl' в строку 'sStr' --/-------------------------------------------------------- function Pack_Tbl(tTbl,bList) --/< table [,(nil|true|false)] if type(tTbl) ~= 'table' then printf("Pack_Tbl:Not_Table=["..type(tTbl).."]:<Warning!>") return "" --/> zero-string (не таблица!) elseif not next(tTbl) then --/ --/>? (отсутствует 1-й элемент таблицы) return "" --/> zero-string (таблица пуста) end --/ локальная функция: проверка типа таблицы:'список' или нет? function is_List(tTbl) local iCntIdx = #tTbl --/ длина индексированной части таблицы if iCntIdx > 0 then --/ есть элементы списка? --/ индексы начинаются с 1 и за 'списком' отсутствует хеш-элемент if next(tTbl) == 1 and not next(tTbl,iCntIdx) then for i=2,iCntIdx-1 do --/ цикл перепроверки: 'отсутствуют пустые элементы?' if tTbl[i] == nil then --/ пустышка? return false --/> не список end end bList = true --/ флаг: это список! end end return bList --/> флаг: список или ... (true|nil) end --/ локальная функция: упаковка (+конвертер) числа (number) в 'dec' или 'hex' строку (string) function Pack_Num2Str(iNum) if iNum == math.modf(iNum) then --/ целочисленное? local iAbs = math.abs(iNum) --/ модуль числа if iAbs > 9 then --/ число 2-x и более разрядное? local iLv = math.floor(math.log10(iAbs+1)) --/ степень ближайшего к модулю бОльшего числа кратного 10-ти if iAbs >= 10^iLv and iAbs < 2^(4*iLv) then --/ разрядность 'dec' > 'hex'? if iNum >= 0 then --/ не отрицательное? return tPackChar.hex[1] .. string.format('%X', iAbs) --/> 'hex'-строка end --/ отрицательное - упаковываем со знаком return tPackChar.hex[1] .. "-" .. string.format('%X', iAbs) --/> 'hex'-строка со знаком end end end return tPackChar.dec[1] .. iNum --/> 'dec'-строка end --/ локальная функция: упаковка ключа (индекса) элемента таблицы function Pack_Key(key) local sType = type(key) if sType == 'number' then return Pack_Num2Str(key) --/> 'dec|hex'-строка elseif sType == 'string' then return tPackChar.str[1] .. key --/> строка end abort("Pack_Tbl:UnSupported_KeyType=" .. sType) end --/ локальная функция: упаковка значения элемента таблицы function Pack_Value(value) local sType = type(value) if sType == 'number' then return Pack_Num2Str(value) --/> 'dec|hex'-строка elseif sType == 'string' then return tPackChar.str[1] .. value --/> строка elseif sType == 'boolean' then return tPackChar.bln[1] .. ((value and "1") or "0") --/> 'bool'-строка ('0'|'1') elseif sType == 'table' then return tPackChar.tbl[1] .. this.Pack_Tbl(value) .. tPackChar.tbe[1] --/> рекурсивный вызов end abort("Pack_Tbl:UnSupported_ValueType=" .. sType) end --/ ------------------------------------------- --/ основное тело функции: all pack local tPackStr = {} --/ временная таблица для частей общей строки if bList or is_List(tTbl) then --/ упаковывается список? table.insert(tPackStr, tPackChar.tbi[1]) --/< маркер 'list' (список) for i=1,#tTbl do table.insert(tPackStr, Pack_Value(tTbl[i])) end else --/ полная упаковка (ключ и значение) for k,v in pairs(tTbl) do table.insert(tPackStr, Pack_Key(k)) table.insert(tPackStr, Pack_Value(v)) end end return table.concat(tPackStr),bList --/> строка [и флаг 'список'] end --/-------------------------------------------------------- --/ распаковка строки 'sStr' (или части строки от 'at') в таблицу 'tTbl' --/-------------------------------------------------------- function Parse_Str(sStr,at) local tTbl,iLen = {},sStr:len() --/ заготовка таблицы и длина строки if not at then at = 1 end --/ по умолчанию: парсинг с начала строки local key,value,iByte,bList = nil,nil,nil,nil --/ ------------------------------------------- iByte,at = Get_Byte(sStr,at) --/ код at-го символа в строке --/ проверка: упакована таблица типа 'список' (table-list)? if iByte == tPackChar.tbi[2] then --/ 'список'? bList = true --/ флаг: 'начало списка' (table-list) iByte,at = Get_Byte(sStr,at)--/ пропускаем маркер списка и переходим к следующему символу end --/ цикл парсинга строки с at-го символа while at <= iLen do --/ (суб)строка не закончилась? if iByte == tPackChar.tbe[2] then --/ проверка: маркер конца субтаблицы? return tTbl,at --/> субстрока субтаблицы закончилась - выход из функции end if not bList then --/ режим 'общей' таблицы (не 'список')? --/ парсинг 'key' if iByte == tPackChar.str[2] then --/ 'string'? key,at = Get_Str(sStr,at) elseif iByte == tPackChar.dec[2] then --/ 'number' (dec)? key,at = Get_Num(sStr,at) elseif iByte == tPackChar.hex[2] then --/ 'number-hex'? key,at = Get_Num(sStr,at,true) --/< 'true' - флаг распаковка 'hex'-строки else --/ ошибка формата упаковки abort("Parse_Str:(%s):UnSupported_TypeKey=" .. tostring(iByte)) end iByte,at = Get_Byte(sStr,at) --/ код следующего символа строки end --/ парсинг 'value' if iByte == tPackChar.dec[2] then --/ 'number' (dec)? value,at = Get_Num(sStr,at) elseif iByte == tPackChar.hex[2] then --/ 'number-hex'? value,at = Get_Num(sStr,at,true) --/< 'true' - флаг распаковка 'hex'-строки elseif iByte == tPackChar.str[2] then --/ 'string'? value,at = Get_Str(sStr,at) elseif iByte == tPackChar.bln[2] then --/ 'boolean'? value,at = Get_Bool(sStr,at) elseif iByte == tPackChar.tbl[2] then --/ 'table'? value,at = Parse_Str(sStr,at) --/> рекурсивный вызов для 'табличных субстрок' else --/ ошибка формата упаковки abort("Parse_Str:(%s):UnSupported_TypeValue=" .. tostring(iByte)) end --/ запоминаем элемент в таблицу if bList then --/ элемент списка? table.insert(tTbl, value) --/ добавляем в таблицу типа 'список' (table-list) else --/ элемент 'общей' таблицы tTbl[key] = value end iByte,at = Get_Byte(sStr,at) --/ код следующего символа строки end return tTbl,at --/> end --/ получение кода at-го символа строки и индекса следующего за ним символа function Get_Byte(sStr,at) return string.byte(sStr:sub(at,at)), at+1 --/> end --/ по-символьный парсер строки 'sStr' от at до 1-го 'управляющего' символа function Get_Str(sStr,at) --/< стринг(строка) и начальный индекс в нем local iSym,iLen = at,sStr:len() --/ индекс конца стринга(строки) for i=at,iLen do local iByte = string.byte(sStr:sub(i,i)) --/ код i-го символа строки if iByte < 32 then --/ 'управляющий' символ? if iByte > 7 or i == at then --/ 'управляющий' символ запрещенный или 1-й? abort("Get_Str:Sym(" .. i .. ")=" .. iByte) end --/ начальная часть (суб)строки и индекс 1-го упр.символа 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,bHex) local sSubStr,iNext = Get_Str(sStr,at) local iNum = nil if bHex then --/ распаковка хекс-строки? if sSubStr:sub(1,1) == "-" then --/ отрицательное число? - отрезаем 1-й символ iNum = tonumber(sSubStr:sub(2,-1),16) *(-1) --/ перевод 'hex'-строки в отрицаткельное десятичное число else iNum = tonumber(sSubStr,16) --/ перевод 'hex'-строки в десятичное число end else iNum = tonumber(sSubStr) --/ перевод 'dec'-строки в десятичное число end if iNum then --/ есть число? return iNum, iNext --/> elseif sSubStr == "" then --/ пустая субстрока? printf("Get_Num:Zero:Str=" .. tostring(sStr)) return 0, iNext --/> #?# пока не будем прерывать else --/ ошибка формата упаковки abort("Get_Num: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*)') or '') == 'true'), iNext --/> end --/ перевод (части) строки 'sStr' в булево значение (true|false) function ToBoolean(sStr) return (sStr == "1" or string.lower(sStr:match('^%s*(%S*)') or '') == 'true') --/> true|false end --/------------------------------------------------------------------ --/#!# Для отладка в SciTE (в кодах игры УДАЛИТЬ!) --/------------------------------------------------------------------ local s = pack_array_to_string(t0) print("PackString:", s) local t1 = unpack_array_from_string(s) print("Count1=", #t1, ", First=", next(t1)) for k,v in pairs(t1) do print(k, "=", v) end --/------------------------------------------------------------------ Примечание: Для отладки в SciTE - вставить код в новый открытый документ, задать имя и расширение (например, my_pack.script) и ... , задав в начале кодов требуемые/желаемые значения для таблицы 't0' - проверять результаты (F5). Добавлено через 19 мин.: malandrinus Исходный вариант (АМК) паковал все числа в строку используя tostring. Смотрим: если число равно 10 -> в строку пишется два символа (байта), для 16 -> 2 байта, для 100 -> 3 байта. При использовании конвертера в хекс-строку имеем: 10 -> 0xA -> 1 байт, 16 -> 0x10 -> 2 байта, 100 -> 0x64 -> 2 байта. Т.о. в указанных ранее диапазонах (10...15,100...255,1000...4095, и далее), т.е. в тех, когда количество разрядов числа в десятичной форме превышает кол-во разрядов в 16-ти ричной форме - конвертация позволяет экономить на каждом таком числе по байту или более. Т.е. кол-фо знаков в суммарной строке упакованной таблицы уменьшается. Для сохранений в pstor объектов в игре это важно. Добавлено через 16 мин.: Поправил пару ошибок в комплекте ... (тяжко все же коды вставлять в парсер форума) Изменено 24 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 27 Сентября 2011 (изменено) Zander_driver 1. Плз, длинные портянки кодов стОит прятать под спойлеры (ИМХО). 2. Zander_driver: Может быть, это будет из разряда "Капитан очевидность рекомендует", но я своих методов в других модах пока не встретил, так что думаю начинающим скриптерам будет полезно. То, что для одних является откровением, для других - пройденым этапом, а для третьих - анахронизмом. Именно для того, чтобы и первым и вторым было чему поучиться, да и третьим встряхнуть извилины и порой сбросить шоры - и предназначен (ИМХО) этот топик. Так что полезным может быть любой совет, лишь бы ... он не преподносился как 'истина в последней инстанции'. Твой вариант конечно может принести пользу многим, кто и этого не знал. Однако от очевидностей он еще довольно далек. Очевидно, что некое множество кратковременных событий ты разбиваешь на более крупные и группируешь, что конечно же сэкономит во многих случаях ресурсы игры, но ... сам же оставляешь явно 'последовательный' подход. Взгляни: function actor_update(actor, delta) --- Действия, которые надо производить с частотой fps time_100 = time_100 + delta if time_100 > 100 then --- Действия, которые надо производить с частотой раз в 100мс time_300 = time_300 + time_100 if time_300 > 300 then --- Действия, которые надо производить с частотой раз в 300мс time_1200 = time_1200 + time_300 if time_1200 > 1200 then --- Действия, которые надо производить с частотой раз в 1200мс time_1200 = 0 end time_300 = 0 end time_5000 = time_5000 + time_100 if time_5000 > 5000 then --- Действия, которые надо производить с частотой раз в 5 секунд time_5000 = 0 end time_100 = 0 end end Сгруппировав потребные таймеры по вхождению друг в друга, взаимопоглощается еще немало операций по сложению и сравнению. Ведь нет смысла складывать те же 300 ms из дельт по ~20 ms, когда они все одно будут равны 100х3. Ни у и т.д. Этот же вариант в переложении на более универсальный по применению, т.е. там где нет акторской 'delta' будет выглядеть так: function any_update() --- Действия, которые надо производить с частотой fps local time = time_global() --/ текущее время if time_100 < time then time_100 = time + 100 --- Действия, которые надо производить с частотой раз в 100мс if time_300 < time then time_300 = time + 300 --- Действия, которые надо производить с частотой раз в 300мс if time_1200 < time then time_1200 = time + 1200 --- Действия, которые надо производить с частотой раз в 1200мс end end end if time_5000 < time then time_5000 = time + 5000 --- Действия, которые надо производить с частотой раз в 5 секунд end end Вставляй любой кусок в любое место нужной функции и используй. Ведь далеко не только апдейтер актора влияет на FPS игры и появление лагов. По сути 2-ой вариант и применяется во многих модах и развитие того, что есть и в оригинальной игре. Так что, кому-то приглянется твой вариант, кому-то иной, а кто-то давно третьим пользуется. А один из основных недостатков и твой и 1-й вариант не решают. Все одно все 'тяжелые' скрипты/алгоритмы будут выполняться единомоментно - а значит будут лаги. Т.о. требуется не только оптимизировать ресурсоемкость/производительность указанной линейки таймеров, а и разносить тяжелые операции по времени, т.е. по нескольким (ассинхронным) таймерам. По более оптимальным и 'правильным' вариантам можно поговорить когда интерес и потребность к ним возникнет. Ну а на твой 'p.s.' по 1-м страницам топика - могу дать пару ремарок: 1. А что тебе мешало по интересующим тебя вопросам начать разговор на тех же 1-х страничках? Места пока в топике предостаточно. :-) Замечу, (ИМХО) твой пост к Lua никакого отношения не имеет (и более уместен где-то в "Скриптовании ..."), по сравнеию с тем, что ранее обсуждался редактор заточенный именно под Lua. 2. В первом классе вначале учатся ручку держать, а не сразу писать в тетрадке. Без 'инструмента' в руке и без навыков работы с ним - писать то не скоро можно научиться ... От используемого редактора зависит как производительность программиста, так и собственно его уровень и навыки. Одному вполне достаточно нотепада, второму его же с плюсиками, а кому то и навороченных в его делах уже порой не хватает. И разговор был именно с целью дать совет посмотреть и ... может тот же кодер и время начнет себе экономить и даже перейдет на следующий уровень. 3. То что интересно тебе, не обязательно интересно другому. Важно чтобы ни ты ни он не мешали друг другу, и не говорили только для себя. Добавлено через 14 мин.: Gun12, аналогично. :-) Почему-то многие боятся задавать вопросы, просить помощи в топиках форума ... И это скорее не стеснение, а боязнь выглядеть в глазах многих иль совсем уж 'чайником', или не таким уж матерым кодером ... Все мы в чем-то чайники и далеко не во всем 'матерые'! На то и раздел - "Школа моддинга" Изменено 27 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 27 Сентября 2011 (изменено) Zander_driver: Ну насчет того, что мой пост был бы уместнее "Скриптовании", поспорю Отлично, но в споре приводят аргументы, а не отвлеченные рассуждения субъективного характера. Твои же слова из того же поста: "Да, к Lua это отношения не имеет никакого. Я применял точно такой же прием, когда писал код в Delphi." - сводят на нет твои же "аргументы" и только подтверждают вышесказанное. Не следует тут путать вопросы по написанию, организации, оптимизации алгоритмов, которые чаще всего никак не увязаны с используемыми языками. Это тоже конечно интересная для многих тема, но ни как не именно для Lua. А вот "Тема для обсуждения скриптов, спавна и логики всего и всех в серии игр STALKER." - как раз и подходит и для обсуждения алгоритмов, т.к. 'скрипт' - это в первую очередь некий алгоритм. Ну а то, что там общение ведется в основном в режиме 'вопрос-ответ' - так это всего лишь желание тех, кого интересуют ответы, и леность тех, кто мог бы поделиться своими соображениями/наработками и дать советы, но ... не делает этого. По сути твоих рассуждений по таймерам: Не хочу обидеть, конечно, но ... давая советы 'начинающим скриптерам', ты сам недалеко от них отошел и набрав немного навыков/опыта, так и не снял шоры на многое. Zander_driver: Вариант "елочка" довольно интересен, но, с другой стороны, когда допустим будут выполнены действия 1200 мс, то и 300мс, и 100мс будут выполнены в тот же такт. А это уже может привести к изрядному подвисанию, если там что-то тяжелое. Несколько независимых таймеров можно подстроить так, что их частоты не будут кратны друг другу. Например, 100мс, 260, 1170, и т.д.. нагрузка на цп будет распределяться равномернее. Хорошо, что все же заметил этот недостаток, хотя при более внимательном чтении о нем же уже было сказано: Artos: А один из основных недостатков и твой и 1-й вариант не решают. Все одно все 'тяжелые' скрипты/алгоритмы будут выполняться единомоментно - а значит будут лаги. Т.о. требуется не только оптимизировать ресурсоемкость/производительность указанной линейки таймеров, а и разносить тяжелые операции по времени, т.е. по нескольким (ассинхронным) таймерам. Жаль не заметил слов 'и твой', читать нужно внимательнее. Все твои таймеры имеют одну и ту же стартовую установку (0) Это означает, что ВСЕ они работают СИНХРОННО, и когда сработает таймер 1200, в это же время сработает и таймер 300 и таймер 100. Сделав вроде как несколько "независимых" таймеров - по сути ты просто продублировал один и тот же, только задав различные периоды. И в любом случае при кратности периодов - таймеры будут запускать ведомые функции единовременно. Заинтересовавший тебя вариант "елочка" НИЧЕМ не отличается от твоего же, только оптимизирован с целью исключения лишник операция по сложению и сравнению. Твой вариант точно также будет запускать все вызовы так же как и "елочка", т.е. единовременно. Итого, если хочешь получить независимые (ассинхронные) таймеры следует сделать что-то типа: local time_1000,time_5000 function any_update() local time = time_global() --/ текущее время if not time_1000 then --/ предустановка таймеров time_1000 = time + 1000 time_5000 = time + 5000 + time_1000/2 --time_5000 = time + 5000 + matc.random(-time_1000/4,time_1000/4) --/ или можно иначе end if time_1000 < time then time_100 = time + 1000 --- Действия, которые надо производить с частотой раз в 1 сек end if time_5000 < time then time_5000 = time + 5000 --- Действия, которые надо производить с частотой раз в 5 сек end end Вот тогда таймеры будут и разнесены (в примере как минимум на 0.5сек) и независимы (Приношу извинения куратору темы за этот оффтопик, но ... так и не понял до сих пор, о чем же тут стОит гутарить, а чего следует избегать.) Изменено 27 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 27 Сентября 2011 (изменено) Все дейстия можно разделить на группы: а) - постоянно требующие проверок условий/вызовов на всем протяжении игры (например, проверка здоровья/хита/...); б) - постоянно требующие проверок условий/вызовов на протяжении игры, но на конкретной локации (группе локаций); в) - требующие непрерывных проверок условий/вызовов до или после некоего события (например инфопоршня); г) - требующие разовой проверки условий/вызовов после некоего события и до другого события; ... Если на апдейты вешать вызовы только тогда, когда это требуется и отключать их, когда в них отпала нужда - бОльшая часть очередей рассосется и 'не активное' не будет вообще как-либо нагружать игру и съедать FPS'ы. :-) Изменено 27 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 29 Сентября 2011 (изменено) Предлагаю обсудить вариант безопасного, информативного и производительного вывода информации в лог, с учетом особенностей игры. Известно, что разработчики по сути кастрировали вывод информационных сообщений из скриптов. Разработка, отладка, поиск ошибок без подобной информации затруднителен. Начинающие модмейкеры порой и не догадываются о полезности вывода в лог удобных сообщений при модификациях кодов. Простейшие варианты грешат тем, что не учитываются возможные ошибки при выводе в лог и подобные функции просто 'глушат', дабы из-за них не вылетала игра. Итак критерии: 1. - надежность (безопасность) кода, 2. - информативность и 3. - производительность. 1. Необходимо, чтобы ни при каких вариациях со значениями параметров, передаваемых в функцию вывода в лог, не возникали бы фатальные ошибки; 2. По-возможности, вся информация, которую хотел бы получить кодер при выводе в лог, должна выводиться в лог. При непредусмотренных ситуациях (отсутствие аргументов, непредусмотренные значения и т.п.) - в лог должна выводиться информация, помогающая понять ошибку/причину. 3. Вывод в лог и получение его в лог-файле должен по-возможности не приводить к значительным лагам в игре. Предлагаю рабочий вариант комплекта функций для вывода в лог (лог-файл): --/------------------------------------------------------------------ --/ Functions for Print-Log --/------------------------------------------------------------------ local console --/ переменная, кеширующая функцию консоли --/---------------------------------------------- --/ вывод в буфер лога --/ незначительно нагружает игру, но при фатальных ошибках информация из буфера лога может быть утеряна --/---------------------------------------------- function printf(fmt,...) if not console then console = get_console() end if next({...}) then fmt = string.sub(this.FormatToString(fmt,...),1,250) console:execute("load :>"..fmt) else console:execute("load :>"..tostring(fmt)) end end --/---------------------------------------------- --/ вывод в лог-файл --/ сохраняет каждую строку в лог-файл в реальном времени, но в циклах и апдейтах может значително тормозить игру. --/---------------------------------------------- function print_f(...) printf(...) console:execute("flush") --/ команда записи буфера лога в файл end --/---------------------------------------------- --/ конвертер строки для 'printf' --/---------------------------------------------- function FormatToString(fmt,...) --/ локальная функция: перевод аргумента в стринг local to_str = function(val,typ) if typ == 'boolean' then return tostring(a) --/> elseif typ == 'table' or typ == 'userdata' then if tonumber(val.x) and tonumber(val.y) and tonumber(val.z) then if typ == 'table' then return string.format('tab{x=%.3f:y=%.3f:z=%.3f}',val.x,val.y,val.z) --/> end return string.format('vec(x=%.3f:y=%.3f:z=%.3f)',val.x,val.y,val.z) --/> end elseif typ == 'number' then return tostring(val) --/> elseif typ == 'string' then return val --/> end return "<"..typ..">" --/> end --/ основное тело функции: парсинг исходной строки if type(fmt) == 'string' then if fmt:match('%\%[s|d]') then --/ есть патерн(ы) if next({...}) then --/ есть аргумент(ы)? local arg,val,typ,i = {...},nil,nil,nil for i=1,#arg do val = arg[i] --/ значение текущего аргумента typ = type(val) --/ тип текущего аргумента if typ == 'string' then fmt = fmt:gsub('%\%s',val,1) elseif typ == 'number' then if fmt:match('%\%d') then fmt = fmt:gsub('%\%d',val,1) else fmt = fmt:gsub('%\%s',val,1) end else fmt = fmt:gsub('%\%s',to_str(val,typ),1) end end end fmt = fmt:gsub('%\%[s|d]',"<NOT_arg!>") --/ заглушка от отсутствия аргументов end else fmt = to_str(fmt,type(fmt)) end --/ с заменой обычных пробелов (sym_space='\032') на печатные ('\160') return fmt:gsub('%s','\160') --/> end --/------------------------------------------------------------------ --/ Abort (функция принудительного прерывания игры) --/------------------------------------------------------------------ function abort(fmt, ...) local sReason = this.FormatToString(fmt or "<>",...) or '<Not_reason>' assert("ERROR:" .. sReason) if not console then console = get_console() end console:execute(string.rep("~",96)) console:execute("load :>ВНИМАНИЕ!_Ошибка!_Ниже_строка_с_информацией_по_ошибке!") console:execute("load :>ОШИБКА:"..string.gsub(sReason,"%s",'\160'),1,500) console:execute("load :>ВНИМАНИЕ!_Игра_прервана!") console:execute("load :>"..string.rep("~",96)) console:execute("flush") exit() --/> прерываем игру при фатальных ошибках end --/------------------------------------------------------------------ Замечания, предложения, критика и оптимизиция - приветствуются. :-) Изменено 29 Сентября 2011 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 30 Сентября 2011 (изменено) Zander_driver Начну с конца, хоть это не совсем оффтопик, т.к. касается любой темы: Zander_driver: Artos, помоему вы очень любите спорить Хотя я и сам такой... Скажем так: "Я не любитель лишь бы спорить, но по делу - не прочь и шпаги скрестить." Есть избитое выражение: "В споре рождается истина". И я полностью согласен с этим, если не называть спором - базар/флуд или переливание из пустого в порожнее. Есть разные формы общения: монологи, диалоги/дискуссии, споры ... Если первые подразумевают информирование, обмен мнениями, то последнее - защиту своего мнения/выбора/решения. Зачастую, те, кто не сам нашел решение, а взял на веру готовое - и не спорят ни о чем. Тот, кто потратил время,силы и нашел решение - конечно же будет его защищать. Иное дело, когда продолжают защищать не общую суть, а некую частность, мелочь, хотя и понимая, что решение по сравнению с другим проигрышно или слишком частно. Вернемся к нашим баранам, то бишь таймерам. Никакого спора с моей стороны не было и нет. Какова цель твоего поста? Я, воспринимаю ее в том чтобы поделиться найденым тобою решением с теми, у кого есть (хотя они могут и не подозревать) в этом надобность. Если же цель типа: а вот это я сделал, оцените меня - то нет смысла далее о чем-то говорить. Увидев в твоем предложенном решении и пользу, но и недостатки - об этом и решил подискутировать, указав тебе на недостатки. Конечно же раписывать 'все и вся' в подробностях и нюансах без нужды никто из нас не затрудняет. От того и недопонималки. Ну а посты 'через день на третий' тем более не способствуют диалогу. Не стану сейчас вдаваться в подсчеты цифр и пр. частности. Признаю, что при определенных условиях таймеры в твоем решении работают ассинхронно, и т.п. И в то же время не собираюсь начинать обсуждать то, что никакого отношения к таймерам они не имеют, точнее если и имеют, то только работая по неким интервалам времени, никак не соответствующим тобою же заявляемым 100мсек, 1сек, 5сек и т.п. Итак, что же потребно тем, у кого есть необходимость в таймерах. Что должо решить предлагаемое? Каковы критерии? 1. Минимизация вызовов внешних функций в единицу времени, дабы не расходовать лишние ресурсы игры; 2. Минимизация единомоментных вызовов внешних функций, дабы исбегать возникновения лагов в игре; 3. По-возможности, независимость от 'субъктивных' причин: слабый комп, маловато памяти, высокие/низкие настройки в игре, ... ; 4. Универсальность реализации/применения в различных скриптах/кодах. Твой вариант "таймеров" частично решает только первую часть задачи. Выполнение 2-го критерия полно услловностей и никак не исключает того, что в какой-то момент времени все функции будут вызваны единомоментно. Ну о 3 и 4 можно и не говорить, т.к. завязка периодов целиком завязана на собственно 'мощности компа' и кроме как в методах апдейта подобное не применить. Дав вариант решения "вариант универсальный:" и чут позже "ассинхронные таймеры" - я, например и не вижу далее обсуждать, и тем более спорить, о прежнем. Или тебе хочется поспорить что хужЕе твой вариант или "елочка"? ;-) Ну и о полезном. Выше malandrinus и я дали тезисно основные ключевые моменты для таймеров, предназначенных для решения сделующих задач (напомню): 1. Минимизация вызовов внешних функций в единицу времени, дабы не расходовать лишние ресурсы игры; 2. Минимизация единомоментных вызовов внешних функций, дабы исбегать возникновения лагов в игре; 3. По-возможности, независимость от 'субъктивных' причин: слабый комп, маловато памяти, высокие/низкие настройки в игре, ... ; 4. Универсальность реализации/применения в различных скриптах/кодах. Вот вполне рабочий вариант, который конечно же является только частным случаем реализации: local timer,counter = 0,0 --/ таймер и счетчик периодов function any_update() local time = time_global() --/ текущее время if timer < time then timer = time + 100 --/ 'тикает' таймер (можно задать и иное) if counter >= 10 then counter = 1 --/ сброс счетчика периодов else counter = counter +1 --/ следующий период end --/ Действия, которые надо производить с 1 раз в 0.1 сек (100ms) if counter == 1 then --/ Действие 1, выполняющиеся 1 раз в сек (1s) elseif counter == 3 then --/ Действие 2, выполняющиеся 1 раз в сек (1s) elseif counter == 5 then --/ Действие 3, выполняющиеся 1 раз в сек (1s) elseif counter == 7 then --/ Действие 4, выполняющиеся 1 раз в сек (1s) elseif counter == 9 then --/ Действие 5, выполняющиеся 1 раз в сек (1s) else --/ Действия-02, выполняющиеся 5 раз в сек (200ms) if counter == 3 or counter == 6 or counter == 9 then --/ Действия-03, выполняющиеся 3 раза в сек (333ms) if counter ~= 6 then --/ Действия-05, выполняющиеся 2 раза в сек (500ms) end end end end end Ну и более соответствующий всем 4-м критериям: local iCnt,iIdx = 0,0 --/ кол-во активных функций и индекс текущей local iTimer,iTimePrd = 0,0 --/ таймер и период его обновления local tExtFunc = { --/ массив внешних функций и флаг(вкл/откл) [1] = {func = my_script_1.my_func_1, flg = true}, [2] = {func = my_script_2.my_func_2, flg = true}, [3] = {func = my_script_3.my_func_3, flg = true}, [4] = {func = my_script_4.my_func_4, flg = true}, [5] = {func = my_script_5.my_func_5, flg = true} } function any_update() if iTimer < time_global() then --/ сверка с текущим временем if iIdx < iCnt then iIdx = iIdx +1 --/ следующий период else --/ закончен цикл: обновляем параметры iIdx = 1 --/ сброс индекса текущего периода iCnt = 0 --/ сброс кол-ва функций for k,v in pairs(tExtFunc) do if v.flg == true then --/ вызов функции активен? iCnt = iCnt +1 --/ подсчет кол-ва активных вызовов end end --/ установка периода вызовов в пропорции от кол-ва активных вызовов iTimePrd = 1000/math.max(iCnt,0.1) --/ при нуле - ставим тайм-аут 10 сек end iTimer = time_global() + iTimePrd --/ 'тикает' таймер if tExtFunc[iIdx].flg == true then --/ функция активна? tExtFunc[iIdx].func() --/ вызываем внешнюю функцию end end end function Add_MyFunc(my_func) --/ добавление функции table.include( tExtFunc, {func = my_func, flg = true} ) end function Start_MyFunc(iIdx) --/ активизация вызова функции tExtFunc[iIdx] = true end function Stop_MyFunc(iIdx) --/ деактивизация вызова функции tExtFunc[iIdx] = false end Данный вариант предусматривает только по-секундный таймер, который позволяет 'размазать' вызываемые функции по всему диапазону времени (периоду 1 сек), от(в)ключать вызовы функций извне, добавоять дополнителльные функции. Сделать при необходимости аналогичные таймеры на милисекунды иль минуты/часы - получаем полный набор. Внеся предуставноки, позволяющие разнести и добавленные таймеры, можно добиться исключения единомементности запуска различных функций, что минимизирует возможность лагов в игре. Т.о. если есть желание спорить о том, что какое-то решение имеет ошибки или лучше иного - давай поспорим. Если же о том, что нечто далеко не лучшее, но все же получше плохого - давай делать это не в этой теме. И уж тем более не по частностям невостребованных вариантов. P.S. Подправленный код для local iCnt,iAct,iIdx = 0,0,0 --/ кол-во функций/вызовов и из порядковый индекс local iTimer,iTimePrd = 0,0 --/ таймер и период его обновления local tExtFunc = { --/ массив внешних функций и флаг(вкл/откл) [1] = {func = my_script_1.my_func_1, flg = true}, [2] = {func = my_script_2.my_func_2, flg = true}, [3] = {func = my_script_3.my_func_3, flg = true}, [4] = {func = my_script_4.my_func_4, flg = true}, [5] = {func = my_script_5.my_func_5, flg = true} } function any_update() if iTimer < time_global() then if iIdx >= iCnt then --/ цикл закончен: обновляем параметры iTimePrd = 1000 --/ общий период цикла таймера (1 сек) iCnt,iAct,iIdx = 0,0,1 --/ сброс кол-в и индекса for k,v in pairs(tExtFunc) do iCnt = iCnt +1 --/ подсчет кол-ва функций if v.flg == true then iAct = iAct +1 --/ подсчет кол-ва активных вызовов end end if iCnt > 1 then --/ 'размазываем' период на кол-во активных вызовов iTimePrd = iTimePrd/iAct end else iIdx = iIdx +1 --/ следующий период цикла end iTimer = time_global() + iTimePrd --/ 'тикает' таймер for i=iIdx,iCnt do --/ неактивные вызовы пропускаются if tExtFunc[i].flg == true then --/ функция активна? tExtFunc[i].func() --/ вызываем внешнюю функцию return end end end end function Add_MyFunc(my_func) --/ добавление функции table.include( tExtFunc, {func = my_func, flg = true} ) end function Start_MyFunc(iIdx) --/ активизация вызова функции tExtFunc[iIdx] = true end function Stop_MyFunc(iIdx) --/ деактивизация вызова функции tExtFunc[iIdx] = false end Изменено 30 Сентября 2011 пользователем kokkai "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение