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

Народная 2010 разработка


n6260

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

Monnoroch, malandrinus, да, это всего лишь указатель и много места не занимает. Но самих переменных-указателей Луа насоздаёт и забудет столько, сколько раз эта переменная будет объявлятся. А уже по срабатыванию сборщика мусора освободит память, которую занимали эти все переменные. Это ведь явно описано в родной документации по Луа, выдержку из которой я приводил в предыдущем сообщении.

Механизм по каждому проходу:

- объявляется перменная Х (ей отводится память) и туда заносится информация;

- переменная Х используется в коде;

- возврат на начало цикла с забыванием предыдущей переменной (новая переменная Х указывает на новую область памяти);

...........

- проходит время; много кода отрабатывается; много памяти от объявленных-использованных-забытых переменных остаётся помеченной как неиспользованная, но ещё не освободившаяся сборщиком мусора

...........

- запуск сборщика мусора и освобождение помеченной памяти.

Вот ещё цитата из документации по Луа:

Lua осуществляет автоматичекое управление памятью. Это означает, что вам не нужно думать о выделении памяти при создании новых объектов и ее освобождении, когда объект становится ненужным. Lua время от времени автоматически запускает процедуру сборки мусора для удаления устаревших объектов (то есть объектов, которые более недоступны из Lua). Сборщик мусора обрабатывает все объекты Lua: таблицы, данные типа userdata, функции, потоки и строки.

 

В Lua реализован инкрементный сборщик по принципу пометить-очистить.

 

Ещё по теме оптимизации: раз, два, три.

P.S. Почитал это всё - сколько в скиптах ещё неоптимального кода... :blink:

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


Ссылка на сообщение
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 идёт на что-то служебное) и всё остальное, что в него не умещается, пойдёт в обычную память (что-то похожее и в языке С - сколько не указывай компилятору переменных для помещения в регистры, что он может - толкает в регистры, остальное - в обычную память).

Кроме того сам факт создания переменной - это лишния операция.

 

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


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

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

И память освобождается в этот же момент. И это не имеет вообще никакого отношения к сборщику мусора.
Где бы об этом почитать? :russian_ru:

А то из документации по Луа это никак не следует:

Сборщик мусора обрабатывает все объекты 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 к чему ведёт в плане использования стека/памяти?

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


Ссылка на сообщение
Ray, переписывать ничего не нужно - этот пример был взят из документации по Луа.

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


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

Arhara, там инфопорция выдаётся только если её ещё нет у ГГ:

    -- даём инфопорцию, если есть
    if params.info and db.actor:dont_has_info(params.info) then
        db.actor:give_info_portion(params.info)
    end

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


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

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

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

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


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

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-ы.

 

:offtopic:

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 и на тестовые скрипты от допы.

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

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


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

Написал тут по просьбе Valerich-а функцию по нанесению поражения ГГ и защите перчатками при взятии артефакта (впрочем как и любого другого предмета :rolleyes: ):

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. В живую не проверял :russian_ru: , но отзыв не за горами :D

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


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

Зарегистрировал и немного уже настроил хранилище на 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. Системы контроля версий - очень удобный инструмент для любого разработчика. Инофрмации по ним полно.

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

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


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

Мысли о кражах из тайников с замками.

Просматривал я скрипты и нашел вот что - неписи в оффлайне пробуют достать из тайников, которые были добавлены в список тайников на уровне (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, и папка с асфальтом...

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

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


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

Прислали сохранение перед сейфом на Радаре. Если туда положить замок с последними правками, то получаю вылет:

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 вставить проверку на протектед_итем, то большая часть проблем с собранными неписями квестовыми предметами уйдет. Потом ещё найти, где они в онлайне поднимают предметы.

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

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


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

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


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

Мысли по ходу:

использование 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

 

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


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

Мысли по ходу:

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() и функции выборки из таблицы готовых значений).

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

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


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

Пока проверял исправленный вариант взрывчатки нарвался на такой вылет в процессе обыска свежезаваленного бандита совместно с дружественным неписем и взятии последнего предмета (бронежилета):

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. Вылет конечно "редкоземельный""редкозонный", но всё же есть :russian_ru:

 

Добавлено через 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,
}

 

Такой пойдёт?

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

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


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

К вопросу по оптимизации. Провёл тут один тест таким кодом:

    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, да пока никуда ничего. Это на будущее для оптимизации. А пока - исправление кривого кода и глюков.

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

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


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

Чую недоброе :russian_ru:

А всё - из-за времени и его старых и кое-где до сих пор существующих проблем с переполнением целого 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-м и играть дальше.

И так нужно сделать для каждого такого срипта.

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

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


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

Ещё одна оптимизация.

[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() и т.д.).

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

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


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

[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

, которая используется во всём остальном коде и в которой идёт проверка по совершенно другим классам.

Так какие классы корректны ?

 

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


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

Бывает такой вылет (и не только в Солянке):

ловлю на его сейве такой вылет:
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

Что там за неугодные объекты были ? :)

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


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

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