Dennis_Chikin 3 661 Опубликовано 12 Ноября 2014 Начнем с простенького, но сразу в тему рефакторинга. Скриптик, где банально выдрана работа с нетпакетами из amk, поправлены ошибки древние, и максимальное количество функций развернуто. Делалось для максимальной скорости. Ни какого сложного апи - как видим, так и вызываем. Или тупо вставляем к себе в критичный участок кода тело нужной функции. Организованно от простых функций к более сложным. То есть, читается минимально осмысленная часть нетпакета, если этого мало - смотрим следующую функцию, которая читает по-больше, и, наконец, самую всеобъемлющую. Аналогично - с записью, хотя писать пакет надо целиком, но возможно кто-то какую-то часть хочет формировать своим кодом. https://dl.dropboxusercontent.com/u/27871782/amk_netpk.script В ШМ уже было, ну да пусть еще будет. Дополнения/исправления (я не Папа Римский, вполне могут быть и опечатки, и результаты копипаста нежданные) - приветствуются. И, да, кой-чего здесь не хватает. А вообще, конечно, в теме следовало бы расписать и теорию, и привести альтернативные варианты. Для альтернативных, поскольку там апи уже довольно развит, неплохо было бы и его описание подробное, и описание собственно внутреннего устройства всего этого дела. Дополняйте. Немножко истории: началось все с оружейного аддона Бака, который в соли тормозил изрядно. Вплоть до того, что приближаясь к группе из десятка сталкеров получали вполне ощутимый эффект "хромоты". При разбирательстве - что же там ТАК тормозит, оказалось, что буквально все, но нетпакеты вносят довольно весомый вклад. При этом код из аддона работал быстрее, чем код от Xstream, и оба - заметно быстрее amk (хотя в амк в принципе тот же экстримовский, но в нем больше кросс-вызовов). Соответственно, переписан именно чтобы выбросить все лишнее. Скорость, скорость, и еще раз скорость. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 22 Ноября 2014 (изменено) Гравипушка. На сей раз просто банальный рефакторинг. Но мало-ли, кто чего обсудить захочет, так что отдельной темой. И как пример к посту про физобъекты в справочнике по ФиК. Изменено 30 Января 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 22 Ноября 2014 (изменено) https://dl.dropboxusercontent.com/u/27871782/sleep_manager.script- сон. https://dl.dropboxusercontent.com/u/27871782/amk_timers.script- таймеры. А вообще таймеры не нужны. Сон - практически единственный случай, где использование оправдано. Да и тот - какой-то рудимент (перемещение через неизвестное количество минут после сна) от незнаю чего, для типа как бы "совместимости". Все навороты с 20 взамными вызовами из amk_mod.script - amk.script, из него опять amk_mod и т.д., повторить до полного фалломорфирования (зато там когда-то античит был, да) - тоже не нужны. Сносить к Етицкой Богомыши безжалостно. Разбор того, что внутри: http://www.amk-team.ru/forum/index.php?showtopic=13068&p=912220 Изменено 28 Января 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 4 Декабря 2014 (изменено) Изначально здесь вообще все было в одной теме - в ковырялке, потом еще создали "скриптование и спавн", который переименовался в просто "скриптование", и была где-то логика, но ее прикрыли. Логику до сих пор не восстановил по тому, что не понял, как разделить мух и котлеты.В общем, ковырялку лучше оставить для "ковырнул что попало, ничего не понял, прошу объяснить: что это было, где я, и зачем вообще тут все ? Кусок чегопопала прилагается."Скриптование - вот там всяких кровососов, типа последнего - вроде, будет нормально искать.А здесь мы займемся анатомированием крупной дичи. Вот у меня есть заказ, а взамен попробую кой-чего своего скинуть, которое в антисоветское подполье не совсем формат, да и держать его там - только протухнет. И не обязательно, но все же просьба: в начале поста оставлять одно-два слова для поиска. Ну, чтобы 20 тем не плодить, а в поиск ввел какой-нибудь "нетпакет" - и получил всю историю вопроса. Блин, все еще жду, пока это ненормальный движок не успокоится, и не прекратит над постами измываться. 8( Изменено 4 Декабря 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 4 Декабря 2014 (изменено) xr_logic, pstor, сохранение Итак, от меня небольшой взнос в "Прозекторскую", и будет заказ. Взнос - это про сохранение данных объектов через их pstor, для тех, кому "бездонные" от Артоса-ли, от XStream-ли - не совсем в кассу. Ну, например у меня куча всякого, что именно с pstor'ом, но одного пакета, тем не менее - за глаза. Переписывать все "вдребезги и пополам" - смысла не вижу. Кроме того, api и обертки - это все-таки трата ресуров. Поэтому - простенькие модификации уже имеющегося в xr_logic: pstor_store(), pstor_retrieve() и иже с ними: [/code]local pstor_number = 0 local pstor_string = 1 local pstor_true = 2 local pstor_false = 3 local pstor_custom = 4 local pstor_table = 5 local pstor_end_t = 6 local pstor_bool = 7 local pstor_types = { ["number"] = 0, ["string"] = 1, ["true"] = 2, ["false"] = 3, ["userdata"] = 4, ["table"] = 5, ["end_table"] = 6, ["boolean"] = 7 } function pstor_is_registered_type( tv ) return pstor_types[tv] ~= nil end -- Вернет ид актора, если переменной нет в таблице tabl_netpda_varnames -- быстрее (чтобы не перебирать таблицу) - только указывать запись конкретной переменной на ПДА function get_pda_id( vn ) return ( tabl_netpda_varnames[vn] and get_pda() ) or 0 end function pstor_store( o, vn, v ) -- log( "info", "pstor_store, %s, o: %s", vn, ( o and o:name() ) or "none" ) local st = db.storage[ ( tabl_netpda_varnames[vn] and get_pda() ) or ( o and o:id() ) or 0 ] if st then local pstor = st.pstor if not pstor then pstor = {} st.pstor = pstor end if pstor_types[ type( v ) ] then pstor[vn] = v else abort( "pstor_store, invalid type: %s [%s]", type( v ), vn ) end else abort( "pstor_store, no pstor for variable [%s] !", vn ) end end function pstor_retrieve( o, vn, def ) local pstor = db.storage[ ( tabl_netpda_varnames[vn] and get_pda() ) or ( o and o:id() ) or 0 ].pstor local r = pstor and pstor[ vn ] if r ~= nil then return r end return def end function pstor_get_custom( npc, cls, vn ) local pstor = db.storage[npc:id()].pstor if pstor then local v = pstor[vn] if v then return v.st end else pstor = {}; db.storage[npc:id()].pstor = pstor end local v = pstor_custom_impl[cls]() pstor[vn] = v return v and v.st end function pstor_save_tbl( t, pk ) pk:w_u8( pstor_table ) local p for k, v in pairs( t ) do if type( k ) == "number" then pk:w_u8( pstor_number ); pk:w_float( k ) else pk:w_u8( pstor_string ); pk:w_stringZ( k ) end if v == true then pk:w_u8( pstor_true ) elseif v == false then pk:w_u8( pstor_false ) else p = type( v ) if p == "number" then pk:w_u8( pstor_number ); pk:w_float( v ) elseif p == "string" then pk:w_u8( pstor_string ); pk:w_stringZ( v ) elseif p == "userdata" then pk:w_u8( pstor_custom ) if v.classname == k then pk:w_stringZ( "" ) else pk:w_stringZ( v.classname ) end v:save( pk ) elseif p == "table" then pstor_save_tbl( v, pk ) else abort( "pstor_save_all, not registered type: %s, k: %s", p, k ) end end end pk:w_u8( pstor_end_t ) end function pstor_save_all( npc, pk ) local pstor = db.storage[npc:id()].pstor if not pstor then pstor = {} db.storage[npc:id()].pstor = pstor end local n = 0 --for k, v in pairs( pstor ) do n = n + 1 end --pk:w_u32( n ) local p for k, v in pairs( pstor ) do n = n + 1 pk:w_stringZ( k ) if v == true then pk:w_u8( pstor_true ) elseif v == false then pk:w_u8( pstor_false ) else p = type( v ) if p == "number" then pk:w_u8( pstor_number ); pk:w_float( v ) elseif p == "string" then pk:w_u8( pstor_string ); pk:w_stringZ( v ) elseif p == "userdata" then pk:w_u8( pstor_custom ) if v.classname == k then pk:w_stringZ( "" ) else pk:w_stringZ( v.classname ) end v:save( pk ) elseif p == "table" then pstor_save_tbl( v, pk ) else abort( "pstor_save_all, not registered type: %s, k: %s", p, k, _util.to_str( npc ) ) end end end pk:w_stringZ( tostring( n ) ) pk:w_u8( pstor_end_t ) end function pstor_load_tbl( pk ) local t = {} local k, v, cls while true do k = pk:r_u8() if k == pstor_number then k = pk:r_float() elseif k == pstor_string then k = pk:r_stringZ() elseif k == pstor_end_t then return t else abort( "pstor_load_tbl, save file corrupted: %s (key type: %s)", _util.to_str( k ) ) end v = pk:r_u8() if v == pstor_true then t[k] = true elseif v == pstor_false then t[k] = false elseif v == pstor_number then t[k] = pk:r_float() elseif v == pstor_string then t[k] = pk:r_stringZ() elseif v == pstor_custom then cls = pk:r_stringZ() if cls == "" then v = pstor_custom_impl[k] else v = pstor_custom_impl[cls] end if v then v = v() else abort( "pstor_load_tbl, invalid custom: [%s], cls: [%s]", k, cls ) end t[k] = v v:load( pk ) elseif v == pstor_table then t[k] = pstor_load_tbl( pk ) else abort( "pstor_load_tbl, save file corrupted: %s (type: %s)", _util.to_str( k ), _util.to_str( v ) ) end end end function pstor_load_all( npc, pk ) local id = npc:id() local pstor = db.storage[id].pstor if not pstor then pstor = {} db.storage[id].pstor = pstor end -- local n = pk:r_u32() -- if ( n >= 11 ) and ( id ~= 0 ) then -- -- максимум 10 итераций: у неписей пишется 5 переменных, у техники - 7, пусть 10 будет -- -- если у вас в пстор что-то свое пишется, ориентируйтесь на свои значения -- -- и обязательно убираем из проверки актора - у него очень толстый пстор, и, к -- -- тому же, если уж поврежденным будет его пстор, то тут точно уже ничего не поможет -- if id ~= get_pda() then abort( "pstor_load_all, n: %s (%s)", n, _util.to_str( npc ) ) -- -- return remove_zavis.remove_obj( npc ) -- elseif n >= 1000 then abort( "pstor_load_all, pda, n: %s", n ) -- end end local k, v, cls local n = 0 while true do k = pk:r_stringZ() v = pk:r_u8() if v == pstor_true then pstor[k] = true elseif v == pstor_false then pstor[k] = false elseif v == pstor_number then pstor[k] = pk:r_float() elseif v == pstor_string then pstor[k] = pk:r_stringZ() elseif v == pstor_custom then cls = pk:r_stringZ() if cls == "" then v = pstor_custom_impl[k] else v = pstor_custom_impl[cls] end if v then v = v() else abort( "pstor_load_all, custom: [%s], cls: [%s], n: %s, (%s)", k, cls, n + 1, _util.to_str( npc ) ) end pstor[k] = v v:load( pk ) elseif v == pstor_table then pstor[k] = pstor_load_tbl( pk ) elseif v == pstor_end_t and tonumber( k ) == n then return else abort( "pstor_load_all, save file corrupted: %s, n: %s (%s)", _util.to_str( k ), n + 1, _util.to_str( npc ) ) -- не надо пытаться вылетать - просто не пишем поврежденные данные -- при этом обязательно удалять саму переменную - в результате записи -- мусора в пстор одно только ее название может повесить загрузку -- dc: таки НАДО вылетать. Если файл битый - он битый, почему-то. -- pstor[k] = nil end n = n + 1 end end function save_obj( npc, pk ) local st = db.storage[npc:id()] pk:w_stringZ( st.ini_filename or "" ) pk:w_stringZ( st.section_logic or "" ) pk:w_stringZ( st.active_section or "" ) pk:w_stringZ( st.gulag_name or "" ) save_logic( npc, pk ) if st.active_scheme then issue_event( npc, st[st.active_scheme], "save" ) end pstor_save_all( npc, pk ) end function load_obj( npc, pk ) local id = npc:id() local st = db.storage[id] local s = pk:r_stringZ() if s == "" then st.loaded_ini_filename = nil else st.loaded_ini_filename = s end s = pk:r_stringZ() if s == "" then st.loaded_section_logic = nil else st.loaded_section_logic = s end s = pk:r_stringZ() if s == "" then -- В activate_by_section нужно передать строку "nil", а не nil, чтобы не -- активировать ни одной из схем. При этом реальная active_section станет равной nil. st.loaded_active_section = "nil" else st.loaded_active_section = s end s = pk:r_stringZ() if s == "" then st.loaded_gulag_name = nil else st.loaded_gulag_name = s end load_logic( npc, pk ) pstor_load_all( npc, pk ) end[/code] pstor_number и т.д. - типы данных, с которыми оно работает. То есть, в отличие от se_stor и иже с ними я здесь не стал пытаться охватить все на любой случай жизни, а выбрал простенько и быстренько. get_pda_id( vn ) -- рудимент амк/соли, где обращение шло традиционно через 500 функций, размазанных по 50 скриптов. pstor_store( o, vn, v ) и pstor_retrieve( o, vn, def ) -- в общем-то то же самое. Объект, имя переменной, значение. Делают они в общем-то то, что извлекают id из game_object o, и пихают/читают переменную vn со значением v (либо подставляют def, если нет такой) в/из db.storage[o:id()].pstor Ну или с "улучшением" - запихивают вообще куда попало, если имя объекта есть в специальном списке. Зачем такое, с дикими тормозами, извращение - я не знаю, когда каждый объект в принципе сам знает и свой id, и где у него pstor. А если не знает - так ему и не надо. pstor_get_custom( npc, cls, vn ) -- некое извращение, присутствующее в Солянке, и там, откуда оно в нее попало. Как бы зачем-то сохраняет в pstor данныe каких-то очень специальных объектов, запиханных в pstor более главному. Оставлено для очередной мифической "совместимости" неизвестно чего с неизвестно чем. pstor_save_tbl( t, pk ) и pstor_load_tbl( pk ) -- а вот это нововведение для работы с таблицами, которые теперь можно держать в pstor'е, и они будут сохраняться/грузиться, без вызовов всяких сташных amk.pack_array_to_string(), amk.unpack_array_from_string и прочих злобнотормозных простыней. То есть, надо нам таблицу у актора - вот так и кладем ее в db.storage[0].pstor.yet_another_tbl = t; ну или берем там же. pstor_save_all( npc, pk ) и pstor_load_all( npc, pk ) -- в общем-то, как и все, что выше, нормальным людям НЕ НУЖНО. Оно нужно двум функциям, описание которых будет ниже. А делают они то, что пишут/читают даные pstor объекта в пакет и обратно. Про загрузке/сохранении. Интерес тут скорее именно прозекторский, и я распишу коротко суть предлагаемых изменений по отношению к оригиналу. Прежде всего изменен формат сохранения. Поскольку pstor - это само по себе таблица, с произвольными индексами, смысла считать ее размер (и хранить) - нет. Мы проверяем только момент, когда она закончилась. Далее, хотя ключом может быть что угодно, включая вектор или функцию, на практике такой экзотики нет. Ну и наконец, отдельный признак для bool отдельно значение - не нужны - храним сразу эквивалент для false или true. А то, что таблица повреждена - определяем по некоректному типу. Итого, исходный вариант сильно упростился, да и пакетик экономнее расходуется. Еще есть вариант с двумя "классическими" таблицами, которые можно хранить компактнее, и работать будет быстрее, но их как раз лучше сохранять/грузить отдельно. Об этом - следующий раз. save_obj( npc, pk ) и load_obj( npc, pk ) -- собственно функции сохранения и загрузки, вызываемые из биндеров. Здесь все элементарно. Итого, мы имеем исправление для xr_logic, позволяющееся обойтись минимальными переделками всего (и только когда руки дойдут), обращающееся с пакетами максимально быстро и экономно. А что касается ограничения в 8килобайт, то если не хранить в акторе эпические поэмы и видео с порнухой, то на сей раз прав скорее именно Билл Гейтс со своим "640К достаточно доля всего". Разумеется, эти изменения можно и еще доработать, именно в сторону бездонности, и как раз на эту тему у меня будет заказ для тех, кто плотно работает с se_stor. Точнее, сначала - на class_registrator. Чтоб убить сразу 2-х зайцев. Изменено 4 Декабря 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 4 Декабря 2014 (изменено) Выложил "затравочный" пост. Суть же заказа с class_registrator: Артос его дорабатывал для нетпакетов и хранилища, но в результате "потерялась" никогда не существовавшая совместимость с другим кодом/конфигами. А читаемость скрипта в оригинале была ужасна, в модах - еще хуже. Что надо: отпрепарировать/провести необходимые гибридизации для повышения читабельности, сразу разобрав на блоки - что осталось от оригинала, что от амк, что добавлено для хранилища, что - для пакетов. Ну и откомментить как-то. Ну а потом - аналогичная процедура с хранилищем и пакетами - чтобы можно было и фрагменты всем желающим использовать, и целиком, не вдаваясь в подробности и без мучительных "адаптаций". Карлан, С измененными "менеджерами" (которые надо оторвать совсем, как минимум от актора) у меня пакет сейчас в районе 1800-2000байт. Это в солянке-то. С регистратором не так то, что если мы берем Артососкрипт, и пихаем в любой мод - ничего хорошего из этого не выйдет. Надо каждый раз внимательно разбираться, что там зачем. Второй момент, кстати, это медленные и печальные is_monster() / is_stalker() в _g.script. и многочисленные их аналоги повсюду, например - в se_smart_terrain.script (в последнем оно еще каждый апдейт проверяется - надо ли заполнять !!!). Ну а от самих функций - кроме мордобития (возврата true) - ни каких чудес. Я для себя аж 8 новых констант/константотаблиц завел, к которым можно обрашаться как if c_mob[obj.clsid], например, и при этом они там не true хранят, а что-то полезное. НО это, во-первых, инитить надо довольно криво, во-вторых - таблицы руками забиваются. Вместо того, чтобы вот в регистраторе сразу все и сделать. Ну вот например, маленький кусочек: ["mob"] = { [clsid.bloodsucker_s] = "bloodsucker_s", [clsid.boar_s] = "boar_s", [clsid.burer_s] = "burer_s", [clsid.cat_s] = "cat_s", [clsid.chimera_s] = "chimera_s", [clsid.controller_s] = "controller_s", [clsid.dog_s] = "dog_s", [clsid.flesh_s] = "flesh_s", [clsid.fracture_s] = "fracture_s", [clsid.gigant_s] = "gigant_s", [clsid.poltergeist_s] = "poltergeist_s", [clsid.pseudodog_s] = "pseudodog_s", [clsid.psy_dog_s] = "psy_dog_s", [clsid.snork_s] = "snork_s", [clsid.tushkano_s] = "tushkano_s", [clsid.zombie_s] = "zombie_s" }, -- по синтаксису видно, кстати, что это только часть чего-то большого. Примерно понятно, зачем оно надо ? А есть синтетическая табличка c_ai - для там есть сталкеры, но нет актора. И т.д. По поводу давно и у всех - ну вот примерно то и видим в показанном недавно скрипте того же кровососа. И это все руками набивается, либо копипастится. Ага, а неписи потом из гранат стрелять пытаются. Про smart_terrain - вот вам ваше "давно", любуйтесь: -- возвращает:-- а) если сталкер: группировку сталкера и true-- б) если монстр: вид монстра и falsefunction se_smart_terrain:get_obj_community( obj ) local cls = obj:clsid() if cls == clsid.script_stalker then return obj:community(), true else return monster_classes[cls], false endend -- заполняет таблицу monster_classesfunction se_smart_terrain:fill_monster_classes() if not monster_classes then monster_classes = { [clsid.bloodsucker_s ] = "bloodsucker", [clsid.boar_s ] = "boar", [clsid.burer_s ] = "burer", [clsid.cat_s ] = "cat", [clsid.chimera_s ] = "chimera", [clsid.controller_s ] = "controller", [clsid.pseudodog_s ] = "pseudodog", [clsid.psy_dog_s ] = "psy_dog", [clsid.dog_s ] = "dog", [clsid.flesh_s ] = "flesh", [clsid.fracture_s ] = "fracture", [clsid.poltergeist_s ] = "poltergeist", [clsid.gigant_s ] = "pseudo_gigant", [clsid.snork_s ] = "snork", [clsid.tushkano_s ] = "tushkano", [clsid.zombie_s ] = "zombie" } endend function se_smart_terrain:register_if_needed() --' Если уже зарегистрены то ничего не делать. if self.registred == true then return end self.registred = true printf( "%s register called", self:name() ) -- DEBUG if self.disabled then return end self:fill_monster_classes()... Изменено 29 Апреля 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 5 Декабря 2014 (изменено) "различные общеполезные блоки, модули, куски кодов." - собственно, здесь. Как раз для того, чтобы потом по всему разделу не искать. Вот кто археологическими раскопками займется - не знаю. Изменено 7 Декабря 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 11 Декабря 2014 (изменено) торговля, trade_manager Я, конечно, понимаю, что И ЭТО ТОЖЕ давно у всех исправлено, и вообще написано свое. У ВСЕХ, а так же и у многих других. Но я вот лично вчера наконец вчитался, и {замаскированный мат здесь запрещен, так что даже и на латыни свое состояние описывать не буду}. Итак, сегодня у нас на столе вот такая забавная зверушка - этот вот самый trade_manager: function update(npc) ... if tt.update_time ~= nil and tt.update_time < time_global() then return end tt.update_time = time_global() + 3600000 Спрашивается, а как же оно тогда работает, если обновление раз в час реального времени ? Особенно, если if tt.current_buy_supplies ~= str then if tt.resuply_time ~= nil and tt.resuply_time < time_global() then return end ... tt.resuply_time = time_global() + 24*3600000 - ведь ассортимент у торговцев явственно меняется по мере диалога с тем же Сидоровичем в самом начале, да и цены у позеленевших неписей явно взлетают, а покупать что-то они согласны только совсем задешево, по сравнению с желтыми ? (Последнее, кстати, само по себе забавная шуточка, но если о ней сказать все, что я думаю, то уже сам себе по 2.1 один раз выписал, а теперь точно придется на месяц в r/o уйти. Но да, есть такой анекдот, про "но ты же - друг !") В общем, я отвлекся, а на самом деле все очень просто: СНАЧАЛА делается load() из xr_motivator/bind_monster, а потом из xr_logic.set_schemes() дергается function init() ... trade_manager[npc:id()] = {} Вот так вот. И при апдейте все эти tt.что_попало естественно оказываются ~= str, зато очень даже ~= nil И апдейт благополучно все обновляет. А завершающим номером нашего цирка будет - Маэстро, урежте марш - разумеется: function load(obj, packet) ... tt.config = ini_file(tt.cfg_ltx) после чего - неожиданно овощи: function trade_init(npc, cfg) ... trade_manager[npc:id()].config = ini_file(cfg) - да, да. Для КАЖДОГО непися, входящего в онлайн. Со всеми 1700 костюмами и прочими 30000 квестовыми итемами. Ура, товарищи ! ТакЪ победимЪ ! P.S. Да, разумеется, переписал, щас тестить буду под ОПу. P.P.S. Да, разумеется, вывод все тот же - НЕ НУЖЕН. Совсем. По тому как обновление всего этого ахтунга имеет смысл только в начале диалога с конкретным трейдером или неписем, и ТОЛЬКО для этого непися. Хотя таки да, открытые файлы можно хранить в специальной табличке файлов диалогов. И в сами-знаете-где именно так и будет. Изменено 11 Декабря 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 11 Декабря 2014 (изменено) init() очевидно не нужен вообще, "апдейт" - нужен исключительно по вызову из xr_meet. Ну и про открытие 10000 файлов, которые не system_ltx() все написано и в подполье, и вытащено оттуда в солянкотему. И в общем-то сохранение загрузка половины хлама тоже не нужны (если нужны вообще, в чем я лично сомневаюсь). upd: в общем, поправленного на скорую руку уродца, так, чтобы больше ни где ничего не трогать, выложил в теме солянкоразработки. Просто такой вот фикс для старья всякого, против тормозов. Изменено 13 Декабря 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 21 Декабря 2014 (изменено) treasure_manager, тайники, сохранениеПодумавши, решил перенести все это дело в кастомдату самих тайников. Получатся просто, быстро и красиво. Прямо при разборе конфига, а затем - собственно при выдаче и снятии. Можно, кстати, пустые ненужные заодно и в онлайн не пускать совсем.Соответственно, не надо это дело дергать/инитить ни из _g.script, ни даже грузить/сохранять в акторе.Хотя, тогда уж наверное и se_что-там_у_них (кстати, что ?) поправить.Вопрос все тот же: ну и зачем он такой красивый вообще нужен, в виде менеджера, с целым отдельным классом, когда все прекрасно умещается в целых 3 банальных плоских функции ?P.S. 1824 -> 660 байтиков. Пакет для "Солянки" стал. 28ms инициализация все-про-все. Сохранение, кстати, тоже очевидно быстрее. Ибо зачем писать сиды, если они в конфиге все руками вбиты ? И вообще не нужны, кстати.P.P.S. Там же, кстати, проверяем соответствие конфига и спавна сразу.То есть, сохраняя, зачем-то, формат ООП-шного уежища, получаем со всеми наворотами нечто вроде: https://dl.dropboxusercontent.com/u/27871782/treasure_manager.script Upd 06.01.2015: упс, ой ! Для ООП и иже с ним оно больше не подходит. Исправлял ошибку - закинул свой, уже от "обезжиренной" соли. Ну да оно и к лучшему. Будем сразу приучаться к нормальным скриптам, без заведомого мусора. Изменено 6 Января 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 1 Января 2015 (изменено) Ну раз уж новый год, и время дарить подарки, а Валерич куда-то пропал - вот вам солянкоскриптик, вроде бы низведенный до совместимости как с солянкоокаменолостями, так и амк-окаменелостями.В принципе, только одну табличку надо руками поправить - вписать clsid'ы оружия. Но можно и пустую оставить.Что и зачем:-- Сталкеры не пялятся на ГГ по пол-часа, и, соответственно, не застревают в проходах,-- если не пялиться на них. Если смотреть в сторону - через 5 секунд продолжат заниматься своими делами.-- Если увеличить радиус взятия предметов - актора не заклинивает при попытке начать разговор-- с расстояния более 2-х метров - разговор просто не начинается. Кроме Сидоровича, которому надо еще-- биндер править.-- Тормоза злостные исправлены.-- todo: решить что-то с жертвами аборта^w^w заказов на убийства, ибо бред и тормоза.-- ВНИМАНИЕ !!! Для адаптации под древнескрипты:-- log() и abort() поправить на свои-- cannot_talk_by_weapon() см. на предмет отключенной проверки use.local c_wpn = _G.c_wpn or {} -- здесь должна быть табличка clsid стволов.Внимание, в файле опечатка: local c_wnp вместо local c_wpn. Не влияет, если определено _G.c_wpn, но будет вылетать/виснуть, если определяете здесь. Поправьте.https://dl.dropboxusercontent.com/u/27871782/xr_meet.script Изменено 8 Мая 2016 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 7 Января 2015 (изменено) Фи, поручик ! © Вот специально создана тема под крупную форму. Для начала имею предложить вот: https://dl.dropboxusercontent.com/u/27871782/xr_wounded.script Он не столько распутан, сколько переписан, и под модифицированный _g.script + модифицированную обвеску, но зато все плоско и отформачено. Надеюсь, actor в _G и global_time_ms вместо time_global() не сильно мешать будут. Да, туда же запихано самолечение, в том числе в бою, а не только лежание, и исправлены все ошибки со storage и c зачем-то самовырубанием при проигрывании анимаций попадания пули в кость. local parse_data1, parse_syn_data1 = xr_logic.parse_data, xr_logic.parse_syn_data local parse_condlist1 = xr_logic.parse_condlist1 - это аналоги стандартных, но принимающие один аргумент, по тому как остальные не нужны, и в стандартных тоже не работают. Upd 12.07.2015: тоже обновил, с учетом того, что pstor для неписей не сохраняется нормально, и вообще много лишних переменных где попало - зло. Изменено 12 Июля 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 28 Января 2015 (изменено) {}Да, это все лирика была.Вообще-то, планировал еще и расписывать: как, что и зачем сделано, но... В общем, попытаюсь.В принципе, планировалось все по порядку описывать, с пляской от собственно _g, но можно и "с конца". Итак, собственно, с конца и пойдем: function init() local t = actor_data.get_pstor() -- хранилище данных актора if t.timers then for k, v in pairs( t.timers ) do timer_n = timer_n + 1 timer_t[timer_n] = { k, v[1], v[2] } end end timer_sort = true for k, v in pairs( { --["blowout"] = amk_mod.Blowout_pp, --["test"] = amk_mod.Run_Blowout_pp, --["blowout_ss"] = amk_mod.blowout_scary_sounds, --["blow_shift"] = amk_mod.Run_Blowout_pp, --["item_transform"] = amk_mod.item_transform, ["timer_test"] = timer_test } ) do timer_f[k] = v end bind_stalker.task_add( "amk_timers.check_timers", 200, check_timers ) bind_stalker.add_on_save( on_save ) timer_start( "timer_test", 1 ) return true end init() -- инициализация. Цепляем тогда, когда появился актор. Раньше просто смысла нет. Что делаем:Из pstor берем табличку таймеров в формате { имя таймера, время срабатывания от момента старта, данные }Сохранение pstor слегка переделано (см. прозекторскую), так что преобразование таблиц в строки и обратно дополнительно делать не нужно. Хранится все довольно компактно, самих таймеров и данных реально мало. Сиречь, ни каких дополнительных хранилищ тоже не нужно.Далее осталась табличка для руками забиваемых функций - атавизм от "13Гб мода" . Поскольку все закомментировано - очевидно, что уже не нужна.bind_stalker.task_add() - вешаем на апдейт актора (раз в 200 ms, чаще нет смысла), bind_stalker.add_on_save() - добавляем функцию, вызываемую при сохранении. (Да, руки дойдут - распишу, но вообще по аналогии с коллбэками по предметам - чтобы по 50 скриптов не трогать при подключении нового). function on_save() -- в таблицах таймеров сохраняется разница времени сработки и текущего local t = {} local d for i = 1, timer_n do d = timer_t[i] t[ d[1] ] = { d[2] - game_time_ms, d[3] } end d = actor_data.get_pstor() d.timers = t end on_save() -- вот та самая функция, вызываемая при сохранении. Что и зачем:нам не нужно знать, сколько времени прошло от начала игры. Нам нужно знать, через какое время сработает. Поэтому храним не 64бит-впемя, а дельту. Сколько осталось до срабатывания. Вот эту дельту и вычисляем. function check_timers() -- проверка таймеров if timer_sort then table_sort( timer_t, function( a, b ) return b[2] < a[2] end ) timer_sort = false --_util.list_tbl( timer_t, "timers" ) --log( "info", "check_timers, n: %s, time: %s", timer_n, game_time ) end local t, f while timer_n ~= 0 do -- идём с хвоста по таймерам с наименьшим временем срабатывания t = timer_t[timer_n] -- t[1] - ключ, t[2] - время акции, t[3] - данные -- log( "info", "check_timers, at: %s, time: %s", t[2], game_time ) if game_time_ms >= t[2] then -- пора запускать Берлагу f = timer_f[ t[1] ] if f then f( t[3] ) table_remove( timer_t, timer_n ) -- отработал свое timer_n = timer_n - 1 end else break -- таблица отсортирована, так что остальные - ждут end end end check_timers() -- проверка таймеров. То самое, что добавляли на апдейт. В принципе, все прозрачно. Время срабатывания подошло - вызываем и удаляем. Практически как в оригинале. Только что табличка отсортирована по времени, то, что должно сработать раньше - в хвосте. Перебираем не все, а прекращаем проверку на первом же вхождении, которое "еще не пора". local time_factor = sys_ini:r_float( "alife", "time_factor") or 5 local time_factor1000 = time_factor * 1000 local timer_t, timer_n = {}, 0 -- таймеры local timer_f = {} -- функции таймеров local timer_sort = true -- нужна сортировка function has_timer( key ) local i = timer_n while i ~= 0 do if timer_t[i][1] == key then return i, timer_t[i][2], timer_t[i][3] end i = i - 1 end end function timer_get_f( key ) return timer_f[key] end function timer_add( key, f ) if key then timer_f[key] = f end end function timer_start( key, delay, prm ) -- старт таймера в реальном времени table_insert( timer_t, { key, game_time_ms + ( delay or 0.2 ) * time_factor1000, prm } ) timer_n = timer_n + 1 timer_sort = true return true end local ms_m = 60 * 1000 local ms_h = ms_m * 60 local ms_d = ms_h * 24 function timer_g_start( key, dd, hh, mm, prm ) -- старт таймера в игровом времени (минуты) table_insert( timer_t, { key, game_time_ms + ( dd or 0 ) * ms_d + ( hh or 0 ) * ms_h + ( mm or 0 ) * ms_m, prm } ) timer_n = timer_n + 1 timer_sort = true return true end function timer_stop( key ) -- удаление таймера if timer_sort then table_sort( timer_t, function( a, b ) return b[2] < a[2] end ) timer_sort = false end local t local i = timer_n while i ~= 0 do -- идём с хвоста по таймерам с наименьшим временем срабатывания t = timer_t[i] -- t[1] - ключ, t[2] - время акции, t[3] - данные if t[1] == key then -- нашли, удаляем table_remove( timer_t, i ) timer_n = timer_n - 1 end i = i - 1 end end timer_stop() -- удаление таймера, если вдруг стал не нужен. Не дожидаясь сработки.timer_start() и timer_g_start() -- запуск таймера для реального и игрового времени соответственно. Реальное здесь сразу преобразуется в игровое, по тому что смысла на самом деле нет хранить и считать по-разному. Все события игры привязаны так или иначе к игровому.timer_add() -- добавление новой функции, которую будем вызывать по таймеру, из сторонних скриптов по инициативе этих скриптов. То есть, все а же система, что и с bind_stalker.task_add() и т.д.has_timer() и timer_get_f() -- соответственно, проверка на то, что функция уже добавлена, и что таймер запущен. В основном, на случай, когда одинаковая функция требуется более чем одному скрипту. Ну и некоторые сценарные события можно контролировать - например, ждем, пока произойдет одно, прежде чем добавить другое.Собственно, вот, такая вот процедурщина. По тому что именно на процедурщину заточено железо и среда.Пример использования - в скрипте сна можно найти. По тому они в одной теме и лежат. Собственно, один из 2-х случаев из упомянутого 13Гб мода. 2 Shadows: а вот ООП здесь почему-то оказалось ну вот совершенно незачем. P.P.S. да, загадочная переменная game_time_ms - это время от загрузки игры. Изменено 30 Января 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 29 Января 2015 (изменено) У меня там всего 600 байт лежит. Из которых еще 300 подлежит сносу.При том, что легким движением руки максимальный объем расширяется до бесконечности.Ну так и почему бы не pstor тогда ?P.S. Пф ! Можно подумать, что есть всего один мод на 13 Гб... Ну пусть не на 13, а на 14... (Это хвост про таймеры, которые не нужны. Бурное обсуждение "обо всем" перехало во флудилку.) Изменено 30 Января 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 30 Января 2015 (изменено) "Вскрытие показало, что больной умер от вскрытия."Тема для "крупной формы", то есть, на уровне скриптов целиком или больших частей оных скриптов. "Что у него внутри, зачем оно там, и что с этим можно сделать ?" Изменено 30 Января 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 11 Апреля 2015 (изменено) Гм, методически неверно было с самого начала выкладывать что попало куда попало без объяснений, но, впрочем, объяснения были даны, про то что на форуме было отдельное спрятанное от большинства подполье, где предполагалось понаписать всякого разного, а потом все писатели куда-то делись, и в мир вышло вот это самое что попало и как попало. И, да, по-хорошему бы надо и к этим кускам, каждому, во-первых добавить пояснения про то, что это такое, зачем, что внутри, и как это едят, а еще перетащить сюда же дискусси из других тем. Но... в общем, некогда и лень. Однако, не лень добавить еще один очередной малоосмысленный кусок чего-то странного. Точнее, не то, чтоб не лень, а "за державу обидно". Обидно в общем-то следующе: в свое время я поимел немало дивного секса с кучей монстротаблиц в "солянке", и был этим самым сексом, э-эээ, удовлетворен более, чем хотелось. До полного осознания смысла фразы "много хорошо - тоже плохо". А еще кроме монстротаблиц в десятках экземпляров одного и того же, было 100500 чтений конфигов с разной степени этой самой пессимизации. В общем, все это было выковыряно, и собрано в набор "табличных" скиптов, а также написана пара скриптов "кэширующих". Не в смысле "держать все конфиги в памяти", по тому как этим прекрасно занимается движок, а убрать откуда попало кучу строк со странными вызовами и странными преобразованиями. Ну так вот, смотрю я сейчас на некоторые вполне "современные" изделия, и снова становится обидно и мучительно больно за бесцельно прожитые годы. Итак, выкладывается набор, который может быть кому пригодится, а нет, так хоть на пару мыслей наведет. И, да, я знаю, что "у всех уже сделано", и про "на лесоповал, чтоб не смели". Можно про это не повторяться. https://dl.dropboxusercontent.com/u/27871782/%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%8B%20%D1%81%D0%BE%D0%BB%D1%8F%D0%BD%D0%BE%D1%87%D0%BD%D1%8B%D0%B5.zip Основная часть этого - именно таблицы, позволяющие по какому-либо свойству объекта убедиться, что это именно тот объект, который нам нужен. Заменяют простыни "if .. then ... elseif ... end" на 100500 строк. Ну, то есть, ничего принципиально нового, но собрано в разные часто используемые комбинации. Да, в этой теме я уже упоминал про class_registrator, и по-хорошему надо бы доработать все это дело, по тому как часть таблиц явно должна заполняться оттуда, а не руками, и наоборот, данные браться из таблиц, чтоб не дублировать набивание руками. Из того, что заслуживает некоторого количества отдельных слов: _tbl_protected.script - исторически сложилось, что делается куча проверок для противоестественого интеллекта неписей и прочих уборщиков, чтобы не уничтожали квестовые и уникальные предметы. По-хорошему, это бы надо всего 2 проверки: на флажок quest_item в конфиге (который препятствует продаже или выбрасыванию ценного предмета), и на уникальный story_id. Но, опять таки исторически сложилось, что конфиги - священны и неприкосновенны, и конфигурист пишет там то, что левой пятке захотелось, а потом скриптами мы пытаемся все это как-то разобрать. Но, там где используется protected_items.script - скрипт этот, мягко говоря, тоже странен. Куча неочевидных странных и кривоработающих проверок десятками способов. На практике мне оказалось как-то проще сделать таблицы свойств, и поверять наличие нужного свойства в нужной таблице. Да, в процессе еще вынесено куча "одноразовых секций", поскольку элементарно пересчитывается в конкретный объект непосредственно из логики ситуаций. Но это уже другой отдельный вопрос. Далее, _tbl_outfit.scipt: как понятно из названия, позволяет получить свойства костюмов из визуалов неписей, и наоборот. Заменяет 5 разных солянкоскриптов, делающих примерно одно и то же, требующих одновременных правок, и, кстати, все с ошибками (ну или конфиги с ошибками, но от того виснуть и бредить все это дело не не перестает). В обшем, смотрите комментарии. И, наконец, xl_imgr.script - это банальное "кэширование конфигов". Когда вместо упомянутых 100500 строк чтения конфигов мы просто берем нужное свойство из таблицы по секции конструкцией ( t[sect] or get_prm(sect) ).prm Так реально помогает, а память даже и экономится. На данный момент кэшируются только те свойства тех предметов, с регулярным опросом которых я сталкивался. Вот примерно так. P.S. Да, тот код, который я время-от времени куда-нибудь выкладываю, именно на эти таблицы и опирается. Так что, опять же, если кого интересуют какие мои поделия, то вот можно глянуть сюда на предмет всяких "недостающих" частей. Изменено 11 Апреля 2015 пользователем Dennis_Chikin 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 29 Апреля 2015 (изменено) Для поиска: диалоги, dialog_manager, адаптация Все-таки, пожалуй, выложу свой вариант этого самого менеджера, раз спрашивают. https://dl.dropboxusercontent.com/u/27871782/dialog_manager.script Очень коротко: xr_logic.parse_condlist1 здесь - то же самое, что xr_logic.parse_condlist, но на входе - только строка, которую следует разобрать. actor - то же, что db.actor, вызов init() делается в любой удобный момент, например - из reinit() актора, но перед load(), из _g.script здесь вызывать ничего не надо. В db.scipt нужна строка: ver = script_server_object_version() or -1 -- метка весии игры Это и есть вся "адаптация". Основной смысл всего действа - зачистка мусора. Несколько слов про сохранение: вообще оно здесь нафиг не нужно, поскольку сохраниться изнутри диалога все равно нельзя, и информация о выбранной фразе все равно пропадает. Но оставлено, поскольку лень было чистить. Версия движка определяется ОДИН раз, поэтому определять script-версию непися в мотиваторе - не нужно, и передавать сюда - тоже не нужно. Проверки на версию для каждой фразы не нужны, опять же, поскольку версия определена при ините, и все id сразу сформированы в соответствии с ней. То, что откомментировано как "устарело" - оно устарело и не нужно, кроме как для других скриптов, которые зачем-то могут пытаться это дергать. А вообще, опять же, не нужен совсем. Нужны диалоги на скриптовом гуе: это избавит от сложных конструкций и вылетов на забытых треугольных скобках и прочих опечатках. Плюс можно оперативно генерировать диалоги применительно к ситуации, а не хранить кучу заранее набитого ненужного, десятками и сотнями мегабайт. p.s. разбор потрохов, надеюсь, воспоследует. Как руки дойдут. Изменено 29 Апреля 2015 пользователем Dennis_Chikin 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 29 Апреля 2015 (изменено) Я б этот массэффект еще видел... Впрочем, оформление может быть любым - основной-то вопрос в создании/изменении всяких мегабайтных wawka_dialog и иже с ними. Которые даже редакторы-то нормально пережевать не могут. 2 abramcumner: так они именно что в xml. 180 файлов, 6 мег в gameplay, и еще 4 в text\rus. Изменено 29 Апреля 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 29 Июля 2015 xr_logic Раз пошла такая пьянка, закину сюда свою недоделку: или кто что нужное найдет, или недоделанное поможет допилить. Ну и вообще как затравка для анатомирования и препарирования: разберем, будет руки и желание дойдут - что и зачем где делается. https://dl.dropboxusercontent.com/u/27871782/xr_logic.script 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 661 Опубликовано 30 Июля 2015 Сдается мне, что в parse_infop надо ловить неалфавитный символ + строку (включая точку). Как-то так. А потом ругаться, если непечатный символ не определится как осмысленный. И мне не нравится elseif-простыня, ПЕРЕД которой дергается parse_func_params. Кажется, должно быть решение изящнее. abort_syntax_error_in_cond - там в оригинале таких шедевров... Причем не работают - ВСЕ. По разным причинам, но не работают - дают глухой висяк + через непредсказуемое время после - наше любимое stack overflow/memory... Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение