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

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


Malandrinus

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

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

 

--[[
  Call functions in protected environment (original: http://www.amk-team.ru/forum/index.php?showtopic=6458#entry258260)
  @param string sType - "file" or "chunk". Required. TODO: "chunk" functionality
  @param string sName - file path. Optional. Path of the file to load relative to bin, will use default (aa_test.script) if nil.
  @param boolean bFunc - execute one function with arguments. Optional. By default will execute function test(...)
  @param table tArgs - arguments for function above. Optional. TODO: find out why ... didn't work.
  @return object oResult - function execution results (if any)
--]]
function sandbox(sType, sName, bFunc, tArgs)
    local sStatus, oResult = pcall(exec, sType, sName, bFunc, tArgs)
    if sStatus == false then
      --ODS("~C07[~T] ~C0C[ERROR] ~C07in ~C0Asandbox()~C07 call - ~C0B"..tostring(sResult).."~C07")
      --// Ouput to screen or log
    else
      return oResult
    end
end

--[[
  Execute file or chunk (original: http://www.amk-team.ru/forum/index.php?showtopic=6458#entry258260)
  @param string sType - "file" or "chunk". Required. TODO: "chunk" functionality
  @param string sName - file path. Optional. Path of the file to load relative to bin, will use default (aa_test.script) if nil.
  @param boolean bFunc - execute one function with arguments. Optional. By default will execute function test(...)
  @param table tArgs - arguments for function above. Optional. TODO: find out why (...) didn't work.
--]]
function exec(sType, sName, bFunc, tArgs)
  local t = tArgs or {}
  if sType == "file" then
    if sName then
      local sReturn = assert(loadfile(sFilePath))
      if type(sReturn) == "function" then
        if bFunc then
          sReturn()
          test(t) --// execute specific function with arguments t
        else
          sReturn()
        end
      else
        --ODS("~C07[~T] ~C0C[ERROR] ~C07in ~C0Aexec()~C07 call - ~C0B"..tostring(sReturn).."~C07")
        --// Ouput to screen or log
        return
      end
    else
      local sFilePath = "..\\gamedata\\scripts\\aa_test.script"
      local sReturn = assert(loadfile(sFilePath))
      if type(sReturn) == "function" then
        if bFunc then
          sReturn()
          test(t)
        else
          sReturn()
        end
      else
        --ODS("~C07[~T] ~C0C[ERROR] ~C07in ~C0Aexec()~C07 call - ~C0B"..tostring(sReturn).."~C07")
        --// Ouput to screen or log
        return
      end
    end
  elseif sType == "chunk" then
    --// TODO: chunk functionality
    return
  else
    --ODS("~C07[~T] ~C0C[ERROR] ~C07in ~C0Aexec() ~C07call - invalid sType: ~C0B"..tostring(sType).."~C07")
    --// Ouput to screen or log
  end
end

 

 

 

Примечания - используя Lua перехватчик от alpet, который помимо всего прочего (alpet спасибо!), позволяет выводит строки в одельную консоль и сдвигать ее на второй монитор.

 

Callback:

function on_actor_hit(oObj, fAmount, vDirection, oWho, iBoneIndex)
  aa_util.sandbox("file", "..\\gamedata\\scripts\\aa_test1.script", true, {oObj, fAmount, vDirection, oWho, iBoneIndex})
end

 

aa_test1.script:

function test(tbl)
  oObj, fAmount, vDirection, oWho, iBoneIndex = unpack(tbl)
  local sWhoName = oWho:character_name()
  local sWpn = oWho:active_item():section() or oObj:active_item():section()
  local sBodyPart = aa_util.get_body_part(iBoneIndex)
  local fActorHealth = oObj.health
  if fActorHealth <= 0.5 then
    oObj.health = 1
  end

-- вевести все на экран или в лог (я использую Lua перехватчик, поэтому вызовы закомментировал - думаю разберетесь)
--[[ 
  ODS("~C07[~T] ~C0E[INFO] ~C06Actor hit details: ----------------------------~C07")
  ODS("~C07[~T] ~C0E[INFO] ~C07Hit by: ~C0B"..sWhoName.."~C07")
  ODS("~C07[~T] ~C0E[INFO] ~C07Amount: ~C0D"..string.format("%2.1f", fAmount*100).."~C07")
  ODS("~C07[~T] ~C0E[INFO] ~C07Body P: ~C0B"..sBodyPart.."~C07")
  ODS("~C07[~T] ~C0E[INFO] ~C07Health: ~C0B"..string.format("%2.1f", fActorHealth*100).."~C07")
  ODS("~C07[~T] ~C0E[INFO] ~C07Weapon: ~C0B"..sWpn.."~C07")
--]]
end

 

 

 

 

П.С. Если будут проблемы - пишите, помогу отладить.

 

Прежде всего - спасибо за x-ray extensions и справочник по функциям! Трудно представить сколько времени и усилий было затрачено.

 

Вопросы:

1. При использовании оператора ... у меня выдавалась ошибка по типу no such operator defined. Пришлось использовать table, хотя во многих скриптах этот оператор используется. Чем может быть вызвано?

2. Если будет время, напиши небольшой how-to по xray extensions (r18). Видел люди выкладывали то что извеснто на данный момент, но там далеко не все.

 

 

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

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


Ссылка на сообщение
Функция exec принимает аргументом sName, по наличию которого вызывается функция assert, но в нее передается неопределенный аргумент sFilePath

 

Пардон, невнимательно повырезал функции Luacap - у меня sFilePath назначается в ExpandPath(sName), потом идет проверка на существование файла FileExists(sFilePath)

 

Андрей

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


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

xStream

И вообще, лучше б отписался по сендбоксу. У тебя критика конструктивная получается. :-P

Полностью с тобой согласен, бери пример :P

 

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

 

Artos

Насчет проблемы с оператором '...' - что-то непонятно..

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

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


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

Ну то что не пропадут даром - не сомневайся ^_^. Лично для себя вижу много чего интестного, как и для мода, так и просто поучиться. Поэксперементирую, напишу фидбэк подробней.

 

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


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

xStream

Вчера наконец нашел время пограть с sandbox/timers, вот небольшой фидбэк:

 

1. В таймерах строка

local self.trigger_time = time_global() + time_seconds * 1000

 

изменил на (убрал local)

self.trigger_time = time_global() + time_seconds * 1000

 

2. Можно-ли регистрировать функцию калбэка находящююся в классе? Хотел инкапсулировать определенный функционал в классе, но не срабатывает регистрация типа:

event("actor_hit"):register(sample_class:on_actor_hit)

 

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

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

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


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

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


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

Artos забыл добавить required brain.script в начале модуля, от того и происходит :P - под снос, plz

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

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


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

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


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

Перечитав тему, примерно так представляются требования для универсального хранилища:

1. У каждого bindable объекта есть метод save(…). Метод знает как проверять и сохранять данные в зависимости от типа (number, table, etc…).

2. Данные сохраняются в специально созданный объект хранилище. Pstor не используется (оставим для оригинального функционала)

3. По мере надобности (лимит 8К?) создаются дополнительный(е) хранилища (transparent to end user) и присоединяются к родительскому объекту наравне с уже созданным

4. Объект хранилище содержит информацию про тот объект которому принадлежит (parent object)

5. Данные можно получить методом obj:read() который пройдет по всем созданным хранилищам данного объекта и считает нужную информацию.

6. Есть некий универсальный (ни кому не принадлежащий) объект с методами save() and read() который можно использовать в случаях когда нет логического родителя – примерно то что сделала xStream, как я понял.

 

Если что не так выразил поправьте и дополните - пытаюсь разобраться с хранилищами

 

П.С. Сервер глючит не по детски

 

 

 

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

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


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

malandrinus,

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

mt.__call = self.method_to_call

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

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


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

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

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


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

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

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


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

Что если проверять при каждом перемещении в хранилище на этапе разработке/тестировании - по идее, все 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

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


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

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


Ссылка на сообщение
Есть нечто, объявленное как class "XXX" (YYY). Чем является XXX? Биндером, наследником, чем-то другим?

Чем-то другим - метатаблицей ;). Вообще, это напоминает спор о венгерке... Жаль что в том обилии полезностей, что выложила xStream ты усмотрел только лишь названия классов.

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


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

xStream,

Поясни работу проверки зависаний (hangCheck()). Попытался вывести в лог сообщение о зависании, но так и не получилось, хотя насиловал код и саму игру как только мог.

Колбэки зависали, это видно по выводам в дополнительную консоль из luacap алпета. Вызов поставлен в top level event trigger – на пример в

actor_binder:on_item_use(itm)

идет следующий вызов

event(”item_use”):hangCheck():trigger({itm = itm})

, затем в методе подписчике выставляю

e:setFingerprint(”on_item_use is hanging blah blah….”)

. Сами ивенты работают прекрасно.

 

Также, по версиям файлов – так как работа над всем этим хозяйством продолжается, почему бы тебе не использовать github.com для source control. Очень удобный интерфейс, простота использования, все доступно онлайн, подсветка изменений версий кода, apps for iphone/ipad/android/etc… Люди заинтересованные в модифицировании имеют возможность сделать так называемую ”вилку” (fork) твоего кода в свою репозитори, при этом будет видно откуда была сделана вилка (кто оригинальный разраб). Ну и тд и тп ☺

 

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

malandrinus

У меня своя реализация, я её активно использую
- выкладывай :). Перепробовал уже с пяток разных методов, ни один не нравится, последнее на чем остановился это то с чего начал (1/uninitialized). С assert к сожалению ты прав, не всегда срабатывает, хотя наиболее изящный, на мой взгляд, способ.

П.С. Поддерживаю Monnoroch, только немного наоборот: strict_subscribe с вылетами, а subscribe, если уже подписались, просто выводит в лог сообщение (чтоб не гадать почему колбэк не срабатывает) и нечего не делает.

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

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


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

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