sapsan 336 Опубликовано 6 Марта 2010 Monnoroch, malandrinus, да, это всего лишь указатель и много места не занимает. Но самих переменных-указателей Луа насоздаёт и забудет столько, сколько раз эта переменная будет объявлятся. А уже по срабатыванию сборщика мусора освободит память, которую занимали эти все переменные. Это ведь явно описано в родной документации по Луа, выдержку из которой я приводил в предыдущем сообщении. Механизм по каждому проходу: - объявляется перменная Х (ей отводится память) и туда заносится информация; - переменная Х используется в коде; - возврат на начало цикла с забыванием предыдущей переменной (новая переменная Х указывает на новую область памяти); ........... - проходит время; много кода отрабатывается; много памяти от объявленных-использованных-забытых переменных остаётся помеченной как неиспользованная, но ещё не освободившаяся сборщиком мусора ........... - запуск сборщика мусора и освобождение помеченной памяти. Вот ещё цитата из документации по Луа: Lua осуществляет автоматичекое управление памятью. Это означает, что вам не нужно думать о выделении памяти при создании новых объектов и ее освобождении, когда объект становится ненужным. Lua время от времени автоматически запускает процедуру сборки мусора для удаления устаревших объектов (то есть объектов, которые более недоступны из Lua). Сборщик мусора обрабатывает все объекты Lua: таблицы, данные типа userdata, функции, потоки и строки. В Lua реализован инкрементный сборщик по принципу пометить-очистить. Ещё по теме оптимизации: раз, два, три. P.S. Почитал это всё - сколько в скиптах ещё неоптимального кода... Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 7 Марта 2010 sapsan, всё же сборщик мусора освобождает только память под значения переменных. Если переменные (т.е. ссылки) локальные, то они хранятся в стеке функции и без всякого сборщика мусора погибают вместе с завершением функции (точнее своего блока). Как раз факт гибели ссылки и может запустить сборщик мусора, если на значение больше нет ссылок. Они то погибают, но место своё не освобождают и ждут своего часа Х, когда за ними придет страшный и ужасный сборщик, чтобы онести их к свободной памяти. Он это делает не сразу, а когда запустится, а запускается он когда сойдутся все условия:Цикл работы сборщика мусора зависит от двух параметров: пауза сборки мусора и коэффициент шага сборки. Паузой определяется время между запусками циклов сборки. Большие значения этого параметра делают сборку мусора менее активной. Значения меньше 1 означают, что между запусками циклов сборки паузы нет. При значении 2 сборщик перед следующим запуском ждет удвоения объема использованой памяти. Коэффициент шага сборки управляет скоростью сборки в зависимости от интенсивности выделения памяти. Большие значения параметра ускоряют работу сборщика, но при этом увеличивается размер каждого шага. Значения меньше 1 делают сборщик медленным и могут привести к тому, что цикл сборки никогда не закончится. По умолчанию используется значение 2, в этом случае сборщик работает вдвое быстрее процесса выделения памяти. По памяти - очень интересное замечание и не извесно какой именно коэфициент стоит в игре. К тому же судя по этому: Perhaps you have already read somewhere that, since version 5.0, Lua uses a register-based virtual machine. The “registers” of this virtual machine do not correspond to real registers in the CPU, because this correspondence would be not portable and quite limited in the number of registers available. Instead, Lua uses a stack (implemented as an array plus some indices) to accommodate its registers. Each active function has an activation record, which is a stack slice wherein the function stores its registers. So, each function has its own registers2 . Each function may use up to 250 registers, because each instruction has only 8 bits to refer to a register. Given that large number of registers, the Lua precompiler is able to store all local variables in registers. The result is that access to local variables is very fast in Lua.мест в стеке функции всего 250 (видать 6 идёт на что-то служебное) и всё остальное, что в него не умещается, пойдёт в обычную память (что-то похожее и в языке С - сколько не указывай компилятору переменных для помещения в регистры, что он может - толкает в регистры, остальное - в обычную память). Кроме того сам факт создания переменной - это лишния операция. Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 7 Марта 2010 Обо всём остальном (в том числе об областях видимости, локальных и внешних локальных переменных), кроме этого утверждения, я знаю. И память освобождается в этот же момент. И это не имеет вообще никакого отношения к сборщику мусора.Где бы об этом почитать? А то из документации по Луа это никак не следует: Сборщик мусора обрабатывает все объекты Lua: таблицы, данные типа userdata, функции, потоки и строки. И как тогда объяснить вот это: Обработка каждого объявления local ведет к созданию новой локальной переменной. Рассмотрим следующий пример: a = {} local x = 20 for i=1,10 do local y = 0 a[i] = function () y=y+1; return x+y end end Цикл создает 10 экземпляров функции, в которых используются различные переменные y и один и тот же x. Само повторное объвление в цикле local y к чему ведёт в плане использования стека/памяти? Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 7 Марта 2010 Ray, переписывать ничего не нужно - этот пример был взят из документации по Луа. Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 23 Марта 2010 Arhara, там инфопорция выдаётся только если её ещё нет у ГГ: -- даём инфопорцию, если есть if params.info and db.actor:dont_has_info(params.info) then db.actor:give_info_portion(params.info) end Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 4 Апреля 2010 (изменено) V92, Shadowman, посмотрел как было раньше. Раньше удалялась аномалия в лоб без правильного отключения её в amk_anoms.script. Сейчас она правильно отключается и тут же в лоб удаляется. После чего моментально перестаёт отображатся на детекторе. Похоже, что отображение аномалии, как и писк детектора, это что-то движковое + колбек самих аномалий. Shadowman, аномалии удаляются этим кодом: -- удаляем аномалию, если нужно и она не в исключениях -- код взят из amk_anoms.turn_off_all() if params.remove_anomaly then local sobj = alife():object(params.remove_anomaly_id) if sobj then local map = alife():level_name(game_graph():vertex(sobj.m_game_vertex_id):level_id()) if not amk_anoms.check_exclusion(sobj, map) then local status = amk_anoms.get_anomaly_status(sobj) if status == "on" then amk_anoms.set_anomaly_status(sobj, "del") end alife():release(sobj, true) end end end Изменено 4 Апреля 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 13 Апреля 2010 (изменено) qwertyuiop, да, есть такая проблема и такого кода в скриптах валом: game_minutes = level.get_time_days()*60*24+level.get_time_hours()*60+level.get_time_minutes() Из-за этой "особенности" может зависать в игре что угодно (квесты, погода...). Стоит в том же amk.script сделать глобальную переменную time_minutes и обновлять её один раз за апдейт, а во всех остальных скриптах только обращатся к ней через amk.time_minutes. А что amk.StartTime равен nil - так это потому, что в amk.script он объявлен как локальная переменная. Monnoroch, есть там все end-ы. Arhara, не принимай близко к сердцу - писать с нуля в депо - это не чинить и достраивать на полном ходу локомотив. Shadowman, видать тыкают пальцем, мол глючит, много всего без толку (сам не раз читал такое). А жизнь - она ведь не линейная и очень разнообразная. И сюжетов в ней параллельных валом. Мне игры с единственной сюжетной линией уже не интересны... Ray, ну вот все выражения = level.get_time_days()*60*24+level.get_time_hours()*60+level.get_time_minutes() и нужно переделать в = amk.time_minutes и прописать код обновления значения time_minutes в апдейтере amk.script и месте первого старта игры. Код обновления где-то в amk.script: time_minutes = math.floor(game.get_game_time():diffSec(StartTime) / 60) P.S. Доделаю работу - сделаю на скрипты Солянки от 26.01 и на тестовые скрипты от допы. Изменено 13 Апреля 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 13 Апреля 2010 Написал тут по просьбе Valerich-а функцию по нанесению поражения ГГ и защите перчатками при взятии артефакта (впрочем как и любого другого предмета ): function hit_by_art(obj) -- для каждого вида артефактов сила, импульс и тип поражения -- возможные типы поражения: hit.burn, hit.chemical_burn, hit.dummy, hit.explosion, -- hit.fire_wound, hit.radiation, hit.shock, hit.strike, hit.telepatic, hit.wound local art_hit = { ["af_gravi"] = {["power"] = 0.1, ["impulse"] = 0.1, ["type"] = hit.strike}, ["af_gold_fish"] = {["power"] = 0.15, ["impulse"] = 0.15, ["type"] = hit.explosion} } -- для каждого вида перчаток коэфициент пропуска силы поражения, которая останется от изначальной local perchatki_propusk = { ["normal"] = 0.7, ["good"] = 0.5, ["absolute"] = 0.0 } if obj then local sect = obj:section() if sect and art_hit[sect] then local propusk = 1 local perchatki = db.actor():item_in_slot(0) if perchatki and perchatki_propusk[perchatki:section()] then propusk = perchatki_propusk[perchatki:section()] end local h = hit() h.draftsman = db.actor h.direction = vector():set(0,0,0) h.impulse = art_hit[sect]["impulse"] h.type = art_hit[sect]["type"] h.power = art_hit[sect]["power"] * propusk db.actor:hit(h) end end end Arhara, если нужно - просто вставь её вызов в actor_binder:on_item_take() P.S. В живую не проверял , но отзыв не за горами Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 22 Апреля 2010 (изменено) Зарегистрировал и немного уже настроил хранилище на subversion для Солянки. Создал в расчете на совместную работу над самой Солянкой и для мододелов под Солянку. Оно открыто всем на чтение. Линк хранилища: svn://svn.opensvn.ru/narodnaya_solyanka Линк трака: http://trac.opensvn.ru/narodnaya_solyanka Для тех, кто будет допущен к хранилищу в режиме записи - в настройках клиента (речь идёт о TortoiseSVN) открыть на редактирование конфигурационный файл и сделать следующее: - раскомментировать строку enable-auto-props = yes; - добавить в секции [auto-props] следующие строки: *.script = svn:eol-style=native;svn:mime-type="text/x-lua;charset=windows-1251" *.ltx = svn:eol-style=native;svn:mime-type="text/xml;charset=windows-1251" *.xml = svn:eol-style=native;svn:mime-type="text/xml;charset=windows-1251" Это для корректного отображения виндовой кирилицы в траке. Настройку, логическое распределение и заливку уже существующих правок ещё не закончил. В нём не представлены самые тяжелые папки так как нет уверенности, что Солянку оттуда не выкинут, если всю её туда залить Если есть предложиния и замечания - в личку. P.S. Системы контроля версий - очень удобный инструмент для любого разработчика. Инофрмации по ним полно. Изменено 23 Апреля 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 28 Апреля 2010 (изменено) Мысли о кражах из тайников с замками. Просматривал я скрипты и нашел вот что - неписи в оффлайне пробуют достать из тайников, которые были добавлены в список тайников на уровне (off_npcs[map].inv_boxes) при заполнении списка тайников. А заполнение списка происходит либо при загрузке вызовом amk_offline_alife.update_npc_tables() либо через 3 часа! игрового времени в amk_offline_alife.update() "мягким" способом (каждый объект из 65535 штук обрабатывается по очереди при каждом апдейте актора, а не за раз все 65535). Кроме того когда-то, видимо ради оптимизации, был отключен вызов update_tables() из offline_alife(), хотя по логике он там необходим. Таким образом неписи в оффлайне полезут в тайник не смотря на наличие в нём замка, если только не было произведено перезаполнение таблиц оффлайна. Предлагаю варианты исправление "памяти" неписей: - при установке/снятии замка принудительно вызывать update_tables(lname) для текущего уровня; - период мягкого обновления таблиц уменьшить до 15 минут игрового времени. P.S. "Мягкий" способ работы со всеми 65535 объектами можно применить в других местах. В данный момент это актуально для взрывчатки. Shadowman, по уборщику - можно попробовать как ты говоришь. Вполне может пройти В геймдату был включен и Thumbs.db, и старый jpg, и папка с асфальтом... Изменено 28 Апреля 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 5 Мая 2010 (изменено) Прислали сохранение перед сейфом на Радаре. Если туда положить замок с последними правками, то получаю вылет: FATAL ERROR [error]Expression : I != levels().end() [error]Function : GameGraph::CHeader::level [error]File : e:\stalker\sources\trunk\xr_3da\xrgame\game_graph_inline.h [error]Line : 171 [error]Description : there is no specified level in the game graph : 224 stack trace: Из этого следует, что вылетает эта строка: alife():level_name(game_graph():vertex(obj.m_game_vertex_id):level_id() из функции: function update_tables(level) off_npcs[level]={monsters={},stalkers={},weapons={},artefacts={}, inv_boxes={}} local obj for a=1,65534 do obj = alife():object(a) if (obj and alife():level_name(game_graph():vertex(obj.m_game_vertex_id):level_id())==level) then add_fresh_meat(obj) elseif (obj and obj.parent_id ~= nil) then local p_obj = alife():object(obj.parent_id) if (p_obj) then local p_map = alife():level_name(game_graph():vertex(p_obj.m_game_vertex_id):level_id()) if (p_map == level) then add_fresh_meat(obj) end end end end end Если же перед ней проверять на game_graph():valid_vertex_id(obj.m_game_vertex_id), то вылета нет. А такой "работы" с game_graph без предварительной проверки валом. В том числе и в аддоне Бака по подстволам Ещё мысль: Если в add_fresh_meat(obj) и в место, где наполняется таблица items вставить проверку на протектед_итем, то большая часть проблем с собранными неписями квестовыми предметами уйдет. Потом ещё найти, где они в онлайне поднимают предметы. Изменено 5 Мая 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 5 Мая 2010 Arhara, для начала можно попробовать в скриптах повставлять защиту от некорректных вертексов, а там видно будет... Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 8 Мая 2010 Мысли по ходу: использование math.random() нужно заменить на random_number() так как в последнем перед вызовом math.random() вызывается math.randomseed(device ():time_global()), что даст действительно случайный результат, а то, например, у игроков и у меня часто ЧУ не начинается подряд много раз... [spoiler=random_number() из _g.script] function random_number (min_value, max_value) math.randomseed (device ():time_global ()) if min_value == nil and max_value == nil then return math.random () else return math.random (min_value, max_value) end end Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 11 Мая 2010 (изменено) Мысли по ходу: 1. Перед использованием obj.m_game_vertex_id его нужно проверять с помощью game_graph():valid_vertex_id(obj.m_game_vertex_id); 2. Если obj был получен с помощью level.object_by_id(i), то нужно проверять его не только на nil, но и на присутствие на сервере с помощью alife():object(i) 3. game:time() больше за АМК-ное игровое время, полученное из start_time alife.ltx, приблизительно на 4 с половиной дня (попадает на 26 апреля ) и непонятно зачем при установке переменной StartTime от времени из конфига отнимаются сутки ? 4. Если в начале игры сразу после math.randomseed() создать довольно большую таблицу со случайными значениями и по всем скриптам обращатся к функции, которая будет последовательно выдавать значения из неё, то можно, теоретически, получить прирост производительности (нужно сравнить скорость math.random() и функции выборки из таблицы готовых значений). Изменено 13 Мая 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 16 Мая 2010 (изменено) Пока проверял исправленный вариант взрывчатки нарвался на такой вылет в процессе обыска свежезаваленного бандита совместно с дружественным неписем и взятии последнего предмета (бронежилета): FATAL ERROR [error]Expression : e_entity->ID_Parent == id_parent [error]Function : xrServer::Process_event_reject [error]File : E:\stalker\sources\trunk\xr_3da\xrGame\xrServer_process_event_reject.cpp [error]Line : 23 [error]Description : bandit_outfit_red17289 [error]Arguments : gar_bandit_agr_8 stack trace: Очевидно случился конфликт интересов моих и непися. Вот только не понятно кто его всё-таки успел взять первым. На сколько помню (когда-то видел в скриптах), то у неписей, если они что-то пробуют взять из инвентаря обыскиваемого, происходит проверка на то, что этот предмет не спёр кто-то другой. Наверное у ГГ такой проверки нет... P.S. Вылет конечно "редкоземельный""редкозонный", но всё же есть Добавлено через 62 мин.: По оружию. Функция isWeapon() охватывает далеко не всё оружие в игре. В ней не хватает wpn_fn2000, wpn_fort и wpn_rg6_s. [spoiler=isWeapon()] -- проверяет оружие ли это (передавать game_object) function isWeapon(object) local id = get_clsid(object) if id == nil then return false end if id == clsid.wpn_vintorez_s then return true elseif id == clsid.wpn_ak74_s then return true elseif id == clsid.wpn_lr300_s then return true elseif id == clsid.wpn_hpsa_s then return true elseif id == clsid.wpn_pm_s then return true elseif id == clsid.wpn_shotgun_s then return true elseif id == clsid.wpn_bm16_s then return true elseif id == clsid.wpn_svd_s then return true elseif id == clsid.wpn_svu_s then return true elseif id == clsid.wpn_rpg7_s then return true elseif id == clsid.wpn_val_s then return true elseif id == clsid.wpn_walther_s then return true elseif id == clsid.wpn_usp45_s then return true elseif id == clsid.wpn_groza_s then return true elseif id == clsid.wpn_knife_s then return true elseif id == clsid.wpn_grenade_launcher then return true elseif id == clsid.wpn_grenade_f1 then return true elseif id == clsid.wpn_grenade_rpg7 then return true elseif id == clsid.wpn_grenade_rgd5 then return true elseif id == clsid.wpn_grenade_fake then return true else return false end end Кроме того её бы тоже переделать чтобы она использовала таблицу вместо последовательностей elseif. Также в ней используется функция [spoiler=function get_clsid(npc)] function get_clsid(npc) if npc == nil then return nil end return npc:clsid() -- if is_object_online(npc:id()) then -- return npc:clsid() -- else -- return nil -- end end Только вот почему они обе ограничивается только онлайн объектами ? Ведь метод clsid() есть и у онлайн-, и у офлайн-объектов. P.S. Если где не прав - буду рад услышать. Вот мой вариант функции: [spoiler=isWeapon()] function isWeapon(object) local clsid_weapon_full = { [clsid.wpn_ak74_s] = true, [clsid.wpn_bm16_s] = true, [clsid.wpn_fn2000] = true, [clsid.wpn_fort] = true, [clsid.wpn_grenade_f1] = true, [clsid.wpn_grenade_fake] = true, [clsid.wpn_grenade_launcher] = true, [clsid.wpn_grenade_rgd5] = true, [clsid.wpn_grenade_rpg7] = true, [clsid.wpn_groza_s] = true, [clsid.wpn_hpsa_s] = true, [clsid.wpn_knife_s] = true, [clsid.wpn_lr300_s] = true, [clsid.wpn_pm_s] = true, [clsid.wpn_rg6_s] = true, [clsid.wpn_rpg7_s] = true, [clsid.wpn_shotgun_s] = true, [clsid.wpn_svd_s] = true, [clsid.wpn_svu_s] = true, [clsid.wpn_usp45_s] = true, [clsid.wpn_val_s] = true, [clsid.wpn_vintorez_s] = true, [clsid.wpn_walther_s] = true, } return (object and clsid_weapon_full[object:clsid()]) end Также интересует набор clsid без боеприпасов и гранат. local clsid_weapon_no_ammo = { [clsid.wpn_ak74_s] = true, [clsid.wpn_bm16_s] = true, [clsid.wpn_fn2000] = true, [clsid.wpn_fort] = true, [clsid.wpn_groza_s] = true, [clsid.wpn_hpsa_s] = true, [clsid.wpn_lr300_s] = true, [clsid.wpn_pm_s] = true, [clsid.wpn_rg6_s] = true, [clsid.wpn_rpg7_s] = true, [clsid.wpn_shotgun_s] = true, [clsid.wpn_svd_s] = true, [clsid.wpn_svu_s] = true, [clsid.wpn_usp45_s] = true, [clsid.wpn_val_s] = true, [clsid.wpn_vintorez_s] = true, [clsid.wpn_walther_s] = true, } Такой пойдёт? Изменено 16 Мая 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 27 Мая 2010 (изменено) К вопросу по оптимизации. Провёл тут один тест таким кодом: local t = profile_timer() local g = {{}} t:start() for i = 1, 65535 do table.insert(g[1], math.random()) end t:stop() get_console():execute("load ~~~ TIME SPEND table.insert(g[1], math.random()): "..t:time()) t = profile_timer() g = {{}} t:start() local rnd = math.random for i = 1, 65535 do table.insert(g[1], rnd()) end t:stop() get_console():execute("load ~~~ TIME SPEND table.insert(g[1], rnd()): "..t:time()) t = profile_timer() g = {{}} t:start() local ti = table.insert local rnd = math.random for i = 1, 65535 do ti(g[1], rnd()) end t:stop() get_console():execute("load ~~~ TIME SPEND ti(g[1], rnd()): "..t:time()) t = profile_timer() g = {{}} t:start() local rnd = math.random for i = 1, 65535 do g[1][#g[1]+1] = rnd() end t:stop() get_console():execute("load ~~~ TIME SPEND g[1][#g[1]+1] = rnd(): "..t:time()) t = profile_timer() g = {{}} t:start() local rnd = math.random local tbl = g[1] for i = 1, 65535 do tbl[#tbl+1] = rnd() end t:stop() get_console():execute("load ~~~ TIME SPEND tbl = g[1]; tbl[#tbl+1] = rnd(): "..t:time()) и получил такие результаты: ! Cannot find saved game ~~~ time spend table.insert(g[1], math.random()): 22435.732421875 ! Cannot find saved game ~~~ time spend table.insert(g[1], rnd()): 20538.1015625 ! Cannot find saved game ~~~ time spend ti(g[1], rnd()): 18281.306640625 ! Cannot find saved game ~~~ time spend g[1][#g[1]+1] = rnd(): 10746.590820313 ! Cannot find saved game ~~~ time spend tbl = g[1]; tbl[#tbl+1] = rnd(): 10056.7734375 Если не учитывать последнюю оптимизацию, то имеем ускорение в 52%, если учитывать - то 55% romale, да пока никуда ничего. Это на будущее для оптимизации. А пока - исправление кривого кода и глюков. Изменено 27 Мая 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 6 Июня 2010 (изменено) Чую недоброе А всё - из-за времени и его старых и кое-где до сих пор существующих проблем с переполнением целого 32-разрядного. Например, заглянул в скрипт level_weathers.script новой версии погоды Beautiful Weather и увидел там использование неподходящей функции времени и тут же - сохранение переменных с значением разного времени нужного для скрипта. А сохранение происходит с помощью функции w_u32, которая сохраняет 32-разрядные значения. Также и в скрипте xr_gulag.script, в котором я было изменил получение времени на 64-разрядный результат, но не заметил, что переменные со значением времени сохраняются с помощью w_u32. Тоесть когда значение станет большим за максимально возможное для 32-разрядного целого, то сохранится результат переполнения, а не нужное нам число. Выходит, что нужно всюду ещё и изменить функции сохранения и чтения переменных с временем на их 64-разрядные варианты... Хотя, обычно, такие значения сравниваются с текущим временем и, если они меньше его, то выполняются какие-то действия и значения устанавливаются равными текущему времени плюс интервал ожидания до следующего действия. В этом случае при загрузке игры каждый раз будут производится такие действия и общая логика игры пострадать не должна. Однако никаких гарантий нормальной работы после переполнения нет. P.S. После изменения функций сохранения/загрузки 32-разрядных чисел на сохраняющие 64-разрядные понадобится новая игра. Shadowman, есть соответсвующие функции: number r_u64(); void w_u64(unsigned __int64); number r_s64(); void w_s64(__int64); Shadowman, да - переписать и требовать новой игры. Или сделать два варианта скрипта: 1. только с 64-разрядной записью; 2. с 64-разрядной записью и чтением. 1-й положить в геймдату, загрузить сохранение, сохранится. 2-й положить в геймдату, загрузить сохранение сделанное с 1-м и играть дальше. И так нужно сделать для каждого такого срипта. Изменено 7 Июня 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 9 Июня 2010 (изменено) Ещё одна оптимизация. [spoiler=Код в _g.script] IAmAStalker = {} function IAmAStalkerInit() IAmAStalker = { [clsid.actor] = true, [clsid.script_stalker] = true } end IAmAMonster = {} function IAmAMonsterInit() IAmAMonster = { [clsid.boar_s] = true, [clsid.bloodsucker_s] = true, [clsid.dog_s] = true, [clsid.flesh_s] = true, [clsid.pseudodog_s] = true, [clsid.psy_dog_s] = true, [clsid.burer_s] = true, [clsid.cat_s] = true, [clsid.chimera_s] = true, [clsid.controller_s] = true, [clsid.fracture_s] = true, [clsid.poltergeist_s] = true, [clsid.gigant_s] = true, [clsid.zombie_s] = true, [clsid.tushkano_s] = true, [clsid.snork_s] = true } end [spoiler=Код в amk.script] -- Эта функция вызывается самой первой. Онлайновые объекты недоступны! db.actor недоступен! function on_game_start() mod_call("on_game_start") ver = get_ver() getStartTime() IAmAStalkerInit() IAmAMonsterInit() end local g, se_obj local sim = alife() local t = profile_timer() t:start() for i = 1, 65535 do se_obj = sim:object(i) if se_obj then g = IsStalker(se_obj) end end t:stop() get_console():execute("load ~~~ TIME SPEND g = IsStalker(se_obj): "..t:time()) t = profile_timer() t:start() for i = 1, 65535 do se_obj = sim:object(i) if se_obj then g = IAmAStalker[se_obj:clsid()] end end t:stop() get_console():execute("load ~~~ TIME SPEND g = IAmAStalker[se_obj:clsid()]: "..t:time()) se_obj = sim:object(0) if se_obj then t = profile_timer() t:start() for i = 1, 65535 do g = IsStalker(se_obj) end t:stop() get_console():execute("load ~~~ TIME SPEND only g = IsStalker(se_obj): "..t:time()) t = profile_timer() t:start() for i = 1, 65535 do g = IAmAStalker[se_obj:clsid()] end t:stop() get_console():execute("load ~~~ TIME SPEND only g = IAmAStalker[se_obj:clsid()]: "..t:time()) end ! Cannot find saved game ~~~ time spend g = isstalker(se_obj): 130028.640625 ! Cannot find saved game ~~~ time spend g = iamastalker[se_obj:clsid()]: 106638.2890625 ! Cannot find saved game ~~~ time spend only g = isstalker(se_obj): 38834.3515625 ! Cannot find saved game ~~~ time spend only g = iamastalker[se_obj:clsid()]: 23809.611328125 В чистом виде имеем ускорение в 1,63 раза. P.S. Но удалять оригинальные функции IsStalker() и IsMonster() нельзя - они используются в оригинальных нераспакованных скриптах. Ещё одна оптимизация. [spoiler=Код в _g.script] iniLines = {} function getIniValueFloat(sect, line, default) if not iniLines[sect] or not iniLines[sect][line] then if not iniLines[sect] then iniLines[sect] = {} end local ini = system_ini() if ini and ini:section_exist(sect) and ini:line_exist(sect, line) then iniLines[sect][line] = ini:r_float(sect, line) else iniLines[sect][line] = default end end return iniLines[sect][line] end local sect = "alife" local line = "time_factor" local g t = profile_timer() t:start() for i = 1, 65535 do local ini = system_ini() if ini and ini:section_exist(sect) and ini:line_exist(sect, line) then g = ini:r_float(sect, line) end end t:stop() get_console():execute("load ~~~ TIME SPEND ini:r_float(sect, line): "..t:time()) t = profile_timer() t:start() for i = 1, 65535 do g = getIniValueFloat(sect, line, 0) end t:stop() get_console():execute("load ~~~ TIME SPEND getIniValueFloat(sect, line, 0): "..t:time()) ! Cannot find saved game ~~~ time spend ini:r_float(sect, line): 158315.3125 ! Cannot find saved game ~~~ time spend getinivaluefloat(sect, line, 0): 9088.375 Новый способ быстрее в 17.4 раза! Это, конечно, лабораторный вариант. В реальности при первом обращении к данным будет происходить точно такое же чтение из конфига и внесение их в таблицу, но при наличии данных в таблице ускорение именно таким и будет. Это больше отразится на "сглаживании" игры (снятии заметных и не очень подтормаживаний), чем на реальных FPS. Хотя кто его знает как там всё написано... P.S. При надобности создаются аналогичные функции для других типов данных (getIniValueString(), getIniValueBool(), getIniValueSInt32(), getIniValueUInt32() и т.д.). Изменено 9 Июня 2010 пользователем sapsan Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 21 Июня 2010 [spoiler=Вопрос к знатокам]В _g.script есть функция --// Является ли оbj монстром function is_object_monster(obj) local otype = get_clsid(obj) if(otype == clsid.crow or otype == clsid.zombie or otype == clsid.flesh or otype == clsid.controller or otype == clsid.bloodsucker or otype == clsid.burer or otype == clsid.fracture or otype == clsid.chimera or otype == clsid.boar or otype == clsid.dog_red or otype == clsid.dog_black or otype == clsid.poltergeist or otype == clsid.pseudo_gigant ) then return true end return false end , которая используется лишь один раз в скрипте gulag_military.script в функции ------------------------------------------------------------------------------------------- -- Проверка, имеется ли у гулага враг ------------------------------------------------------------------------------------------- function check_enemy (gulag) for k,v in pairs(gulag.Object) do if v ~= true then if v:best_enemy () ~= nil and v:story_id () ~= 710 and is_object_monster (v:best_enemy ()) == false then return true end end end return false end и больше нигде. В то же время в _g.script есть функция function IsMonster (object, class_id) local id = class_id or get_clsid (object) local monsters = { [clsid.boar_s] = true, [clsid.bloodsucker_s] = true, [clsid.dog_s] = true, [clsid.flesh_s] = true, [clsid.pseudodog_s] = true, [clsid.psy_dog_s] = true, [clsid.burer_s] = true, [clsid.cat_s] = true, [clsid.chimera_s] = true, [clsid.controller_s] = true, [clsid.fracture_s] = true, [clsid.poltergeist_s] = true, [clsid.gigant_s] = true, [clsid.zombie_s] = true, [clsid.tushkano_s] = true, [clsid.snork_s] = true } return monsters[id] or false end , которая используется во всём остальном коде и в которой идёт проверка по совершенно другим классам. Так какие классы корректны ? Поделиться этим сообщением Ссылка на сообщение
sapsan 336 Опубликовано 25 Июня 2010 Бывает такой вылет (и не только в Солянке): ловлю на его сейве такой вылет: Expression : fatal error Function : CScriptEngine::lua_error File : E:\stalker\sources\trunk\xr_3da\xrGame\script_engine.cpp Line : 73 Description : <no expression> Arguments : LUA error: j:\s.t.a.l.k.e.r\gamedata\scripts\rx_wmgr.script:596: attempt to get length of field '?' (a nil value) строка: self.weapons[typ][#self.weapons[typ]+1] = {sec = sec,id = id,prm = prm} Текст ошибки отличается от "обычного" из-за того, что в этой строке была проведена оптимизация (была замена table.insert()), но суть остаётся та же - таблица self.weapons[typ] == nil В другом моде по такому вылету советовали пройти проблемное место без нового оружия. Тоесть проблема в конфигах нового оружия. Вот нашел описание типов: параметр ef_weapon_type отвечает в первую очередь за то как NPC будет стрелять из оружия 5 - стрелять одиночными 6 - стрелять очередями 7 - прицелиться и выстрелить одиночным 8 - ОЧЕНЬ долго целиться и выстрелить (снайпер) 9 - стрельба из гранатомета чем выше цифра, тем оружие по идее имеет больший приоритет для NPC , но опять же указано что это будет работать только вместе с ef_main_weapon_type. то есть там тоже надо менять значения на эквивалентные значениям в ef_weapon_type. пояснение значений ef_main_weapon_type: 0 - пистолет 1 - дробовик 2 - автомат 3 - винтовка 4 - гранатомёт Кстати кому интересно вот инфа об отличии классов оружия - что дает каждый. WP_AK74 - автоматическое оружие с возможностью установить ПБС, прицел, гранатомет WP_LR300 - автоматическое оружие с возможностью установить ПБС, прицел, невозможно установить гранатомет (да да подствольник ставить нельзя - а у самой винтовки LR300 стоит класс WP_AK74 - который как раз разрешает подствольник) WP_PM, WP_HPSA, WP_USP45, WP_WALTH - пистолеты, чем отличаются - неизвестно WP_SVD - винтовка, одиночный огонь, установка ПБС невозможна WP_SVU - винтовка, одиночный огонь, ПБС интегрирован WP_VAL - автоматический огонь, возможна установка прицела, ПБС интегрирован, невозможно установить гранатомет WP_RG6 и WP_RPG7 - отличия неизвестны WP_VINT - автоматический огонь Я в этом не гуру, но вот что нашел странного: 1. винтовка Мосина по сути - снайперка и типы у неё правильные ef_main_weapon_type = 3 ef_weapon_type = 8 , а класс class = WP_SHOTG 2. у некоторых пистолетов есть тип стрельбы из оружия ef_weapon_type = 5 , но нет типа оружия ef_main_weapon_type (как я понимаю - в этом случае берётся значение по-умолчанию 0 ?) 3. болт и зомби (m_zombie_e) имеет тип ef_weapon_type = 1 как у всего холодного оружия Есть, конечно, железный метод if-а - отрезать код предварительной проверкой, но в самом коде и так есть abort(), который на данный момент в Солянке не выбрасывает, а лишь в лог сообщение пишет, if rx_utils.item_is_fa(item) then local sec = item:section() local cnd = item:condition() if wm_modes.forbiddens[sec] ~= true and cnd >= wm_modes.min_cond and self:have_ammo(item) then local params = read_wpn_params(sec) local typ = params.typ if not self.weapons[typ] then abort("weapon_manager: not registered weapon type '%s' in [%s]",typ,sec) end local prm = self:get_weapon_prior(item) self.weapons[typ][#self.weapons[typ]+1] = {sec = sec,id = id,prm = prm} end end . Но полного лога нет - достался только хвост. Однако хочется, чтобы всё было настроено нормально, а не методом отсечение кривых конфигов... Кто-то знает где копать ? Сам смотрел конфиги оружия в геймдате Солянки соответсвенно классам function item_is_fa(o,c) if not c then c = o and o:clsid() end local t = { [clsid.wpn_pm_s] = true, [clsid.wpn_walther_s] = true, [clsid.wpn_usp45_s] = true, [clsid.wpn_hpsa_s] = true, [clsid.wpn_bm16_s] = true, [clsid.wpn_shotgun_s] = true, [clsid.wpn_ak74_s] = true, [clsid.wpn_lr300_s] = true, [clsid.wpn_groza_s] = true, [clsid.wpn_val_s] = true, [clsid.wpn_vintorez_s] = true, [clsid.wpn_svu_s] = true, [clsid.wpn_svd_s] = true, [clsid.wpn_rg6_s] = true, [clsid.wpn_rpg7_s] = true, [clsid.wpn_knife_s] = true} if c and t[c] then return true end return false end , но левого типа так и не нашел. Чуть не забыл - в этом моде используются такие типы: 1,5,6,7,8,9. Добавлено через 105 мин.: Нашел интересный геноцид при начале НИ: for a=1000,20000,1 do local obj=alife():object(a) if obj and ((string.find(obj:name(),"af_") and not string.find(obj:name(),"esc_af_") and not string.find(obj:name(),"pri_af_" ) and not string.find(obj:name(),"af_dumm")) or string.find(obj:name(),"esc_wpn")) then alife():release(obj,true) end end Что там за неугодные объекты были ? Поделиться этим сообщением Ссылка на сообщение