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

Язык 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
Ссылка на комментарий

Взглянул на скрипт xs_netpk.script (пока беглым взглядом).

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

Полностью согласен с KD87, в том, что ACDC в таком аспекте как скрипт для работы с нет-пакетами в игре - базовое руководство. В нем (ACDC) совершенно не учитываются параметры, которые в игре или добавляются к 'базовым' или даже изменяют тип данных, как например, 'job_online_condlist' для сталкеров/монстров. Т.о. достоинство 'делать в формате ACDC' (к сожалению по подустаревшей и не универсальной версии) без учета особенностей онлайн-игры - сводит многое на нет, и только tail_data будет спасать от крахов/коллизий в игре, возвращая табличку нет-пакета с 'бинарным куском' в конце.

Пока мое резюме: Перепев на новый лад с дополнениями прежнего amk.script/xrs_utils.script.

Если нужна (а вдруг ...) конструктивная критика/замечания, то готов и ее дать, хотя ... врядли это востребовано, т.к. задевает ЧВС.

 

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

 

(ИМХО)

 

KD87, по skeleton_flags ты имеешь ввиду что-то типа этого(?):

  tP.skeleton_flags = oPs:r_u8()
  --[[ --/#?#
  if oPu and tP.upd and bit_and(tP.skeleton_flags,4) == 4 then --/ 3-ий бит
    tP.upd.bones_mask = oPu:r_u64()
    tP.upd.root_bone  = oPu:r_u16()
    tP.upd.ph_angular_velosity = Get_Chunk({},oPu,3,'r_s32') --/#?# vector
    tP.upd.ph_linear_velosity  = Get_Chunk({},oPu,3,'r_s32') --/#?# vector
    tP.upd.bone_count = oPu:r_u16()
    for i=1,tP.upd.bone_count do
      local t = {}
      t.ph_position = Get_Chunk({},oPu,3,'r_u8') --/#?# q8v3
      t.ph_rotation = Get_Chunk({},oPu,4,'r_u8') --/#?# q8v4
      t.enabled = oPu:r_u8()
      tP.upd["bone_"..i] = t
    end
  end
  --]]
  tP.source_id      = oPs:r_u16()

и для каких классов объектов? Для cse_alife_object_physic или же и для всех/других?

Или все же что-то с num_items для cse_alife_object_physic перемешалось?

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

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

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

Хочу немного написать на тему сабжа топика. Несколько раз задавали вопросы о том, как выполнить (к примеру с помощью той же самой песочницы) метод объекта или функцию, хранящуюся в таблице (тоже некое подобие объектов). Точнее - как передать куда-то "ссылку на вызов". Попробую описать на примере, как это сделать просто и без заморочек. Для начала напишу код, который потом разберу (внимание, код написан так, чтобы можно было просмотреть в "чистом" ЛУА, например по этой ссылке - http://www.lua.org/cgi-bin/demo, этот сайт представляет собой обертку для ЛУА-интерпретатора, что лично мне очень помогает отлаживать разный код):

math.randomseed( os.time() )

local mega_object = {
    execute = function (self, max_val)
        return math.random(1, max_val or 10)
    end
}

local mega_object2 = {
    execute = function (self)
        return "non random; string"
    end
}

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

local captured_closure1 = object_execute(mega_object)
local captured_closure2 = object_execute(mega_object2)
local val1 = captured_closure1(1000)
local val2 = captured_closure2(100)

io.write( tostring(val1).."\n" )
io.write( tostring(val2) )

Итак, разбор по строкам:

1 - инициализация генератора псевдослучайных чисел.

3-7 - создаем "объект" с функцией execute, которая генерирует число в диапазоне от 1 до значения параметра, переданного в функцию. Внимание! Первый параметр я назвала self, так как данную функцию я буду вызывать через ":", а не через "." (см. ниже), то есть, по сути так, как работает стандартно вызов методов - первым аргументом передается ссылка на сам объект, у которого метод вызван.

9-13 - аналогично, но создаем объект, который делает в методе execute другую работу - возвращает строку.

 

Итак, есть два объекта со схожим интерфейсом, как сделать так, чтоб можно было передать в виде одной переменной "вызов метода объекта"? mega_object2.execute, например, является функцией, но при таком вызове не передается контекст - self, что очень важно, когда надо вызвать именно как метод объекта, а не статическую функцию.

Дальше начинается "магия ЛУА", которая позволит "обернуть" обращение к методу объекта в функцию, которую можно записать в переменную или передать куда-то в качестве параметра.

 

15 - начинается код функции (на вход передается объект, который будем "оборачивать"), результатом работы которой станет... другая функция! Функция, которая будет вызывать нам нужный метод у нужного объекта.

16 - создается ЛОКАЛЬНАЯ переменная, которая будет хранить ссылку на наш объект. То, что она локальная, означает, что при каждом выполнении object_execute, будет создаваться новая переменная. Это нужно для...

17 - ... локальной функции _call, которая собственно и вызывает нужный метод execute. То, что мы сейчас сделали, называется замыканием - переменная _proxy будет существовать до тех пор, пока существует функция _call, которая эту переменную использует.

 

У нас получилась матрешка из функций.

 

18 - возвращаем результат работы метода объекта

 

20 - функция object_execute возвращает функцию _call

 

Зачем? А) эта функция при выполнении object_execute каждый раз получается уникальная, переменная _proxy хранит конкретный объект и не может быть перезатерта при следующем вызове object_execute, потому что создается каждый раз заново, а старая остается связана с функцией _сall, созданной при прошлом вызове object_execute. Б) Функция _call оборачивает вызов метода. Таким образом получаем цепочку: object_execute возвращает функцию, которая выполняет метод объекта. Понять сразу сложно, конечно же, поэтому примеры:

 

23 - создаем переменную, в которой будет лежать "вызов метода execute объекта mega_object".

24 - аналогично, только получаем обертку вокруг другого объекта.

 

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

 

25-26 - как раз выполняем функции-обертки и получаем результат работы методов у разных объектов.

28-29 - вывод результата

------------------------------------

Итак, итог: ЛУА позволяет делать "финты ушами" и довольно просто организовывать работу с методами объектов, передавать их ("ссылки на метод", на деле обертку, но разницы нет) куда угодно, присваивать и т.п.

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

Возникает закономерный вопрос - зачем такие сложности? Почему не вынести это во внутренности какой-нибудь подсистемы и она бы все делала автоматом. Ответ прост: система не сможет организовать вызов через ":" простым путем, придется громоздить огромное количества кода... Это раз. Такие ситуации, на самом деле, встречаются гораздо реже, чем обращение к "обычным" функциям, которые регистрируются один раз за игровую сессию. Это два. И, наконец, три: никто лучше автора не опишет, что и как там должно происходить при вызове метода. Ничто не мешает не просто вызывать метод нужного объекта, а еще и выполнить какую-то дополнительную работу. Может даже в зависимости от условий, вообще не выполнять. Например, сработал колбек, в нем работа со сталкером, а он к этому моменту оказался мертв. Мы можем сделать соответствующую проверку и отписаться от события.

Последние два абзаца - применение на практике. Но в целом, данная, гм, статья(?), показывает такие возможности ЛУА, как замыкания на живых примерах и в живых ситуациях (вызов методов с сохранением self и делегирование этих "вызовов")

------------------------------------

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

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

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

Ссылка на комментарий
*Shoker*,
...необходимость наличия актёра в онлайне...
Поступи как я предлагал xStream и необходимость наличия ГГ отпадет :ny_thumbsup:
Ссылка на комментарий

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

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

Ссылка на комментарий
xStream, в принципе - ты права, но ведь могут быть и исключения, в смысле мб понадобиться загрузить что-то, когда актора еще нет, чем вешать загрузку всего на спавн актора, почему-то нет-пакеты для актора грузятся раньше самого актора(в смысле коллбэк actor_binder:load(reader) вызывается раньше спавна актора), это уже как посмотреть, ты пишешь как тебе удобно... а если руки не кривые, то тот, кому надо грузить все, до того, как актор заспавнится, сможет сделать для себя загрузку через этот коллбэк... Изменено пользователем Viнt@rь
Ссылка на комментарий

Viнt@rь, а вот в этом поддержу выбранную позицию xStream.

Делать единое хранилище для всего и вся (ИМХО) неразумно. Имеются все же различные данные, которые востребованы и в различное время и в различных ситуациях и в различных целях.

Хранилище о котором говорит xStream - расширитель именно pstor'a актора, снимающий ограничение на размер (~8 кБ для SHOC). Вполне можно хотеть запихнуть в него и все остальное, но ... зачем пытаться в одну корзину класть все яица?

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

Хранить огромные списки/тексты, типа "записки сталкера", в которые игрок может понапихать все чего захочет (если не ограничить ему объем) - вполне можно потрудиться и расширить функционал внешними расширителями до записи в 'пользовательские' файлы (*ltx и иже).

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

Хотя ... сам никак не подберу варианта для достаточно простого хранения именно начальных/стартовых настроек (типа доп. опционала), когда возникает необходимотсь до начала игры (соответственно до спавна в нее актора) добавить/изменить именно внутри-игровые настройки/параметры. И хотелось бы это делать не сторонними расширителями, а на ресурсах движка/игры и не засоряя 'user.lx'.

 

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

xStream, можно попросить привести тут наметки конфига для объекта с произвольным объемом нет-пакетов (до готовности всего модуля по расширенному pstor'у)?

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

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

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

Artos, я и не говорю ничего по пстору, в этом я полностью согласен с xStream, я говорю о обязательности наличия актора...

А по поводу пстора, конкретно: куда сохраняется все из него? в сейв? есть ли у него ограничения? и как замена стандартного ПЫС-овского пстора повлияет на игру

Изменено пользователем Viнt@rь
Ссылка на комментарий

Viнt@rь, сорри, но я из контекста по расширенному хранилищу высказанному тут, воспринимаю его как в-первую очередь расширитель именно акторского pstor'а, а это как раз и обуславливает наличие актора в игре/онлайне. Ведь pstor для любого игрового объекта - одна из субтаблиц, которые создаются для объектов в db.storage и, если их даже создать ранее - придется перелопатить коды, дабы не затирались преждевременно созданные 'штатными' скриптами оригинальной игры.

 

P.S. (ох что-то лагает сервер форума) Pstor'ы всех забинденых объектов конечно же сохраняются в сэйвы (см. в xr_logic.script) и размер всего суммарного нет-пакета для объектов как раз и имеет ограничение в 8 кБ (SHOC). Хотя ... вероятно все же есть вариант для расширения этого лимита (жду ответа от xStream). Возможно какие-то технические классы имеют бОльший диапазон ...

 

P.P.S. И я так понимаю, что речь НЕ идет о замене акторского (ПЫС'овского) pstor'а, а именно о его расширении. Т.е. 'штатные' коды/движек/... могут продолжать писать в прежний, а 'модовские' данные можно перенаправлять в удобный - штатный или расширенный.

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

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

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

Artos, смотри как получается, если вызывать загрузку все и вся из пстора актора одновременно с загрузкой нет-пакетов(а это актора еще нет), мы просто будем передавать обьект биндера, а именно bind_stalker.script(обьектом которого и есть тот самый актор) при спавне актора вызывается всеголишь другой коллбэк, а почти вся загрузка, происходит при загрузке нет-пакетов...

 

P.S. как я понял, ты даже не берешь Ид актора функцией, а сам назначаешь ему Ид...

Изменено пользователем Viнt@rь
Ссылка на комментарий

Viнt@rь, вот тут ты заблуждаешься.

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

Только когда серверная копия прочитает весь нет-пакет актора - появится в игре полный серверный объект актора а не его куций абстракт ...

Только когда забинденый (созданный биндером шаблон) клиентский объект актора считает свой pstor - в игре появится полноценный актор.

Можно конечно сохранять данные не в pstor'е, а в custom-data актора - тогда можно ускорить доступ к данным, но ...

 

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

ты даже не берешь Ид актора функцией, а сам назначаешь ему Ид...

И собственно и в назначении ничего зазорного нет, т.к. все одно он будет первым (ID == 0), и в моде все же (дань анахронизмам) при бинде актора (actor_binder:reinit -> _G.idActor = self.object:id()) его уже назначенный ID корректируется с полученным от сервера (хотя это и явно излишне).

И, кстати, обрати внимание на код самих разработчиков в том же _g.script:

--' Проверка на инфопоршны, даже если игрока не существует
function has_alife_info(info_id)
    local aa = alife()
    if aa == nil then
        return false
    end
    return aa:has_info(0, info_id)
end

- 0 - акторский ID, и подобное еще есть в ПЫС'вcких кодах.

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

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

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

http://dl.dropbox.com/u/46539648/xs_scripts2.rar

Собирала второпях. Хранилище и таймеры.

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

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

xStream, спасибо, сейчас покопаемся...)

Artos, спасибо за разъяснение)

 

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

Ожидал бОльшего ... чем код интерфейса для доступа к хранилищу.

По сути все то, что так или иначе уже используется в модах: спавн объекта для хранения (кто флешу, кто кпк, ... иль ящик), хранение (запись/чтение) данных в кастом-дате созданного объекта. Ограничение (<8 кБ) как было - так и осталось (для одного объекта), как понимаю ... :-( И даже более - доп.потери на перевод в строковый формат для кастом-даты.

Если класс этого (эксклюзивного) объекта зарегистрировать в class_registrator.script и дописать стандартные методы, то ... можно ловить его серверную копию и забирать данные методами из созданного серверного класса в момент 'on_register' иль даже 'on_before_register', т.е. не дожидаясь спавна актора в игру.

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

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

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

xStream, Итак, по поводу таймеров)) в принципе я похожими и пользуюсь взял из АМК 2.0 лаунчер(наверно я тебя достал этим АМК, ну извини если что))) я не специально) и немного его доработал(в плане сейва/загрузки функций, стандартное не работало...)

[spoiler=Вот код, если интересно:)]

--[[ ----------------------------------------------------------------------------------------------- 
File       : _s_launcher.script
Description: лаунчер функциий
Copyright  : 2011 © Spectrum project
Author     : © AMK Team 2.0(2009), Refresh
Last edit  : 23.12.2011 (Viнt@rь)
--]] -----------------------------------------------------------------------------------------------
--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-- *                                CSpLauncher                                  *
--* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
class "CSpLauncher"
function CSpLauncher:__init()
    self.aLauncher = {}
    self.iTblSize  = 0
end

--[[
--    SaveData(oActor)
--    Сохранение игры. Сохранение переменных.
--    @param    object    oActor    Обьект актера.
--]]
function CSpLauncher:SaveData(oActor)
    local fId = 0
    for sName, aFunc in pairs(self.aLauncher) do
        local iVal = aFunc.oTime:diffSec(game.get_game_time())
        fId = fId + 1
        
        WriteVar("sName"..fId,sName)
        WriteVar("sValue"..fId,aFunc.sValue)
        WriteVar("iVal"..fId,iVal)
    end
    WriteVar("cFuncs",fId)
end

--[[
--    LoadData(oActor)
--    Загрузка игры. Загрузка сохраненных переменных.
--    @param    object    oActor    Обьект актера.
--]]    
function CSpLauncher:LoadData(oActor)
    local cFuncs = xr_logic.pstor_retrieve(oActor, "cFuncs", nil)
    if cFuncs then
        for i=1,cFuncs do
            local sName = ReadVar("sName"..i, nil, oActor)
            local sValue = ReadVar("sValue"..i, nil, oActor)
            local iVal = ReadVar("iVal"..i, nil, oActor)
        
            self:AddFunc(sName,sValue,iVal)
            
            DelVar("sName"..i,oActor)
            DelVar("sValue"..i,oActor)
            DelVar("iVal"..i,oActor)
        end
    end
end

--[[
--    UpdateFuncs()
--    Апдейт на вызов функций.
--]]    
function CSpLauncher:UpdateFuncs()
    if self.iTblSize < 1 then return end
    
    for sName, aFnc in pairs(self.aLauncher) do
        if aFnc.oTime:diffSec(game.get_game_time()) <= 0 then
            _log("CSpLauncher:UpdateFuncs:=Func:[%s] diffSec(%s):", sName, aFnc.oTime:diffSec(game.get_game_time()))
            local oFunction = loadstring(aFnc.sValue)
            self:DelFunc(sName)
            oFunction() --exemple    
        end
    end    
end

--[[
--    AddFunc(sName, sValue, iSeconds)
--    Добавление функции в лаунчер.
--    @param    string    sName    Метка функции, произвольное название.     
--    @param    string    sValue    Строка запуска функции.
--    @param    integer    iSeconds    Время для таймера, в игровых секундах.    
--]]    
function CSpLauncher:AddFunc(sName, sValue, iSeconds)
    if iSeconds == nil then
        iSeconds = 0
    end
    
    if not self.aLauncher[sName] then
        self.aLauncher[sName] = {}
        self.aLauncher[sName].sValue = sValue
    
        local oIdle = game.CTime()
        oIdle:setHMSms( 0, 0, iSeconds, 0)
        self.aLauncher[sName].oTime = game.get_game_time() + oIdle
    
        self.iTblSize = self.iTblSize + 1
    end
end

--[[
--    DelFunc(sName)
--    Удаление функции из лаунчера.
--    @param    string    sName    Метка функции, произвольное название.             
--]]    
function CSpLauncher:DelFunc(sName)
    if self.aLauncher[sName] then        
        self.aLauncher[sName] = nil
        self.iTblSize = self.iTblSize - 1
    end
end

пример использования:
_s.oSpLauncher:AddFunc("TimerSleep", "_s.oSpSleep:TimerNeedSleep()", 360)

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

и схема требует доработки:

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

2. в плане апдейта, так как видно не вооруженным глазом, что цикл "голый", что грузит игру...

 

 

 

ЗЫ пока только покопал по теме таймеров, сейчас буду дальше разбираться :)

 

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

По поводу универсального хранилища, с одной стороны интересно, а с другой стороны, Artos походу прав...

 

ЗЫЫ а че все молчат?:)

Изменено пользователем Viнt@rь
Ссылка на комментарий

Viнt@rь, прав в чем?...

....

Маленькая ремарка. Все, что я здесь выкладываю, не претендует на мега инновацию. Это просто реализации идей, которые я начала 2-3 года назад, но не доделала по тем или иным причинам. И сделано это сейчас совсем иначе, чем я сделала бы тогда. Сделано буквально на коленке в приступе ностальгии в праздничные дни. Выкладывается это именно в этом топике в первую очередь как пример другого подхода к программированию, на ЛУА в том числе. Проблема в следующем: говоришь абстрактные примеры, просят дать жизненный пример, приводишь пример, говорят - а у нас это есть и работает. А ведь вопрос не в том, что есть и работает, а в том, КАК работает. Об этом топик. Да, конечно, примеры живые и используются в живом коде (Artos, кстати, про код говорят в единственном числе, про тот, что пишут программисты. Коды - это могут быть, например, пароли, шифры и т.п. Так, оффтоп), но помимо практики присутствет и теория, про которую тут же забывают. Не спрашивают, что это и как, а спрашивают примерно "а чем это лучше того, что есть, докажите". Никто ничего никому не докажет - это отдельный подход. Кому-то нравится, кому-то нет. Проблема в том, что никому это здесь оказалось не надо. Либо те, кому надо, сидят и молчат :)

...

Кратко по скриптам.

 

Хранилище - ничего революционного. Просто созданы объекты, которые занимаются только этой задачей. ПДА пусть остаются ПДА, а хранилище - хранилищем. Хранить в кастом-дате имеет смысл небольшой объем информации, связанный непосредственно с тем объектом, который информацию хранит. Ничего преступного не вижу и сама это использую (хранение в кастом-дате). Хранилище же абстрактное, для каких-то вещей, не привязанных ни к каким игровым сущностям. Надеюсь, эта идея понятна. Что же касается on_register и тому подобных колбеков, то обычно на этом этапе еще нет надобности что-то выполнять, игра еще не началась. Но гораздо более существенным является то, что пользуясь такими методами, мы не знаем, все ли в хранилище у нас загрузилось или не все - объектов, которые хранят информацию - несколько. Поэтому именно спавн актора - колбек срабатывает одним из первых после процесса регистрации всяческих объектов. Друго дело, что на on_register можно повесить поэтапное заполнение хранилища, но поскольку загрузка идет один раз, то старый-страшный метод перебора тут тоже вполне работает. Следующее - интерфейс. Прост до безобразия, две функции. Собственно и говорить тут не о чем. Тут, кстати, как я поняла, почему-то решили, что ограничение на хранилище - 8кб. Не на хранилище, на одно значение в нем. Если не так поняла, то ок.

 

Таймеры. Тоже абсолютно ничего революционного, кроме такой "маленькой" детали, что таймеры - это объекты. То есть их можно передавать по ссылке и отслеживать их состояние. Если вспомнить самую первую реализацию из АМК, то функция стартовала таймер, потом отследить за его жизнью было тяжело, в хранилище они представлялись пачкой переменных и т.п. То есть основная идея - таймер, как объект, несущий в себе (ООП, как никак) всю необходимую информацию. Вместо вызова функции с 1001 параметром, получаем объект, параметры которого можно менять во время жизни этого объекта. Это важно, на мой взгляд. (Не столько применительно к таймерам, как вообще к идеологии объектов).

 

Пакеты. Как заявили "то же самое, что было в АМК". А покажите-ка мне не то же самое? Это одна и та же техника. Только, опять же, подход другой. Вместо того, чтоб вызывать пачки функций, и таскать тот объект, к которому эти функции применять, создается отдельный объект, который знает, к чему применять, хранит информацию в себе. Его, этот объект, тоже удобно можно передавать как параметр в функции, например, не озадачиваясь тем, чтоб таскать параметром и еще тот объект, к которому это было применено. Наглядный пример - хранилище. Создали объект, а он уже в себе и ссылку на серверный объект содержит и нужный инструментарий. Код становится лаконичнее, интерфейсы - проще. Вот, к примеру, был код для оффлайн-алайфа:

        if IsMonster(obj) then
            local tbl = amkII_rdpk.amkReadMonster(obj)
            tbl.iHealth = health
            tbl.iUpdHealth = health
            amkII_wrpk.amkWriteMonster(tbl, obj)
        elseif IsStalker(obj) then
            local tbl = amkII_rdpk.amkReadStalker(obj)
            tbl.iHealth = health
            tbl.iUpdHealth = health
            amkII_wrpk.amkWriteStalker(tbl, obj)
        end

а стал

        local pk = xs_netpk.monster(obj)
        if not pk:isOk() then
            pk = xs_netpk.stalker(obj)
        end        
        local data = pk:get()
        data.health = health
        data.updhealth = health
        pk:set(data)

Если в первом случае от условий зависит и вызов проверки и выбор двух(!) разных функций для работы с пакетами, то во втором, получив нужное, интерфейс одинаков. Более того, если передать объект и не монстра и не сталкера, второй фрагмент отработает как ни в чем не бывало, никаких ошибок, падений и прочего. Пользователь библиотеки защищен от ошибок. Ему не дадут сделать то, что не предусмотрено. И опять акцентирую - объект. Один раз получив пакет, нам уже наплевать на исходный объект obj. Ссылка на него сохранена и не "потеряется". И еще, тут прозвучало, что сложно и громоздко. Неправда - интерфейс простой. Для конечного пользователя-программиста он очень прост. Все то обилие кода - реализация структур, идентичных структур в движке, что позволяет снизить процент ошибок.

 

Хелперы. Ну тут, думаю, ясно. Из нюансов - расширение функционала стандартных библиотек. И трансляция важных вещей в глобальную область видимости, чтобы вызовы нужных вещей были покороче, чем xs_helpers.trim, например. Станет просто trim, доступное везде, во всех скриптах. Или сериализация таблиц. Ее можно запускать двумя способами (как раз писала про оператор ":"): table.serialize(tbl), а можно tbl:serialize(). Или tbl:clone() - получим клон таблицы, а можно table.clone(tbl). Это уже фокусы самого языка ЛУА. Вообще, очень удобно помнить о таком поведении оператора ":".

 

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

 

Итог сей длинной простыни: ООП имеет право на жизнь и в модострое сталкера, только надо понимать его.

На эту тему, спасибо Viнt@rь, я как раз попробую пояснить про псевдо-ООП.

Viнt@rь приводил код из АМК. Не важно, кто автор, что там делается, главное - как. Как вы все знаете, скрипты при загрузке помещаются в отдельный неймспейс, переменную, имеющую имя этого скрипта, все что в скрипте описывается, доступно через эту переменную. Это поведение очень похоже на так называемый синглтон - образец класса (в данном случае не класса, а сущности другой - скрипта) в программе присутствует в единичном числе. Даже если сделать символических ссылок, объект остается тот же. Так вот. Есть скрипт, по сути - синглтон. Внутри описывается класс, создается образец класса, тоже в единственном числе и.... все. Используется. Вопрос - а зачем нужен такой класс тогда? Я могу смело убрать имя класса из функций,вынести содержимое конструктора просто в тело скрипта, а в самом начале скрипта обозначить local self={}. После этого я смогу делать все то же самое и работать оно будет точно так же. Только вызовы функций будут не через ":", а через ".". Но суть не меняется. Я даже выигрываю - чтоб сделать что-то, мне не надо создавать инстанс объекта, я просто сразу вызову нужную функцию. В результате вопрос остается открытым - зачем класс? Зачем объект этого класса?

Второй пример псевдо-ООП (есть скрипт переодевания неписей, вроде оттуда же). Все как и в первом примере, только НИГДЕ не используется self, конструктор пустой, в методах логика, которая проверяет объект, который дают на вход, но хуже всего - таких объектов куча, на каждого непися по штуке. Правильнее делать иначе - в конструкторе передавать объект, и вызывать метод, не указывая, что проверять - объект "проверяльщика" сам будет знать, что проверять. А еще правильнее - оставить логику как есть, ибо она простая до безумия, и избавиться от класса. В результате получим две простых функции.

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

---------------------------

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

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

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

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

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

 

По хранилищу:

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

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

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

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

Но вот как раз по недостаткам:

Понятно, что можно писать и по старинке, проверенными временем вариантами, но(!) мы же в топике по программированию, поэтому:

1. Использование перебора всего диапазона игровых идентификаторов и выборка объектов по их секциям - все же уже достаточный анахронизм.

Ведь зарегистрирован же новый класс с "CUST_ST" -> clsid.custom_storage и гораздо оперативнее идентифицировать объекты по именно нему, эксклюзивному числовому признаку.

Вообще зачем перебирать объекты на старте? Они сами регистрируются (уже было упомянуто про on_register) и могут сами себя добавлять в читалку для хранилища.

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

Да и по большому счету, не обязательно читать отдельными читалками - определив в STATE_Read/STATE_Write наличие уже не фейкового идентификатора (65535), а реального (любого иного) - можно прямо из этого пакета обьъекта считать все данные, не занимаясь этим далее. (но это уже для тех кто в теме ...).

 

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

 

А вот по пакетам:

Тут нареканий побольше. И что-то не встречалось "заявили "то же самое, что было в АМК".", а было произнесено: "Перепев на новый лад с дополнениями прежнего ...", что далеко не одно и то же.

Что режет глаза на фоне новшеств:

1. Достаточно "грязное" форматирование кодов/строк, с пропущенными начальными/конечными пробелами/табуляторами, перемежение пробелов/табуляторов, отсутствие или наличие пробелов между переменными/операторами/значениями ...

Все же, если следовать чему-то одному - то и придерживаться этого.

2. На фоне выборки из таблиц (по clsid'ам) соседствуют и цепочки if ..., что при добавлении, например, тех же классов для аномалий из модов, только удлинит эти цепочки.

3. (спорно, но упомяну) Почти прежняя амк-гирлянда из функций типа r_q8v/r_l32u16v(/... или это дань соответствия имен функций и типов из ACDC? Но ничего не мешает создать табличку соответсвий.

4. О том, что нужно "проходиться рашпилем" говорил и имел ввиду в первую очередь достаточное кол-во нестыковок или невостребованностей из ACDC и для модуля работы с не-пакетами в игре. Если уж в некоторых сулчаях все же подставлять некие дефолтные значения (как для inventory_box), то зачем оставлять '$editor'? что заведомо приведет в игре, например для вертушек, к проблемам ...

 

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

(если нужно - можно продолжить и по конкретным ошибкам/неточностям/упущениям/...)

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

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

Ну, про что я и говорила. Посрать на идею, давайте пробелы посчитаем. Мне, может, венгерку припомнить? :) Хехе.

Черт, я ж забыла, что тов. Артоса игнорирую. К сожалению в топике от него никуда не деться. По существу топика постов не вижу. Так что пока. Оставляю паству на ваше попечение, магистр ;)

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

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

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

Сделал пока в чистом lua. Ну сделал это несколько громко сказано. Тестировать нужно.

Описание :

Использование :

----------------------------------

Создание таймера :

------------ статический таймер -----------------

_timer:create(5,'Action1'[,0])

 

Создаётся таймер, который отработает 5 секунд (в Сталкере это значение будет равно соответственно 5000 мс)

По завершению этого времени вызовется функция Action1 (строка).

Action1 может быть как именем глобальной функцией, так и функцией модуля. Т.е. "module_name.function_name".

Опциональный пареметр [,0] задаёт частоту апдейта.

По умолчанию равен 0 секунд (в Сталкере думаю сделать это значение 1000 (мс - т.е. 1 сек.)

------------ динамический таймер -----------------

---- варианты ------

1) _timer:create('timer_1', 5)

 

Создаётся объект таймера, с именем 'timer_1' (строка), который отработает 5 секунд.

Он будет работать с частотой апдейта, установленной по-умолчанию.

Используется совместно с методами. См. ниже. Например :

if timer_1:action() then -- если таймер отработал (т.е. наступило действие), то...

 

2) = _timer:create('timer_1', 5, 2)

 

То же, что и первый вариан, но устанавливается частота апдейта

 

3) _timer:create('timer_1', 5, 'Action1')

 

Создаётся объект таймера с именем 'timer_1', который отработает 5 секунд.

По завершению времени вызовется функция Action1.

Он будет работать с частотой апдейта, установленной по-умолчанию.

Используется совместно с методами. См. ниже. Например :

timer_1:changeAction(NewFunction) -- установка новой функции-события

 

4) _timer:create('timer_1', 5, 'Action1', 2)

 

Создаётся объект таймера с именем 'timer_1', который отработает 5 секунд.

По завершению времени вызовется функция Action1.

Он будет работать с частотой апдейта 2 сек.

Используется совместно с методами. См. ниже.

----------------------------------

Методы таймеров (На первое время думаю хватит):

----------------------------------

create(...) -- Уже описано

stop()                                 -- Приостановка работы таймера

start()                                -- Запуск таймера после приостановки

getRemainTime()                   -- Получение оставшегося времени работы

changeAction(name_function) -- Изменение функции-события (имя функции - строка)

changeActionTime(sec)          -- Изменение времени срабатывания таймера (+number/-number в сек. - в сталке мс)

kill()                                    -- Удаление таймера

action()                               -- Проверка, отработал ли таймер. Возвращает true/false

setUpdatePeriod(period)          -- Установка периода апдейта таймера (сек. - в сталке мс)

isRunning()                           -- Проверка, запущен или остановлен таймер

save()                                 -- Сохранение таймеров

load()                                  -- Загрузка таймеров

 

-- restart() -- Перезапуск таймера (Пока выключено)

Кто заинтересуется и поможет довести код до ума - кроме кода и благодарности ничего не получит :)

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

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

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

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

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

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

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

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

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

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

Войти

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

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

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

AMK-Team.ru

×
×
  • Создать...