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

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


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

С чего начинать и где взять.

 

Установка Lua:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=629106

 

Руководство «Программирование на языке Lua», третье издание:
http://www.amk-team.ru/forum/index.php?showtopic=11584&p=905308

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

Artos

 

1. Упрощен/оптимизирован процесс записи/чтения нет-пакетов объектов хранения. Никаких лишних операций.

2. Операция по сериализации хранимых в хранилище таблиц оставлена только непосредственно при сохранении таблий в объекты хранения., что дало возможность оптимизировать оперции чтеия/записи в собственно хранилище.

 

Блин, а. :huh: Артос, не надо чинить то, что работает, пожалуйста. В твоё текущем варианте есть пара злобных привнесенных косяков:

 

Во-первых не ясно, когда хранилище таки полностью загрузилось, в результате все равно надо ждать спавна актора. Какой тогда смысл был переделывать, ммм? Во-вторых эта твоя "оптимизация" привела к тому, то ты не узнаешь, если сериализованная таблица вылезла за размеры хранилища.

 

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

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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

malandrinus,

Как я понимаю, класс может иметь только один метод-подписчик:

mt.__call = self.method_to_call

Как быть в ситуациях когда в классе несколько методов которые необходимо подписать на разные события? Например, есть класс "actor_hit_effect", в нем два метода-подписчика - один на actor_hit, другой на item_use. Или же я что-то недопонял?

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

Andrey07071977,

Как быть в ситуациях когда в классе несколько методов которые необходимо подписать на разные события? Например, есть класс "actor_hit_effect", в нем два метода-подписчика - один на actor_hit, другой на item_use. Или же я что-то недопонял?

Использовать замыкания. Я раньше писала, как это делать. Я не знаю, зачем такие сложности Саша придумал :)

Вот вариант:

function bind_method_x(obj)
    return function (...)
        return obj:method_x(...)
    end
end

local funct = bind_method_x(megaobject) --в переменной funct окажется функция, которую можно передать куда угодно.

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

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

xStream, спасибо, перечитаю твой пост и пройдусь по замыканиям, пока что я туплю не по детски на эту тему... :dash2: Такое впечатление, что крутится где-то в голове но пока не могу расставить точки над i.

 

То есть, для моего примера необходимо создать два метода?

function object_execute(obj)
    local _proxy = obj
    local function _call(...)
        return _proxy:execute(...)
    end
    return _call
end
function object_execute_1(obj)
    local _proxy = obj
    local function _call(...)
        return _proxy:execute_1(...)
    end
    return _call
end

 

Да, я знаю про что ты говорила :)

 

Понял, спасибо. Но теорию все равно нужно поднатаскать.. уж больно много пробелов :)

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

Andrey07071977, http://www.amk-team.ru/forum/index.php?s=&...st&p=661284

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

 

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

Да, правильно. Можно, правда, еще короче:

function object_execute(obj)
    return function (...)
        return obj:execute(...)
    end
end
function object_execute_1(obj)
    return function (...)
        return obj:execute_1(...)
    end
end

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

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

Kamikazze,

Во-вторых эта твоя "оптимизация" привела к тому, то ты не узнаешь, если сериализованная таблица вылезла за размеры хранилища.

Разве это не проверка перед сохранением?

for key, val in pairs(storage) do
        lua_type = type(val)
        num_type = allowed_types_rev[lua_type]
        err = num_type == nil
        if not err then
            -- calculate variable len for saving
            elm_len = 1 + string.len(key) + 1
            if lua_type == 'boolean' then
                elm_len = elm_len + 1
            elseif lua_type == 'number' then
                elm_len = elm_len + 4
            else
                if lua_type == 'table' then
                    val,err = table.serialize(val)
                    if err or val:len() >= max_packet_len then
                        --/ тип данных или длина строки недопустимы
                        --log("save:var_name=[%s],err=[%s]:<%s>", key, err, "Warning!")
                        val,err = "",true
                    end
                end
                elm_len = elm_len + val:len() + 1
            end
            
            if not err then
                --/ check room to save
                if len + elm_len > max_packet_len then    -- element is full of data
                    -- create new element to store variables
                    -- ugly hook, but fastest solution :)
                    tail_pk = new_storage_element()
                    
                    len = elm_len
                else -- still have room to save
                    len = len + elm_len
                end
                
                --write variable to tail
                tail_pk:w_u8(num_type)
                tail_pk:w_stringZ(key)
                if lua_type ==  'boolean' then
                    tail_pk:w_u8(val)
                elseif lua_type == 'number' then
                    tail_pk:w_float(val)
                else
                    tail_pk:w_stringZ(val)
                end
            end
        end
    end

 

 

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

kamikazze, если ты не понял ни цели "для чего выложено", но того что же сделано, зачем же сразу наезжать?

Мы тут обсуждаем или ругаем? Если говоришь о "злобных привнесенных косяков" - ну так и ткни носом, а не голословно ругай и ... подучи мат.часть вначале.

1. Собственно ничего и НЕ чиню, а делаю свою реализацию (считай сделал) на базе материалов xStream и собственных наработок. Как я понял, у malandrinus'а аналогичное.

2. Если тебе непонятно как работает - поясню, для этого и вложил второй рабочий на 100% вариант, чтобы если в первом упустил/очепятался, то по второму все можно проверить.

 

Основное отличие моей реализации в том, что почти никаких внешних вмешательств в функционирование хранилища не требуется.

1. Само хранилице (массив) расположено в _G и готово к записи/чтению сразу по запуску игры. Естественно оно первоначально пустое.

2. По мере игры в хранилище поступают различные данные различных типов данных. Специально убрал сериализацию таблиц, дабы и не усекать возможносьт хранения в общем хранилище даже тех типов данных, которые не будут сохранены. Мне пока такое не требуется (привык не хранить в pstor'ах лишнего), но не вижу надобности в таком ограничение в самой игре. Само собою, в сэйвы невалидные типы данных не сохранятся.

3. По событию "сохранить данные из хранилища", которое я у себя делаю или по окончанию сэйва актора или даже по сэйву его КПК - включается цикл сохранения. Расписывать несколько объектов для сохранения нет смысла (пока) и рассмотрим один.

- Создается объект и им порожденный tail-пакет, которому устанавливаются необходимые позиции чтения и записи. Именно этот подготовленный tai-пакет передается в менеджер сохранения данных хранилища.

- В tail-пакет (со 2-ой позиции) записываются необходимые данные - до его заполнения (<8кБ) и пакет оставляется в покое(!), т.е. никаких принудительных операций сторонними нет-писалками с ним не делается. Хранилище сохраняет далее в следующий созданный аналогчный tail-пакет ...

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

- Если игра именно прекрашается, т.е. "* Saving objects..." - объекты хранения движком сохраняются, что вызывает для каждого метод STATE_Write, в который добавлен копировщих STATE_Copying. Эта связка приводит к тому, что штатным вызовом сохранения объекта убиваются оба зайца, сохраняется как пакет объекта, так и перезаписанный в него tail-пакет этого объекта(!).

Т.к. позиции чтения выставлена и имеется конец записи - в объект копируются именно данные из хранилища.

Все,объект(ы) сохранены.

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

 

Теперь о заполнении хранилища при старте игры:

- Т.к. имеется серверный класс 'se_custom_storage' с его on_register и STATE_Read + STATE_Copying.

В момент загрузки объектов в иру ("* Loading objects...") загружаются и сторадж-объекты. Для каждого создается tail-пакет и движком вызывается STATE_Read. При этои из нетпакета объекта в tail-пакет считываются ранее сохраненные данные хранилища.

- по on_register tail-пакет передается считывателю данных в хранилище. Опять, никакие сторонние манипуляции сторонними нет-писалками не требуются.

Т.о. хранилще заполняется ранее сохраненными данными по факту появления в игре каждого объекта хранения и ПОЛНОСТЬЮ восстановлено к моменту подключения сервера к игре (Сервер: Соединяемся...). С этого момента хранилище готово выдавать все те данные, которые были в нем в момент сохранения в сэйв.

Хочешь ждать спавна актора - твое дело, но можно и не дожидаться, а еще до его спавна пользоваться сохраненными данными. Восстанавливать настройки модулей, таймеры и пр. ...

Так что и сам уже пользуюсь и думаю и другие будут пользоваться!

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

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

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

Andrey07071977,

Разве это не проверка перед сохранением?

А смысл мне от этой проверки? Это проверяется при сохранении и получаем ошибку.

Функция set была изначально организована так, что возвращала булевое значение - успешность помещения переменной в хранилище. Тогда, программист, получив false, мог бы принять меры - уменьшить как-то размер таблицы. (Черт! Надо еще длину у строки проверять!) А так. Он запихнул, а потом огреб ошибку, и как ее обрабатывать?

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

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

По поводу сериализации уже дали реплику.

Зачем ограничивать само хранилище, когда это делать нужно только для объектов хранения!

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

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

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

Andrey07071977,

Как быть в ситуациях когда в классе несколько методов которые необходимо подписать на разные события? Например, есть класс "actor_hit_effect", в нем два метода-подписчика - один на actor_hit, другой на item_use.

Как раз для этого есть вариант с указанием self

local hit_effect = actor_hit_effect()
sm:subscribe({signal = "on_actor_hit", self = hit_effect, fun = hit_effect.on_hit})
sm:subscribe({signal = "on_item_use", self = hit_effect, fun = hit_effect.on_use})

или даже так

class "actor_hit_effect"
function actor_hit_effect:__init()
    self.sm = ogse_signals.get_mgr()
    self.desc_on_hit = {signal = "on_actor_hit", self = self, fun = self.on_hit}
    self.sm:subscribe(self.desc_on_hit)
    self.desc_on_use = {signal = "on_item_use", self = self, fun = self.on_use}
    self.sm:subscribe(self.desc_on_use)
end
function actor_hit_effect:unsubscribe_itself()
    self.sm:unsubscribe(self.desc_on_hit)
    self.sm:unsubscribe(self.desc_on_use)
end

 

вариант с функциональным классом - это в сущности редко нужные изыски. С точки зрения Lua функциональный класс - это та же функция. Поскольку можно подписать функцию, то можно и функциональный класс. Только и всего.

 

xStream,

Я не знаю, зачем такие сложности Саша придумал

Нет там никаких сложностей. Как раз эта фича обеспечивается всего парой дополнительных строк. Передаётся первым аргументом self при его наличии, и сравнение дескрипторов слотов на равенство включает ещё и сравнение self.

 

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

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

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

 

Ссылка на комментарий
Это проверяется при сохранении и получаем ошибку

Не получаем ошибку, а получаем предупреждение, кпо которому можно при необходимости и абортнуть. Это все же разница.

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

 

Ну а озабоченным ничего не мешает два типа set'ов себе сделать: обычный и с сериализацией (типа set_safe) и будет ему счастье отлавливать баги.

Зачем же боясь аварии всегда ехать со скоростью 40 км/час, когда штатно можно можно и под 80 .. ;-)

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

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

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

Саш, дополнительные параметры в инетрфейсе, использование метатаблиц - это не сложности? По сравнению с одним единственным замыканием - еще какие.

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

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

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

Что если проверять при каждом перемещении в хранилище на этапе разработке/тестировании - по идее, все bottlenecks должны быть найдены на этих стадиях. А уже в финале оставить лишь проверку перед сохранением, в крайнем случае какие-то данные не сохранятся.

 

malandrinus, примерно так и преставлял, потом приведенный пример сбил с толку.

 

class "some_luabind_class"
function some_luabind_class:__init()
    local mt = getmetatable(self)
    mt.__call = self.method_to_call
end
function some_luabind_class:method_to_call(arg1, arg2)
end

local slot_desc = {signal = "signal_name", fun = some_luabind_class()}

Изменено пользователем Andrey07071977
Ссылка на комментарий
Andrey07071977, прикол в том, что боттлнека, кстати, не будет при таком подходе: при set производится одна сериализация, при сохранении в "оптимизированном" варианте - при сохранении сериализуется все. В первом подходе нагрузка распределена по времени. И влияние на производительность оказывает небольшое. Зато мы сразу предупреждаем программиста - у тебя тут гавно, срочно принимай меры.

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

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

xStream,

Саш, дополнительные параметры в инетрфейсе, использование метатаблиц - это не сложности? По сравнению с одним единственным замыканием - еще какие.

Да нет там никаких метатаблиц. Я же говорю, с функциональным классом - это изыски, побочный эффект и в общем и не нужно. А вот насчёт указания self, чего там мудрёного то? Ну указать объект надо, так а что такого? Есть объект, есть его метод, так и надо при подписке указать объект и метод. Если эта идея вызывает запредельный ступор при использовании, то я уже не знаю о чём тут говорить.

 

Однако, никто не мешает это не использовать. Как я говорил, это не усложняет реализацию ни на йоту, и при неиспользовании никому не мешает.

 

Andrey07071977,

потом приведенный пример сбил с толку

Там же был пример с обычным методом. Разве нет?

 

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

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

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

 

Ссылка на комментарий
malandrinus, если с обычном методом ты имеешь ввиду пример с глобальной функцией - то там все понятно. Не совсем понял про методы класса - думаю имеет смысл перенести пример, который ты привел позже, в первый пост. Пример довольно наглядный Изменено пользователем Andrey07071977
Ссылка на комментарий

Уж если разговор о работе с хранилищем, а не о сохранении записи, то ... мною НЕ используется сериализатор, а используется компрессор-сериализатор, что дает (даст) выигрыш более 40% на каждый объект хранения. Т.о. таблица, не влезающая в 8кБ сериализатром, вполне может быть сохранена без усечений и абортов игры.

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

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

Хранилище должно надежно функционировать и не давать сбоев, а не заниматься воспитанием модмейкера.

 

И, кину камень в ваш огород: В моем варианте в хранилище не попадут ни большие таблицы, ни большие строки, проверка длины которых в ваших вариантах с сериализатором НЕ предусмотрена и это (большая строка) обрушит объект хранения (сэйв)!

Конечно сохранять строку более 8кБ врядли кому понадобится, но ... давайте уж последовательно. Озаботились предупреждать нерадивых о таблицах, так чем же строки вам глянулись и не требуют проверки? ;-)

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

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

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

Саш, peace :) Просто ты привел такие примеры.

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

 

Все, кто стоит на моем пути: идите нахрен и там погибните! ©

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

Andrey07071977,

имеет смысл перенести пример, который ты привел позже, в первый пост.

И тем не менее, пример там есть. Последний по счёту. Впрочем, ты прав, заменю на более поздний. Только уже завтра...

 

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

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

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

 

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

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

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

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

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

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

Войти

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

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

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