Dennis_Chikin 3 658 Опубликовано 4 Января 2015 Поделиться Опубликовано 4 Января 2015 (изменено) С чего начинать и где взять. Установка 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 Изменено 2 Марта 2015 пользователем Kirgudu Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Artos 99 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) Взглянул на скрипт 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 перемешалось? Изменено 8 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
xStream 86 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) Хочу немного написать на тему сабжа топика. Несколько раз задавали вопросы о том, как выполнить (к примеру с помощью той же самой песочницы) метод объекта или функцию, хранящуюся в таблице (тоже некое подобие объектов). Точнее - как передать куда-то "ссылку на вызов". Попробую описать на примере, как это сделать просто и без заморочек. Для начала напишу код, который потом разберу (внимание, код написан так, чтобы можно было просмотреть в "чистом" ЛУА, например по этой ссылке - 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 и делегирование этих "вызовов") ------------------------------------ Материал довольно сложный для понимания, мне кажется, поэтому жду вопросов и постараюсь объяснить нюансы. Отвечать на вопрос, "а нафига это ваще нужно" не буду, так как нафига - описано в самом начале. Раз люди спрашивают, значит вопрос действительно их интересует. Я же постаралась предложить решение. Оно не единственное, но, на мой взгляд, наиболее эффективное и простое - используются только особенности языка. Изменено 8 Января 2012 пользователем xStream Все, кто стоит на моем пути: идите нахрен и там погибните! © Ссылка на комментарий
Viнt@rь 50 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 *Shoker*, ...необходимость наличия актёра в онлайне... Поступи как я предлагал xStream и необходимость наличия ГГ отпадет :ny_thumbsup: GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
xStream 86 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 Хранилище без завязки с сейвом, на мой взгляд, не имеет большого смысла. А загрузка актора говорит о том, что игровая сессия началась. Или я что-то упускаю? Все, кто стоит на моем пути: идите нахрен и там погибните! © Ссылка на комментарий
Viнt@rь 50 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) xStream, в принципе - ты права, но ведь могут быть и исключения, в смысле мб понадобиться загрузить что-то, когда актора еще нет, чем вешать загрузку всего на спавн актора, почему-то нет-пакеты для актора грузятся раньше самого актора(в смысле коллбэк actor_binder:load(reader) вызывается раньше спавна актора), это уже как посмотреть, ты пишешь как тебе удобно... а если руки не кривые, то тот, кому надо грузить все, до того, как актор заспавнится, сможет сделать для себя загрузку через этот коллбэк... Изменено 8 Января 2012 пользователем Viнt@rь GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
Artos 99 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) Viнt@rь, а вот в этом поддержу выбранную позицию xStream. Делать единое хранилище для всего и вся (ИМХО) неразумно. Имеются все же различные данные, которые востребованы и в различное время и в различных ситуациях и в различных целях. Хранилище о котором говорит xStream - расширитель именно pstor'a актора, снимающий ограничение на размер (~8 кБ для SHOC). Вполне можно хотеть запихнуть в него и все остальное, но ... зачем пытаться в одну корзину класть все яица? Имеется и 'user.ltx' с его скриптовой обвязкой, и при потребности хранить некие данные до загрузки игры - вполне может быть рассмотрен как хотя и достаточно куцый, но все же расширитель. Хранить огромные списки/тексты, типа "записки сталкера", в которые игрок может понапихать все чего захочет (если не ограничить ему объем) - вполне можно потрудиться и расширить функционал внешними расширителями до записи в 'пользовательские' файлы (*ltx и иже). pstor все же более узкое имеет предназначение и в первую очередь для данных по самой начатой игре (ее состоянию на момент сэйва). Хотя ... сам никак не подберу варианта для достаточно простого хранения именно начальных/стартовых настроек (типа доп. опционала), когда возникает необходимотсь до начала игры (соответственно до спавна в нее актора) добавить/изменить именно внутри-игровые настройки/параметры. И хотелось бы это делать не сторонними расширителями, а на ресурсах движка/игры и не засоряя 'user.lx'. Добавлено через 13 мин.: xStream, можно попросить привести тут наметки конфига для объекта с произвольным объемом нет-пакетов (до готовности всего модуля по расширенному pstor'у)? Изменено 8 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Viнt@rь 50 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) Artos, я и не говорю ничего по пстору, в этом я полностью согласен с xStream, я говорю о обязательности наличия актора... А по поводу пстора, конкретно: куда сохраняется все из него? в сейв? есть ли у него ограничения? и как замена стандартного ПЫС-овского пстора повлияет на игру Изменено 8 Января 2012 пользователем Viнt@rь GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
Artos 99 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) Viнt@rь, сорри, но я из контекста по расширенному хранилищу высказанному тут, воспринимаю его как в-первую очередь расширитель именно акторского pstor'а, а это как раз и обуславливает наличие актора в игре/онлайне. Ведь pstor для любого игрового объекта - одна из субтаблиц, которые создаются для объектов в db.storage и, если их даже создать ранее - придется перелопатить коды, дабы не затирались преждевременно созданные 'штатными' скриптами оригинальной игры. P.S. (ох что-то лагает сервер форума) Pstor'ы всех забинденых объектов конечно же сохраняются в сэйвы (см. в xr_logic.script) и размер всего суммарного нет-пакета для объектов как раз и имеет ограничение в 8 кБ (SHOC). Хотя ... вероятно все же есть вариант для расширения этого лимита (жду ответа от xStream). Возможно какие-то технические классы имеют бОльший диапазон ... P.P.S. И я так понимаю, что речь НЕ идет о замене акторского (ПЫС'овского) pstor'а, а именно о его расширении. Т.е. 'штатные' коды/движек/... могут продолжать писать в прежний, а 'модовские' данные можно перенаправлять в удобный - штатный или расширенный. Изменено 8 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Viнt@rь 50 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) Artos, смотри как получается, если вызывать загрузку все и вся из пстора актора одновременно с загрузкой нет-пакетов(а это актора еще нет), мы просто будем передавать обьект биндера, а именно bind_stalker.script(обьектом которого и есть тот самый актор) при спавне актора вызывается всеголишь другой коллбэк, а почти вся загрузка, происходит при загрузке нет-пакетов... P.S. как я понял, ты даже не берешь Ид актора функцией, а сам назначаешь ему Ид... Изменено 8 Января 2012 пользователем Viнt@rь GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
Artos 99 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) 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ких кодах. Изменено 8 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
xStream 86 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 http://dl.dropbox.com/u/46539648/xs_scripts2.rar Собирала второпях. Хранилище и таймеры. Все, кто стоит на моем пути: идите нахрен и там погибните! © Ссылка на комментарий
Viнt@rь 50 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 xStream, спасибо, сейчас покопаемся...) Artos, спасибо за разъяснение) GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
Artos 99 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) Ожидал бОльшего ... чем код интерфейса для доступа к хранилищу. По сути все то, что так или иначе уже используется в модах: спавн объекта для хранения (кто флешу, кто кпк, ... иль ящик), хранение (запись/чтение) данных в кастом-дате созданного объекта. Ограничение (<8 кБ) как было - так и осталось (для одного объекта), как понимаю ... :-( И даже более - доп.потери на перевод в строковый формат для кастом-даты. Если класс этого (эксклюзивного) объекта зарегистрировать в class_registrator.script и дописать стандартные методы, то ... можно ловить его серверную копию и забирать данные методами из созданного серверного класса в момент 'on_register' иль даже 'on_before_register', т.е. не дожидаясь спавна актора в игру. Изменено 8 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Viнt@rь 50 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) 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 походу прав... ЗЫЫ а че все молчат? Изменено 8 Января 2012 пользователем Viнt@rь GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
xStream 86 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) 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, конструктор пустой, в методах логика, которая проверяет объект, который дают на вход, но хуже всего - таких объектов куча, на каждого непися по штуке. Правильнее делать иначе - в конструкторе передавать объект, и вызывать метод, не указывая, что проверять - объект "проверяльщика" сам будет знать, что проверять. А еще правильнее - оставить логику как есть, ибо она простая до безумия, и избавиться от класса. В результате получим две простых функции. Все эти доводы можно оспорить. Есть класс и работает. Но проблема в том, что своего прямого назначения такие конструкции, увы, не выполняют (если вспомнить, что такое классы, инкапсуляция, наследование и т.д.) --------------------------- Прошу прощения за простыню, но, думаю, она по сабжу топика. И надеюсь, что у кого-то в голове сработает понимание, в чем отличие этих двух разных подходов к программированию. И почему я вообще начала про все это говорить. И показывать примеры. Изменено 8 Января 2012 пользователем xStream Все, кто стоит на моем пути: идите нахрен и там погибните! © Ссылка на комментарий
Artos 99 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 Несколько неудобно неявно оппонировать, но раз это приводит к более спокойному обсуждению и высказыванию мнений, воспоьзуемя это формой. По хранилищу: Вероятно и авторитет автора, и заявленный им новаторский подход с барабаннаой дробью, и упоминание про расширение 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 - да. Но все же пакеты - не таймеры иль ивенты. (если нужно - можно продолжить и по конкретным ошибкам/неточностям/упущениям/...) "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
xStream 86 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 Ну, про что я и говорила. Посрать на идею, давайте пробелы посчитаем. Мне, может, венгерку припомнить? Хехе. Черт, я ж забыла, что тов. Артоса игнорирую. К сожалению в топике от него никуда не деться. По существу топика постов не вижу. Так что пока. Оставляю паству на ваше попечение, магистр Все, кто стоит на моем пути: идите нахрен и там погибните! © Ссылка на комментарий
Nazgool 250 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 Понятное дело что тягаться не выйдет, но может кто-то заинтересуется и кому-то понадобиться? Сделал пока в чистом 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 99 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 (изменено) Мда-а-а, похоже малейшая критика или указание на промахи/недостатки вызывает приступ ... отторжения и переходы на личность. Ну что же ... тоже оставляю топик, пусть остальные пообсуждают/повосхваляют. Изменено 8 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Ссылка на комментарий
Andrey07071977 18 Опубликовано 8 Января 2012 Поделиться Опубликовано 8 Января 2012 Gun, код то выложи Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти