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

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

Вопрос-1: Как оптимально определить тип таблицы 'список' (list), т.е. типа: {"a","b","c","d"} или {1,5,2,4,3}?

 

Интересует не просто что-то вроде 'if next(Tbl) == 1 then', что явно недостаточно, и не полный перебор списка на поиск 'дыр' в индексах, а что-то пооптимальнее. Т.е. 100%-ое, нересурсоемкое и быстрое определение именно списка, а не иного типа массива. Совместимость с LUA игры обязательна.

 

Вопрос-2: Как перевести целочисленное число в хекс-стринг, т.е. типа: 65535 => "FFFF" и обратно?

Интересуют быстрые оптимальные варианты совместимые с LUA игры.

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

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

Ссылка на комментарий

Artos

По первому вопросу боюсь что без перебора не получиться.

В принципе есть идея чтобы не перебирать. Но это когда буду дома.

По второму вопросу могу пока предложить string.format

hex = string.format('%x', 65535)
dec = string.format('%d', '0x'..hex)

У меня есть доморощенная функция преобразования DЕС/НЕХ, но я сейчас с тела. Вечером сравню скорости и сообщу.

 

Метод 'string.format('%d', '0x'..hex)' - не применим для игры, т.к. ожидается только численное значение и стринг не приемлем (=> фатал еррор)

--/ Artos

Изменено пользователем Artos
Ссылка на комментарий

Artos,

Как оптимально определить тип таблицы 'список' (list), т.е. типа: {"a","b","c","d"} или {1,5,2,4,3}?

На мой взгляд нет такого способа. Ведь между этими таблицами нет никакой принципиальной разницы. Только в типе ключей и в наличии/отсутствии разрывов в последовательности ключей.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

malandrinus,

(уже позже, перечитав вопрос, понял о двузначности фразы ...)

Не разница между этими таблицами интересует, а это два основных варианта таблиц именно интересующего типа 'список'.

Т.е. интересуют все таблицы, которые ключем имеют индексную последовательность, а не собственное значение ключа.

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

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

Ссылка на комментарий

Artos,

Т.е. интересуют все таблицы, которые ключем имеют индексную последовательность, а не собственное значение ключа.

Совсем я запутался. Таки надо определить, массивом или таблицей общего вида является таблица? Или что-то другое?

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Artos, Тоесть. ты хочешь определить, имеет ли список строчные значения, или же числовые?! Но ведь список может иметь и оба типа значений. К кому такой список приравнять? А по сути, без полного перебора таблицы, невозможно определить типы всего списка значений.

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

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

Опаа-а!!! Ливер вылез!

Ссылка на комментарий

Недавно нарвался на интересную и неприятную особенность метода перебора с ipairs. Я хотел парсить список аргументов переменной длины и написал что-то вроде такого

function fun(...)
    for _,arg in ipairs({...}) do
        -- ...
    end
end

Так вот выяснилось, что перебор останавливается на первом же аргументе со значением nil даже если за ним имеются ещё аргументы. Это, вообще говоря, идёт вразрез с официальной документацией. Вот цитата (на русском, но на английском написано тоже самое):

ipairs (t)

Возвращает три значения: итератор, таблицу t, и 0, поэтому конструкция

        for i,v in ipairs(t) do body end

будет выполнять цикл парами (1,t[1]), (2,t[2]), ···, до первого целого ключа, отсутствующего в таблице.

Т.е. по идее значение имеют только ключи. Они должны быть целые, непрерывные от единицы. Ну вы понимаете. Это здесь естественно выполняется автоматом, но ipairs тем не мене не работает. Точнее ipairs то может и работает, но не работает алгоритм перебора, основанный на нём.

Пришлось делать так:

function fun(...)
    local args = {...}
    for k=1,#args do
        local arg = args[k]
        -- ...
    end
end

Самое интересное, что оператор получения длины # (решётка) при наличии значений nil работает без проблем и показывает полную длину, а не до ближайшего nil.

 

Именно с этим столкнулся, когда делал безопасный универсальный вывод в лог-файл вместо штатного 'printf()', и пришел к аналогичному решению.

Вот и в данном случае, в отношении таблиц-списков, интересует - как выделить такие 'args = {...}' даже с 'nil'-ами, отделив их от иных типов массивов/матриц.

--/ Artos

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

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

malandrinus, И всё же. Вопреки описанию, методы pairs и ipairs производят проверку значений на nil. И если таковой находится, итерация прекращается. Чтобы в этом убедиться, достаточно просмотреть исходник данного метода:

...

static int pairsmeta (lua_State *L, const char *method, int iszero,
                      lua_CFunction iter) {
  if (!luaL_getmetafield(L, 1, method)) {  /* no metamethod? */
    luaL_checktype(L, 1, LUA_TTABLE);  /* argument must be a table */
    lua_pushcfunction(L, iter);  /* will return generator, */
    lua_pushvalue(L, 1);  /* state, */
    if (iszero) lua_pushinteger(L, 0);  /* and initial value */
    else lua_pushnil(L);
  }
  else {
    lua_pushvalue(L, 1);  /* argument 'self' to metamethod */
    lua_call(L, 1, 3);  /* get 3 values from metamethod */
  }
  return 3;
}


static int luaB_next (lua_State *L) {
  luaL_checktype(L, 1, LUA_TTABLE);
  lua_settop(L, 2);  /* create a 2nd argument if there isn't one */
  if (lua_next(L, 1))
    return 2;
  else {
    lua_pushnil(L);
    return 1;
  }
}

//Метод вызываемый функцией pairs(tbl)
static int luaB_pairs (lua_State *L) {
  return pairsmeta(L, "__pairs", 0, luaB_next);
}


static int ipairsaux (lua_State *L) {
  int i = luaL_checkint(L, 2);
  luaL_checktype(L, 1, LUA_TTABLE);
  i++;  /* next value */
  lua_pushinteger(L, i);
  lua_rawgeti(L, 1, i);
  return (lua_isnil(L, -1)) ? 1 : 2;
}

//Метод вызываемый функцией ipairs(tbl)
static int luaB_ipairs (lua_State *L) {
  return pairsmeta(L, "__ipairs", 1, ipairsaux);
}

...

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

 

 

Небольшая (пока) ремарка: Прекращается при значении key == 'nil' , но(!) метод 'pairs' продолжается при key ~= 'nil' и 'value' == 'nil' .

Т.е. критично для итерации значение 'key', а не 'value'.

--/ Artos

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

Опаа-а!!! Ливер вылез!

Ссылка на комментарий

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

Загрузка секторов и порталов...
* Loading HOM: d:\namod\gamedata\levels\l01_escape\level.hom
* phase time: 41 ms
* phase cmem: 273647 K
Загрузка ИИ объектов...
- Loading music tracks from 'l01_escape_musics'...
* phase time: 82 ms
* phase cmem: 273686 K
Клиент: Создание...
- Game configuring : Started 
- Game configuring : Finished 
* phase time: 506 ms
* phase cmem: 281511 K
Загрузка текстур...
* t-report - base: 976, 339154 K
* t-report - lmap: 0, 0 K
* phase time: 5219 ms
* phase cmem: 281511 K
Клиент: Синхронизация...
* phase time: 42 ms
* phase cmem: 281511 K
* [win32]: free[942052 K], reserved[246088 K], committed[908948 K]
* [ D3D ]: textures[339154 K]
* [x-ray]: crt heap[281511 K], process heap[457918 K], game lua[26436 K], engine lua[216 K], render[0 K]
* [x-ray]: economy: strings[5446 K], smem[26970 K]

FATAL ERROR

[error]Expression    : fatal error
[error]Function      : CScriptEngine::lua_error
[error]File          : E:\stalker\patch_1_0004\xr_3da\xrGame\script_engine.cpp
[error]Line          : 73
[error]Description   : <no expression>
[error]Arguments     : LUA error: d:\namod\gamedata\scripts\_g.script:20: bad argument #2 to 'format' (string expected, got no value)


stack trace:

Scheduler tried to update object escape_trader
! Invalid ogg-comment version, file:  d:\namod\gamedata\sounds\wepack\weapons\oc14\reload.ogg
Intro start 7147
* MEMORY USAGE: 301282 K

FATAL ERROR

[error]Expression    : fatal error
[error]Function      : CScriptEngine::lua_error
[error]File          : E:\stalker\patch_1_0004\xr_3da\xrGame\script_engine.cpp
[error]Line          : 73
[error]Description   : <no expression>
[error]Arguments     : LUA error: d:\namod\gamedata\scripts\_g.script:20: bad argument #2 to 'format' (string expected, got no value)


stack trace:

 

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

 

Пролистай несколько страниц назад (см. пост #2603 и далее), где была и аналогичная запись в логе и дана была рекоментация по замене 'кастрированной' функции 'abort()' для поиска причины ошибки.

--/ Artos

Изменено пользователем Artos
Ссылка на комментарий

Цель заданных вопросов по таблице-списку и переводам Hex->String->Hex:

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

Вариантов сохранения по сути три:

1. Записывать в 'pstor' объектам;

2. Записывать в пакет объекта напрямуую;

3. Записывать во внешние файлы.

Вариант 3 - для ТЧ практически отпадает, т.к. класс работы с файлами достаточно рудиментарен, тем более на запись.

Да и 'мусорить' на компе игрока - не самый удачный вариант.

 

Вариант 2 для разрабатываемых модов или опционально подключаемых мини-модов/модулей - неприемлем и-за несовместимости сэйвов.

 

Остается вариант 1.

 

Запись в 'pstor' (вариант 1) имеет два подварианта:

1.1. Штатный метод из 'xr_logic.script' (pstor_store/pstor_retrieve);

1.2. Расширеный метод, дополненный сохранением таблиц.

Ярким (и наиболее часто используемым до сих пор) примером являются методы АМК-мода (amk.script -> save_variable/load_variable).

 

Особенности использования варианта 1.1 'штатным' методом:

В 'pstor' записываются только три типа значений "boolean", "string", "number". Запись таблиц невозможна.

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

Все(!) численные значения записываются в сэйвы единым методом "w_float", т.е. всем им в обязательном порядке выделяется 4 байта (float == w_u32).

Т.о. даже числу '1' (единичке) выделяются те же 4 байта из потребного одного бита.

В игре из числовых значений как правило сохраняются время, игровые идентификаторы объектов, некие кол-ва и флаги (0/1/2).

Т.о. основная часть сохраняемых числовых значений вполне укладывается в два байта (u_16 -> 65535).

Внеся в 'штатный' метод сохранения/чтения сэйвов доп.признак 'целое число не более FFFF' - можно сэкономить немало байт в сэйвах.

 

 

Особенности использования варианта 1.2 'расширеным' методом:

Расширеный вариант помимо некоторого удобства применения (не требуется указывать объект и можно записывать 'nil'), имеет возможность записи таблиц.

Для записи таблиц используется перевод всех полей (кеу и value) в таблицах в строковые значения и конкатенация (слияние) их в единую строку, которая и сохраняется. При чтении - обратная операция по восстановлению таблицы.

 

1. При переводе все типы таблиц упаковываются едино, т.е. и ключ и значение его поля (key & value) переводятся в строки.

Несложно заметить, что для таблиц типа 'список', в качестве ключей будут упаковываться индексы (1,2,3... и т.д.).

Внимание(!). Но ведь для таких таблиц по сути и НЕ требуется запоминать ключи-индексы. Достаточно только запомнить, что это именно 'список' и сохранить значеня в порядке их следования. Минусом/плюсом является эффект 'чистки', если в списке есть 'дырки' (нилевые значения). Для сэйвов это плюс.

 

2. При переводе таблицы в строку, опять же используется единый метод 'tostring(number)' для всех численых значений.

Т.к. переводится в строку десятичное значение(!), то кол-во знаков (а каждый знак - это байт при записи!) для чисел более 9 начинается 'растранжиривание' байтов. Если конвертировать в стринг не десятичное, а шестнадцатеричное значение - экономия на лицо.

 

 

Практически реализовал вышеописанную оптимизицию для записей в 'pstor', которая совместима с нынешними играми/сэйвами (при отсутствии обратной совместимости). Осталось гарантированно (все же сэйв) определять признак (НЕ)списков и найти оптимальный вариант методов конвертеров Hex -> String -> Hex. Думаю к 'завтра' выложу для публичной критики и/или общего применения.

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

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

Ссылка на комментарий
....т.к. ожидается только численное значение и стринг не приемлем (=> фатал еррор)

Я поленился на теле набирать, но ведь tonumber ещё не отменяли :-)

dec = string.format('%d', tonumber('0x'..hex))

Или Сталкер и этого не понимает?

К сожалению ... Метод 'tonumber' ожидает только десятичное представление числа символами строки.

Любой не 'числовой' символ ('x') - воспринимается как буква и => 'nil'.

--/ Artos

P.S. Подзабыли про параметр '16' для метода ... :-)

Изменено пользователем Artos
Ссылка на комментарий

_Призрак_

Хотя ты и немного ошибся с переводом в 'hex_num' (удалив и пост), но спасибо за подсказку! Это как раз перевод в 'dec_num'.

Похоже это:

local iDec = 12345 --/ исходное десятичное целочисленное
local sHex = string.format('%X', iDec) --/ переводим в хекс-строку
iDec = tonumber('0x'..sHex, 16) --/ переводим в десятичное число хекс-строку

- то, что нужно! (Подзабыл про параметры для этого метода)

 

Одна голова - маловато будет. Вот несколько - в самый раз. :good2:

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

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

Ссылка на комментарий

Можно без '0x' (на всякий случай уточню) в чистом lua

hex = string.format('%x', 65535)
dec = tonumber(hex,16)

Спасибо. Проверил: в игре такой вариант работает.

--/ Artos

Изменено пользователем Artos
Ссылка на комментарий

У меня вопрос.Функции, относящиеся к гулагам из xr_conditions.script, применимы ли к смарттерейнам, работающими на основе схемы genelar_lager?

Что означают эти параметры смартов

switch_0 = {!is_day}

switch_1 = {=is_day}

switch_0 = {=gulag_empty(esc_stone_lager)}

switch_1 = {=gulag_population_comed_ge(esc2_dogs_zamost:3) !gulag_empty(esc_stone_lager) =gulag_state(esc2_dogs2_exit:0)}?

Заранее,спс.

Я так понял, switch_0 = условия для дневной работы, а switch_1 = условия для ночной работы, да?

Изменено пользователем panzyuza
Ссылка на комментарий

panzyuza

Функции из 'xr_conditions.script' - это функции общего применения и не привязаны к каким-то конкретным гулагам (если внутри функции нет конкретных указаний на имена/секции) и могут применяться в любых схемах логики. Да и простыми скриптами, при правильном указании входных аргументов.

 

Параметры 'switch_0' и 'switch_1' - аналоги функции 'load_states()' из скриптов типа 'gulag_XXX.script' для 'именных' гулагов, возвращающей состояние гулага в зависимости от неких условий.

Твое предположение почти верно - гулагам типа 'genelar_lager' именно этими параметрами задаются условия и проверяются их состояния.

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

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

Ссылка на комментарий

Artos, прошу еще пояснить.

switch_0 = {=gulag_empty(esc_stone_lager)}

switch_1 = {=gulag_population_comed_ge(esc2_dogs_zamost:3) !gulag_empty(esc_stone_lager) =gulag_state(esc2_dogs2_exit:0)}

В данном смарте ночная работа доступна при отсутсвии кого-либо в гулаге esc_stone_lager.

Во втором параметре дневная работа доступна, если в гулаге esc2_dogs_zamost больше трех обьектов, если в гулаге esc_stone_lager есть кто-то, если в гулаге esc2_dogs2_exit установлена дневная работа.Я правильно истолковываю понятия?

Точнее наоборот, switch_1 = дневная работа, switch_0 = ночная.

Я понял, что в скрипте xr_conditions функции работают по принципу = условие равно, ! = условие ложно.

Изменено пользователем panzyuza
Ссылка на комментарий

panzyuza

Все (почти), о чем ты спрашиваешь - лежит в кодах 'gulag_general.script' и конечно в 'xr_logic.script'.

 

Условия же читаются из 'xr_conditions.script'.

Что конкретно проверяет каждая функция из условий - зависит от нее.

 

Строка: 'switch_0 = {=gulag_empty(esc_stone_lager)}' - приведет к тому, что при пустом гулаге 'esc_stone_lager' будет возвращено состояние '0' для гулага 'esc2_dogs_zamost' с этой стокою. Никакой тут ночной/дневной работой и не пахнет. Вероятно просто собаки вернутся в свой гулаг (не смотрел логику).

 

Вторая строка с 'switch_1' - вернет состояние гулага '1' при упомянутых в строке (и растолкованных тобою) условиях. Вероятно собаки побегут нападать ...

 

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

 

Более правильно: Условие истино, если - '=' функция вернет true, Условие истино, если - '!=' функция НЕ вернет true (т.е. если вернет false).

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

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

Ссылка на комментарий

Понял.Ошибься.Действительно, даже в скриптах gulag_xxx так регулируються состояния гулага.И нет дневной и ночной работы.Просто немного запуталься.Данные параметры также имитируют алайф в сталкере.

 

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

В статьях по логике написано, что схема xr_rest нормально не работает.Есть ли данная схема, доработанная и дополненная?

Ссылка на комментарий

По поводу определения типа таблиц.

Набросок без итераторов. Проверку на пустую таблицу и скорость не делал.

function GetTabStatus(tab)
    if #tab ~= 0 and not  next(tab, #tab) then
        return 'index' -- индексирудмый массив
    else
        return 'hash' -- всё подряд
    end
end

Ссылка на комментарий

Gun12, не обязательно сравнивать размер таблицы с нулем

if #t then

вполне достаточно, и не сработает если индексы в таблице не натуральные

Vita sine libertate, nihil

Vita sine litteris - mors est

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

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