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

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

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

"Что у нее внутри, и как это сделать лучше". Для тех, кто уже разбирается в скриптах, конфигах, текстурах и "других страшных словах" ©, и имеет желание и время действительно делать их лучше.
См. подробности в первом посте.

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

Изменено пользователем Dennis_Chikin
  • Нравится 2
  • Полезно 1
Ссылка на комментарий
Мысли по ходу:

использование math.random() нужно заменить на random_number() так как в последнем перед вызовом math.random() вызывается math.randomseed(device ():time_global()), что даст действительно случайный результат, а то, например, у игроков и у меня часто ЧУ не начинается подряд много раз...

Это лишено смысла. К чему может привести предлагаемая стратегия иллюстрирует вот такой пример:

for i=1,10 do
    get_console():execute(tostring(random_number()))
end

Если в начале random_number стоит вызов math.randomseed, то этот код выводит 10 одинаковых чисел. Вызов math.randomseed должен быть сделан один раз за всю игру в начале.

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

 

Проверил - да, такой цикл выведет одно и то же число 10 раз. А вот если поставить get_console():execute(tostring(random_number())) в апдейт актора, то будем получать по кругу плавно возрастающие значения, для примера, от 0.00033570360392332 до 0.99887079000473. Тоесть math.randomseed() есть смысл поставить в том же апдейте актора, а random_number() вообще нигде не использовать. sapsan

 

Весьма любопытное наблюдение. Из него во-первых следует, что генератор псевдослучайных чисел в Lua на самом деле паршивенький. Если инициализирующее число в math.randomseed() меняется (а оно от апдейта к апдейту гарантировано меняется), то первое полученное затем число должно в должной степени непредсказуемо отличаться от полученного после предыдущей инициализации. Т.е. к примеру если я сделал

math.randomseed(1000)

a1 = math.random()

и

math.randomseed(1001)

a2 = math.random()

то по идее могу рассчитывать, что a1 и a2 не будут иметь никакой видимой взаимосвязи. А здесь зависимость совершенно очевидная, что заметно даже невооружённым взглядом. Очевидно, что для целей криптографии этот генератор использовать не стоит =)

 

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

 

Согласен. sapsan

 

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

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий
у игроков и у меня часто ЧУ не начинается подряд много раз...

Кстати, а о каком "много" вообще идёт речь? Если оценивать вероятность возникновения цепочек отказов, то получим:

1 раз - 50%

2 - 25%

3 - 12.5%

4 - 6.25%

5 - 3.125%

и т.д.

Можно видеть, что три отказа подряд можно спокойно ожидать, скажем, раз в игровую неделю, а пять - примерно раз в игровой месяц.

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

 

Такое запросто бывает при загрузке одного и того же сохранения. sapsan

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

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

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

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

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

sapsan,

2. Если obj был получен с помощью level.object_by_id(i), то нужно проверять его не только на nil, но и на присутствие на сервере с помощью alife():object(i)

на мой взгляд это имеет смысл только если перебираешь все возможные id циклом. Тогда действительно встречаются чисто клиентские объекты (например фейковые ракеты вертолёта, РПГ и родственные им гранаты после броска). Но всё же слишком усердствовать с этими проверками не стоит.

 

3. game:time() больше за АМК-ное игровое время, полученное из start_time alife.ltx, приблизительно на 4 с половиной дня (попадает на 26 апреля :) ) и непонятно зачем при установке переменной StartTime от времени из конфига отнимаются сутки ?

Если честно, то ничего не понял. Можно подробнее про смещение в 4 дня?

Разница между game:time() и миллисекундами вычесленными

function game_milliseconds()
    if StartTime == nil then
        getStartTime()
        if StartTime == nil then
            return 0
        end
    end
    local gtime = game.get_game_time()
    local seconds = gtime:diffSec(StartTime)
    local y,m,d,h,min,sec,ms = gtime:get()
    return (seconds * 1000 + ms)
end

составляет приблизительно 400499000 миллисекунд (вот же.... а в скрипте забыл дописать 000 в хвосте значения разницы :() sapsan

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

ловля блох. Потери на вычислении случайных чисел мизерны. Гораздо больше плохо организованной (или никак не организованной) работы с таблицами. До сих пор ещё густо рассыпаны цепочки if-elseif-elseif..., которые можно было бы при желании заменить на выборки из таблиц, что дало бы прирост в разы или даже порядки.

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

 

(нужно сравнить скорость math.random() и функции выборки из таблицы готовых значений).

Это очень просто сделать. Для этого есть класс profile_timer. Я описывал его в "справочнике"

Проверил:

! Cannot find saved game ~~~ time spend tbl[i]: 1416.4332275391
! Cannot find saved game ~~~ time spend math.random(): 5592.96875
! Cannot find saved game ~~~ time spend tbl[i]: 1451.8170166016
! Cannot find saved game ~~~ time spend math.random(): 5583.2153320313
! Cannot find saved game ~~~ time spend tbl[i]: 1484.1917724609
! Cannot find saved game ~~~ time spend math.random(): 5778.8422851563

Преимущество очевидно. Правда не ясно как там будет с глобальной таблицей... sapsan

 

Это сравнение с рабочим вариантом? Я представляю это себе примерно так:

random_nums = {} -- массив случайных чисел
-- здесь инициализация массива непременно с помощью table.insert() и ни в коем случае не с помощью random_nums[i] = ...
random_count = table.getn(random_nums)... -- количество элементов в нем
current_num = 1 -- текущее число

function my_rand()
    local res = random_nums[current_num]
    current_num = current_num + 1
    if current_num > random_count then current_num = 1 end
    return res
end

Всё наваял экспромтом и не проверял.

Но выходит не так уж мало кода. Вот если это сравнить с просто вызовом math.random(), будет такой-же результат?

    local t = profile_timer()
    local tbl = {}
    for i = 1, 65534 do
        table.insert(tbl, math.random())
    end
    t:start()
    for i = 1, 65535 do
        local a = tbl[i]
    end
    t:stop()
    get_console():execute("load ~~~ TIME SPEND tbl[i]: "..t:time())
    
    t = profile_timer()
    t:start()
    for i = 1, 65534 do
        local a = math.random()
    end
    t:stop()
    get_console():execute("load ~~~ TIME SPEND math.random(): "..t:time())

и так трижды.

Завтра попробую по-взрослому :)sapsan

 

Эх, баловство это всё. Однако проверил.

local random_nums = {} -- массив случайных чисел
random_count = 0
current_num = 1 -- текущее число
function random_init()
    random_nums = {}
    math.randomseed(game.time())
    for i=0,100000 do
        table.insert(random_nums, math.random())
    end
    random_count = table.getn(random_nums) 
    current_num = 1
end

function my_rand()
    local res = random_nums[current_num]
    current_num = current_num + 1
    if current_num > random_count then current_num = 1 end
    return res
end

 

 

random_init()
local t = profile_timer()
t:start()
for i = 1, 10000 do
    local a =0
end
t:stop()
log(t:time())
local t = profile_timer()
t:start()
for i = 1, 10000 do
    local a = my_rand()
end
t:stop()
log(t:time())
local t = profile_timer()
t:start()
for i = 1, 10000 do
    local a = math.random()
end
t:stop()
log(t:time())

 

три цикла: первый измеряет потери на организацию собственно цикла, второй - цикл с самопальной функцией, третий - с встроенной.

Результаты.

потери на организацию цикла t1 = 130.6

цикл с самопальной функцией t2 = 1134.6

цикл с с встроенной функцией t3 = 1044.6

Меряем в убитых енотах, но они сокращаются.

коэффициент "замедления" = (t3-t1)/(t2-t1) = 0.91

 

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

 

Что для меня было интересно, что хотя методика с предвычислением и оказалась медленнее, но не настолько, как я ожидал. Это косвенно подтверждает репутацию Lua как одного из самых быстрых скриптовых языков. Во многих случаях, особенно в комбинации с JIT компилятором, Lua код уступает компилированному не более чем в несколько раз. В отдельных случаях, когда Lua код интенсивно использует готовые скомпилированные функции можно ожидать почти равной производительности.

Чуть оптимизировал код:

local random_nums = {} -- массив случайных чисел
local current_num = 1 -- текущее число
function random_init()
    random_nums = {}
    math.randomseed(game.time())
    for i=1,1024 do
        table.insert(random_nums, math.random())
    end
    current_num = 1
end

function my_rand()
    current_num = current_num + 1
    if current_num == 1025 then current_num = 1 end
    return random_nums[current_num]
end

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

 

t:start()
local rnd = my_rand
local a
for i = 1, 10000 do
    a = rnd()
end
t:stop()
get_console():execute("load ~~~ TIME SPEND a = my_rand(): "..t:time())

Результат:

time spend a = 0: 32.611957550049

time spend a = my_rand(): 571.32788085938

time spend a = math.random(): 795.11920166016

Получаем "ускорение" в 1.41. Но оно того не стоит из-за того, что была локализированна функция my_rand(), а основной выигрыш именно из-за этого. sapsan

 

хе хе. Тогда уж надо по честному так же поступить и для math.random. В этом случае получим

служебные расходы: 119.4

локализованная my_rand: 937.3

локализованная math.random: 688.8

И имеем по прежнему замедление, только ещё больше чем раньше: 1.44 раза. Это вполне понятно, поскольку при вызове math.random надо дважды искать элемент в таблице: math в глобальной таблице имён и random в таблице math.

 

Кстати, почему "не стоит"? Это хороший резерв оптимизации - запоминание глобальных многоэтажных ссылок в локальных переменных. Это рекомендуют делать и авторы Lua.

 

Я имел ввиду, что не стоит делать таблицу с наперёд заданными случайными значениями. sapsan

 

З.Ы.: Интересно, это самый длинный пост на форуме или ещё нет?

 

Думаю, что вопросы исчерпаны. :)sapsan

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

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

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

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

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
Ссылка на комментарий
..набор clsid без боеприпасов и гранат

sapsan, что-то боеприпасов я там и так не видел :)

Думаю, пойдёт (это ведь фактически что и было, но таблицы вместо elseif работать будут явно быстрее).

Кстати, если уж на то пошло, IsMonster() и IsStalker() тогда тоже без ифов нужно сделать :)

 

И еще: сделал такое наблюдение: хотя ф-ции выглядят так IsMonster (object, class_id), IsStalker (object, class_id), тем не менее, нигде не нашел, чтобы задавался второй параметр при обращении: везде передается обж. И при этом, обращений к этим ф-циям в скриптах - много, даже очень (IsStalker - более сотни, isWeapon, IsMonster - по неск. десятков)! Отсюда делаю вывод, что чем оптимальнее по скорости это будет срабатывать, тем больший выигрыш быстродействия мы сможем получить.

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

Железо: Intel Core i5 9400F / 16Gb DDR4 2400MHz / SSD NVMe M.2 Samsung 970 EVO Plus 256Gb / GF GTX 1050Ti 4Gb Ось: Win10x64


Подарки

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

    Я пытался понять логику работы ЧУ из кода, но признаться не осилил до конца. Объясните мне плз, как должен проходить час ужаса? Если не трудно, подкиньте также сохранёнку желательно на момент перед началом ЧУ и на локации, где он есть.

     

    ЧУ работает строго по игровым минутам + использует инфопорции как флаги.

    Начинается

        -- звук обратного отсчета
        if timeh == horror_begin_time.h and timem >= horror_begin_time.m and timem <= horror_begin_time.m + 2 then

    Тревожатся неписи - ключается состояние тревоги так

                        npc_position = npc:position()
                        position = vector():set(npc_position.x + math.random(-5,5), npc_position.y, npc_position.z + math.random(-5,5))
                        state_mgr.set_state(npc, "hide_s_right", nil, nil, {look_position = position})

    Выдаётся случайное сообщение о начале ЧУ

    presoobj()

    В момент старта есть вероятность 50/50 удачного запуска. В случае удачи - успокаиваем неписей, получаем спецеффекты и спавн монстров. В случае неудачи - лишь соответствующее сообщение.

    Потом, если игра была загружена с сохранения, восстанавливаем спецеффекты.

    В конце ЧУ убираются спецеффекты и живые монстры.

    И в самом конце отбираются инфопорции. sapsan

     

    Два вопроса:

    1. Сирена в начале должна просто проиграться один раз?

    Да.

    2. Не понял с "успокаиванием" неписей. Почему они успокаиваются в случае удачного начала ЧУ? Не наоборот?

    Они "успокаиваются" сразу же перед спавном монстров или в случае "поломки" установки. Если их не "отпустить", то будут большие потери - монстры их начнут рвать. А само "успокоение" не заметно, так как сразу же получаем кучу спецеффектов, звуков и монстров. Зато неписи моментально реагируют на монстров.

     

    В качестве предварительного мнения:

    1. Проверка на начало выглядит ненадёжной. На мой взгляд можно запросто её "проспать", поскольку во время сна игровое время меняется очень быстро.

    Так было задумано или допущено.

    2. Или я чего не понял, или в течении всего времени стартовой проверки (1 или 2 минуты) мы постоянно запускаем один и тот же звук вместо того, чтобы запустить его один раз.

    Так был реализован звук обратного отсчета. Искать чесный звук мне было в лом - поэтому так и оставил.

    3. Инфопоршенов что-то очень много (четыре). На мой взгляд двух достаточно.

    А если учесть все фазы и возможность игрока сохраниться и загрузится или перейти на другую локацию? Я и так добавил восстановление спецеффектов для таких случаев.sapsan

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

    Плагины Total Commander для работы с игровыми архивами:

    Архиваторный плагин (для работы с одиночным архивом): link1 link2

    Системный плагин (для распаковки установленной игры): link1 link2

     

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

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

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

    Смастерил сигнализацию на предмет зависания биндера актора. Нашёлся способ организовать альтернативное периодическое выполнение функции и за счёт этого поставить сторожевой таймер. При зависании биндера по идее должен сразу раздаться непрерывный противный писк.

    [spoiler=в файле _g.script]

    counter = 0
    prev_watch = 0
    function watch_condition()
        counter = (bind_stalker.watch_value == prev_watch) and (counter + 1) or 0 
        prev_watch = bind_stalker.watch_value
        if counter > 5 then 
            if db.actor then
                local snd_obj = xr_sound.get_safe_sound_object("detectors\\da-2_beep1")
                snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 2.5)
            end
        end
        return false
    end
    function dummy_action()
        return false
    end
    function start_game_callback()
        level.add_call(watch_condition, dummy_action)
        ---
        ...
    end

    [spoiler=в файле bind_stalker.script]

    watch_value = 0
    function actor_binder:update(delta)
        watch_value = game.time()
        ...

     

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

     

    Плагины Total Commander для работы с игровыми архивами:

    Архиваторный плагин (для работы с одиночным архивом): link1 link2

    Системный плагин (для распаковки установленной игры): link1 link2

     

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

    magnit,

    Если не трудно выложи правленные файлы

    Если только мне пришлют два упомянутых файлы и номер версии солянки и под какой патч. У меня к сожалению всё в полуразобранном состоянии, и я это настраивал не под солянкой.

     

    Shadowman,

    когда, где

    Я это стал делать тогда, когда словил несколько вылетов подряд. Тогда просто поставил вывод в биндер и понял, что он останавливается, что само по себе вылет объясняло, но непонятно было в какой момент начинается подвисание. Тогда и сделал сигнализацию. Если писк начнётся после определённого действия - взятия/сдачи квеста, убийства монстра и т.п., то хотя бы станет ясно, где копать. Дальше надо ставить отладочный вывод на это место и ловить вылет ещё раз, но уже с подробной трассировкой проблемного места. Как-то так.

     

    Плагины Total Commander для работы с игровыми архивами:

    Архиваторный плагин (для работы с одиночным архивом): link1 link2

    Системный плагин (для распаковки установленной игры): link1 link2

     

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

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

    Хотя, возможно, что именно обработку какого-то конкретного сработавшего дропа нужно отрихтовать. Тогда очень поможет как раз то, что предложил тов. malandrinus.

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

    Думаю, можно, как и раньше, заполучить эту бяку, просто вывалив на землю тысячи две патронов. А дальше - это уже как машинка справится :)

     

    malandrinus, логично. Как думаешь, постоянно такая штука, навешенная в апдейте, не сильно напряжна будет для движка?

    ЗЫ: Честно говоря, один раз всего у себя ловил подвис биндера ГГ, 3 допы назад :)

     

    malandrinus, правильно ли я понимаю конструкцию

    counter = (bind_stalker.watch_value == prev_watch) and (counter + 1) or 0
    ...
    if counter > 5 then

    в течение пяти "тактов" апдейта первое выражение было true (остановка сердца?) и тогда можно накопить цифровое значение отличное от нуля (иначе, в нормальной ситуации, логическое будет false, т.к. игровое время увеличивается на несколько мс с каждым "тактом" и тогда каунтер будет = 0), так?

    А на чем основана уверенность, что если этот counter > 5, то это значит, что биндер повисший (т.е. 4 апдейта - еще не критично, или может можно и 10 пропустить, а на 11-й "очухается" :) )

     

    magnit, вот. Пробуй.

    (правку отбил тегом -- malandrinus ...... -- /malandrinus)

     

    malandrinus, еще вопрос:

    counter = (bind_stalker.watch_value == prev_watch) and (counter + 1) or 0

    работает быстрее, чем такое

    if bind_stalker.watch_value == prev_watch then
         counter = counter + 1
    else
         counter = 0
    end

    или просто более лаконично ?

     

    Dennis_Chikin, У меня тоже работает. Заметил "ложные срабатывания" во время сна и в момент, когда висит окно диалога перехода на локацию.

    Реальных подвисаний биндера пока спровоцировать не удалось :)

     

    Dennis_Chikin, а чем мешает gps_habar? Хочешь - попробуй слегка оптимизированный gps_habar ;)

    (убрано логирование: все равно отладку никто смотреть не будет, а строчки собираются + прямая вставка в таблицы)

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

    Железо: Intel Core i5 9400F / 16Gb DDR4 2400MHz / SSD NVMe M.2 Samsung 970 EVO Plus 256Gb / GF GTX 1050Ti 4Gb Ось: Win10x64


    Подарки

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

    malandrinus,

    Работает. Потестил на том самом файлике, где зависание 50/50 сразу после загрузки.

     

     

    Вопрос ко всем: если оторвать gps_habar.sript (всего нашел 9 мест) - это вроде ни на что не должно повлиять кроме собственно возможности ставить метки ?

     

    Shadowman,,

    Я сейчас пытаюсь проверить пару странных идей, и мне не нравится, например, то, что он вызывается "over 9000" раз из, например, только add_fresh_meat только еще где-то между началом синхронизации и стартом уборщика. Что он при этом делает сам - я даже и предположить боюсь.

     

    Изменено пользователем Dennis_Chikin
    Ссылка на комментарий

    Shadowman,

    Как думаешь, постоянно такая штука, навешенная в апдейте, не сильно напряжна будет для движка?

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

     

    правильно ли я понимаю конструкцию
    Да, всё так.

     

    А на чем основана уверенность, что если этот counter > 5, то это значит, что биндер повисший
    На самом деле при том, что сторожевой таймер работает синхронно с апдейтом, вообще нет необходимости в счётчике. Достаточно просто сравнить. Я ввёл счётчик потому, что был уверен, что апдейт актора идёт с частотой 40 мс, т.е. раза в два-три медленнее, чем fastcall. Но оказалось, что это не так. Число 5 взято с потолка. В расчёте на ситуацию с несинхронной работой апдейта и сторожевого таймера этого хватило бы с запасом, и начало срабатывания будет практически сразу (через 1-2 десятых секунды).

     

     

    работает быстрее, ...или просто более лаконично ?

    почти неотличимо одинаково, я измерил.Но мне так больше нравится =)

     

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

    sapsan,

    1. Сирена в начале должна просто проиграться один раз?

    Да.

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

    Так был реализован звук обратного отсчета. ...

    По ЧУ у меня одни вопросы. Реализация там откровенно мутноватая. Но вот это самый большой вопрос. Я хотел бы ещё раз уточнить, что именно мне не нравится. Там в течении двух игровых минут непрерывно запускается звук сирены. Это означает, что он начинает играть с каждым очередным апдейтом. Это в свою очередь должно означать, что за минуту будет запущено несколько тысяч звуков. Столько звуков одновременно слышать невозможно, и они превратились бы в простой шум. Однако, насколько я понял, есть некий лимит на количество одновременно играющих звуков и этот лимит довольно быстро исчерпывается. В итоге от начала проигрывания первого звука до исчерпания очереди проходит что-то в районе секунды или меньше. Далее мы слышим все эти звуки (которые влезли в лимит) одновременно. Поскольку они смещены по фазе не очень сильно, то сирена всё-таки различима, хоть и весьма искажена.

    Это так задумано? На мой погляд - как-то это неправильно.

    Так было сделано изначально и этого я не менял. Можно обратится в тему озвучки за помощью. Думаю там "организуют" нормальную сирену и обратный отсчет.

    Но проблемы с сохранением во время начала ЧУ я больше подозреваю в начальном "пугании" неписей. Пока не могу заняться ЧУ... sapsan

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

    Плагины Total Commander для работы с игровыми архивами:

    Архиваторный плагин (для работы с одиночным архивом): link1 link2

    Системный плагин (для распаковки установленной игры): link1 link2

     

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

    Решил выложить наблюдени от теста сигнализации от malandrinus может пригодится, если нет то можно стереть: сигналка срабатывает возле тайников, не всех но из 10 около 8 писк был, пищала возле моей глобальной нычки (три мешка от Сидора с коллекцией костюмов, оружия, артов) после того как вычистил нычки наполовину (продал все арты, часть оружия, батоны,водку, колбасу) писк прекратился, есть писк возле вечнотрупов, но не всех а соляночных (трупп монолитовца в х10 например), хотя там же возле труппов учённых и военных по квесту на экспедицию писка нет (возможно какая то проверка запускается, ведь экспедиция была пустая, а в труппе монолитовца были квестовые документы), встретил два зависа, повторимых у меня, на АГ если возле перехода на Янтарь завалить 3 кошек идёт завис, если оставить их в покое то зависа нет (думал что от перевеса, но пришёл полупустым кошек нет так что пока проверить не получилось), идёт писк возле иммитаторов. Вроде пока всё.

     

    Shadowman Проц двухъядерный пень 6850 3.00 GHz, карта радеон 5770, 6 Гигов оперативки, ложные срабатывания возле точек перехода и во время сна есть, нычка лежит в таком месте что рестрикторов там точно никогда не было, за территорией Бара справа, если со стороны Свалки смотреть, в начале подьёма пригорка. Сейв вот http://webfile.ru/4523559 (отличие от оригинала 21 слот и вес повышен, больше ничего лишнего нет).

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

    Относись к людям так, как хочешь чтобы они относились к тебе.

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

    malandrinus, спасибо, понятно. Про 1.0006 - вообще откровение. Теперь понятно, почему с переходом на апрельскую Соль народ отрапортовал про снижение производительности именно на 1.6 в сравнении с 1.4.

     

    magnit, прям коллекция загадок Зоны какая-то.

    Я подозреваю, что комп у тебя - не шибко мощный (процессор какой)? И еще: скорей всего, дело в рестрикторах, при попадании в которые, видимо, учащается апдейт (с нычками и их вычищением, правда, нестыковочка слегка... хотя - может, нычки свои тоже в зоне рестриктора какого разместил). Чем слабее проц - тем меньше ему нужно, чтобы "завесился" биндер. Вот как-то так, имхо.

     

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

    Если твой игровой набор близок к оригиналу 19.04 - скинь сейвик вблизи гарантированного подвиса - просто интересно. Я думаю, что от компа все же сильно зависит (а точнее даже, мощности процессора).

     

    magnit, Ок, спасибо, посмотрим.

    ЗЫ: ну если Солянка "кладёт" уже и 3Ггц двухъядерник - тогда совсем плохо дело :crazy:

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

    Железо: Intel Core i5 9400F / 16Gb DDR4 2400MHz / SSD NVMe M.2 Samsung 970 EVO Plus 256Gb / GF GTX 1050Ti 4Gb Ось: Win10x64


    Подарки

  • Ссылка на комментарий
    после того как вычистил нычки наполовину ... писк прекратился

    Хм. Писк не должен прекращаться, если уж начался (исключая переходов между уровнями) Попробуй, если не влом, увеличить порог счётчика с 5-и до скажем 20-и. У меня есть подозрение, что частота апдейтов и соотношение с частотой fastcall-а связано с производительностью.

     

    Плагины Total Commander для работы с игровыми архивами:

    Архиваторный плагин (для работы с одиночным архивом): link1 link2

    Системный плагин (для распаковки установленной игры): link1 link2

     

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

    Заборол ложный писк при паузе игры. В функции function watch_condition() надо заменить

    if db.actor and then

    на

    if db.actor and not device():is_paused() then

    Кроме того, имеет смысл ставить порог счётчика не пять, а 10 (см. ниже).

     

    Похоже, что период апдейта зависит от:

    - типа рендера

    - общей производительности компьютера

    - производительности видеокарты (может даже больше, чем от процессора)

     

    Также похоже, что частота апдейта и fastcall-ов могут не совпадать на слабых конфигурациях. От патча это не зависит. Я перепроверил. На моём текущем компьютере период совпадает на всех патчах. А первые эксперименты по замеру тайминга я делал на другом компьютере и отчётливо помню, что на четвертом патче период был чётко 40 мс, а fastcall шёл раза в 2-3 быстрее. В общем, стабильности никакой. Рассчитывать на какие-то постоянные цифры нельзя. Ну и значит алгоритм проверки имеет смысл оставить так, как есть сейчас (со счётчиком) и не упрощать.

     

    P.S.: можно и не предотвращать ложный писк. Его наличие показывает, что механизм жив. Но скорее всего скоро надоесть слушать =)

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

    Плагины Total Commander для работы с игровыми архивами:

    Архиваторный плагин (для работы с одиночным архивом): link1 link2

    Системный плагин (для распаковки установленной игры): link1 link2

     

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

    Странно, что никто не вспомнил, что в АМК давным давно уже есть детектор зависания биндера актора :)

    в начале actor_binder:update переменной amk.oau_watchdog выставляется значение 100, а потом в определенных местах уменьшается

    Перед выходом из actor_binder:update amk.oau_watchdog присваивается 0

    А в апдейте биндера НПЦ проверяется, что amk.oau_watchdog равно 0. Если не равно 0, то значит биндер ГГ завис и по значению можно понять в каком месте. Аналогичную штуку можно сделать и для биндеров НПЦ.

     

    Этот механизм работает как часы, нет проблем ни с паузами, ни с загрузкой уровня и прочим

     

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

    Изменено пользователем sapsan
    Ссылка на комментарий

    Доброго дня всем!

     

    Да простит меня Уважаемый куратор, что пишу не в тему Вылетов, но хотелось бы поделиться с мастерами наблюдениями по вылету/завису ... attempt to index local 'respawner'

     

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

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

     

    1. В файле se_respawn.script в функции function se_respawn:create(prob) перед вызовом amk.on_REspawn(obj,self) добавил вывод

    sak.dbglog("SPAWN [%s] -> [%s]", tostring(self:name()), obj:name())

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

     

    У меня таким "нехорошим человеком" оказался некий товарищ свободовец из Варлаба, имеющий

    self:name

    warlab_svobodovzu_respawn_1

    и obj:name

    warlab_svobodovzu_respawn_256852

     

    2. Когда виновник был идентифицирован, в ту же функцию function se_respawn:create(prob), только немного выше - перед строкой

    if spawn_section == "" then

    добавил проверку

    if tostring(self:name()) == "warlab_svobodovzu_respawn_1" then

    return false

    end

    (наверное, было бы более правильно по obj:name, но так радикальнее...)

     

    3. Загрузил сейв перед зависом - нет человека - нет проблемы. :rolleyes:

    Наверное, если бы такой нехорошего свойства респавн был не один, - пришлось бы

    после первого ловить следующий завис. У меня он был один.

    Игра дальше идет нормально (может, до следующего "нетакого"? :rolleyes: ).

     

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

    Но, вероятно, можно попытаться доиграть до победного :rolleyes:

    После прохождения момента некорректного респавна правочки в скрипте закоментировал.

    Про запас. :rolleyes:

     

    Хороший вариант для выявления проблемных неписей. Но о таких нужно тут же сообщать Архаре - возможно проблема в их конфиге, логике или ещё каких настройках. sapsan

     

    Архара,

     

    warlab_svobodovzu_respawn_1 - это не человек, это респавн гулага. Тем самым был убит респавн свободы на Варлабе.

     

    Эх, не люблю убивать, даже респавн... Но надоевший завис по 'respawner' убивает напрочь весь мир Солянки. Приходится выбирать, чтобы хотя бы таким боком продвинуться дальше.

    Надеюсь, с Витамином договоримся ;)

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

    Компьютер: Intel CoreDuo E8200 2,66GHz, GeForge 9800 GT 1024 mb, 4 Gb ОЗУ, Windows 7 64 bit.

    Солянка: Народная Солянка от 19.04.2010, 14.08.10 + 3.09.10 + широкий монитор + 18.11.10

     

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

    WhatAbout,

    warlab_svobodovzu_respawn_1 - это не человек, это респавн гулага. Тем самым был убит респавн свободы на Варлабе. Спасибо за инфу, покопаю.

    Еще 10 лет таких цен, зарплат и пенсий, и вместо переписи населения будет перекличка

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

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

    AMK-Team.ru

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