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

Язык Lua. Общие вопросы программирования


Malandrinus

Рекомендуемые сообщения

malandrinus

Может быть кто-то знает такой инструмент по форматированию LUA контента и посоветует, но мне готовое не известно, но ...

Мысль в слух: Подобное форматирование вероятнее, если и реализуемо, то в виде макроса(ов). Если найти или написать требуемый макрос - то уже упоминавшиеся SciTE или UltraEdit позволяют подключать макросы и пользоваться ими. Обычно около 'крупных' редакторов для программеров формируется комьюнити, которое пишет что-то и на темы форматирований текстов, и выкладывает в депозитории. Могу ошибаться, т.к. давно не занимался такими поисками - но что-то на тему форматирования текстов на уровне макросов попадалось пару лет назад.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

malandrinus

Странно, слова твоей критики больше подходят к пользователям Винды и меньше к программерам.

1. По умолчанию программистский софт ставится или с административными правами или с расширенными.

"Lua for Windows", частью которого и является SciTE, требует при установке именно прав админа.

2. Следствие некорректной/частичной установки.

3. Есть два подхода: Windows - "Запрещено все, что неразрешено" и UNUX - "Разрешено все, что не запрешено".

Ты просто привык к первому.

4. Это все же продукт для тех, кто знает что ставит и чем пользуется, а не для тех, кто сам не помнит что поставил и зачем.

Минимальная инфа есть: версия, ссылки ...

5. согласен, но возможно можно настроить.

6. согласен, хотя опять таки тут конфликт взглядов. Если я сижу и обеими руками стучу по клаве, то мне более чем достаточно хоткея (Ctrl+W), для закрытия окон, чем искать мышь позиционировать на крестик и кликать ...

 

На первый взгляд и первое время мало какой софт воспринимается удобным, если человек уже привык к чему-то иному. Все же везде требуется адаптационный период.

Ну а если что-то остается неудобным или недостаточным - поэтому я ранее и писал о том, что в основном сижу в UE, а SciTE - тогда, когда именно занимаюсь около-"языковыми" вопросами.

 

 

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Вопрос: Каковы возможности в 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)

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение
abramcumner: содержимое lua_help.script не экспортировано

Ну, во-первых, это и не скрипт (lua_help), а считай напоминалка модмейкерам ... далеко не полная и нередко устаревшая.

Во вторых, даже если ... то мы-то кто(?) - модмейкры, т.е. те кто сами пишет/добавляет и классы и пр. - значит "наше" самописное тоже добавлять потребовалось бы.

 

Само собою, любой скрипт, в котором будут встречаться неопределенные переменные/классы/функции будет выполняться ровно настолько, насколько это все неизвестное определено/доступно.

 

Мне, например достаточно проверять не полностью скрипты из игры, которые привык писать для нее, а автономные простенькие, в которых не используются внешние переменные и пр.

Проверить работоспособность, скорость, сравнить, получить результат некоей вычислительной функции - все это и позволяет SciTE при его адаптации в игре (к файлам *.script).

Ну и ... можно же и экспортировать нужное тебе ;)

 

(поэтому мод пишу в UE, но нередко обращаюсь к SciTE, когда вопрос по чистому LUA)

 

Добавлено через 9 мин.:

Gun12: Кстати выполнять выделенную часть кода можно комбинацией "Ctrl + 4"

А вот если пояснишь, как это реализовать для скриптов игры (*.script) - был бы благодарен, т.к. как-то не нашел времени раскопать это ...

А выдирать нужные фрагменты в отдельные фалы - утомительно порою.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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. Выложи, если возможно, твои настройки или, как говорил 'портированный вариант для флешки'.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

(алаверды)

malandrinus, Пока говорим только про SciTE

Cм. вопрос: #9 ... и тишина. :-)

malandrinus, Не соглашусь по обоим пунктам

В моей фразе три пункта, поэтому по трем:

1. не скрипт - кроме расширения к скриптам этот файл не имеет никакого отношения.

2.1. далеко не полная - использовано относительное определение. То, что кому-то 'близко', для кого-то 'далеко'. Согласен, что 'далеко' несколько утрированно, но суть 'не полноты' передает.

2.2. 'Полнота' не обозначена чего именно, и тобою же самим делается оговорка. И даже в том смысле что "там перечислено всё, что экспортировано" - не соглашусь с определением "все". Распечатка даже некоторых методов говорит о далеко не полном перечислении их функций и пр. в этом файле.

3. устаревшая - то, что в этом файле перечислены методы, которые может быть когда-то и работали в каких-то билдах, но не работают с теми релизами, в которых этот файл присутствует - иначе не называют.

Остаюсь при своем мнении, что это всего лишь напоминалка, хотя и достаточно наполненная и даже в чем-то избыточная (на мой взгяд).

Для новичков в модинге, я ее называю "азбука"

 

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Вопрос: Возможно ли по аналогии с перловыми методами из языка 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, хотя и не всегда соответствует многим источника, откуда черпаю ... ;-))

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

malandrinus, задаю вопросы не из теоретический изысканий иль любопытства ради, а все же с учетом практического применения.

Мне не столь нужны вариации, что можно сделать иначе, а результат, т.е. оптимизиция как самого кода так и/или времени (второе первично).

Просто при своих познаниях и навыках могу чего-то не знать, чего-то упустить/забыть, вот и задаю вопрос к тем, кто может быть нашел/знает решение или наоборот, знает что иначе невозможно.

 

P.S. (подумав) Хотя ... ответ то не только мне предназначен, его и другие читают.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Вопрос из категории "не очевидно, но факт":

 

Возвращаясь в вопросу по способу определения типа таблицы: список или нет (#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, в вопросе по патерну для нескольких строк меня интересовал единый патерн и приоритет был - оптимизиция времени. Оптимизация кода была все же вторичной задачей.

Предложенный вариант однозначно проиграет по времени последовательности проверок по каждой строке. :-(

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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

А оператор # возвращает номер последнего индекса в таблице, за которым отсутствуют как минимум два следующих индекса подряд.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Итого, с точки зрения гарантированности, достаточности и оптимальности по производительности (по времени) и строк кода, похоже этот вариант наилучший?

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

-и или есть недостатки, оптимальнее?

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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' больше кол-ва разрядов этого же числа в шестнадцатиричной форме.

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение
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, а игра вторична или ... наоборот? ;-)

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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 и/или хотя бы организовать типа удобного линка на него?

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

(по следам ранее опудликованного)

"Упаковка таблицы в строку (стринг) и обратная распаковка"

 

Доработанный вариант с возможностью проверки в 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 мин.:

Поправил пару ошибок в комплекте ... (тяжко все же коды вставлять в парсер форума)

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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, аналогично. :-)

Почему-то многие боятся задавать вопросы, просить помощи в топиках форума ... И это скорее не стеснение, а боязнь выглядеть в глазах многих иль совсем уж 'чайником', или не таким уж матерым кодером ...

Все мы в чем-то чайники и далеко не во всем 'матерые'! На то и раздел - "Школа моддинга"

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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сек) и независимы

 

(Приношу извинения куратору темы за этот оффтопик, но ... так и не понял до сих пор, о чем же тут стОит гутарить, а чего следует избегать.)

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Все дейстия можно разделить на группы:

а) - постоянно требующие проверок условий/вызовов на всем протяжении игры (например, проверка здоровья/хита/...);

б) - постоянно требующие проверок условий/вызовов на протяжении игры, но на конкретной локации (группе локаций);

в) - требующие непрерывных проверок условий/вызовов до или после некоего события (например инфопоршня);

г) - требующие разовой проверки условий/вызовов после некоего события и до другого события;

...

Если на апдейты вешать вызовы только тогда, когда это требуется и отключать их, когда в них отпала нужда - бОльшая часть очередей рассосется и 'не активное' не будет вообще как-либо нагружать игру и съедать FPS'ы. :-)

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

Предлагаю обсудить вариант безопасного, информативного и производительного вывода информации в лог, с учетом особенностей игры.

Известно, что разработчики по сути кастрировали вывод информационных сообщений из скриптов. Разработка, отладка, поиск ошибок без подобной информации затруднителен. Начинающие модмейкеры порой и не догадываются о полезности вывода в лог удобных сообщений при модификациях кодов.

Простейшие варианты грешат тем, что не учитываются возможные ошибки при выводе в лог и подобные функции просто 'глушат', дабы из-за них не вылетала игра.

 

Итак критерии: 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
--/------------------------------------------------------------------

 

Замечания, предложения, критика и оптимизиция - приветствуются. :-)

Изменено пользователем Artos

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение

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

 

Изменено пользователем kokkai

"Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени

Поделиться этим сообщением


Ссылка на сообщение
  • Недавно просматривали   0 пользователей

    • Ни один зарегистрированный пользователь не просматривает эту страницу.
×
×
  • Создать...