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

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


n6260

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

sapsan

 

Ребят, вы чего? table.getn можно использовать только в циклах, не меняющих число строк в таблице. Я ещё очень давно об этом писал, это одна из самых частых ошибок. table.getn - это динамически рассчитываемая величина, на каждом проходе цикла если вы в нём меняли таблицу, она будет изменять своё значение и цикл поведёт себя совершенно непредсказуемо:

 

Если вы строите цикл по таблице следующим образом:

for i=1, table.getn(table_name) do

 

То никогда, ни при каких обстоятельствах не используйте внутри этого цикла удаление/добавление строк, т.е:

table_name[i] = nil

 

или

table.remove(table_name, index)
table.insert(table_name, index)

 

применительно к таблице по которой гоняете цикл!!! При использовании это приводит к тому, что количество строк в таблице уменьшается, а цикл пытается получить из таблицы строки сверх существующего количества, что в итоге приводит к выходу цикла за отведённый ему диапазон памяти. Результатом будет веер самых разнообразных последствий, самое безобидное из которых это безлоговый вылет на рабочий стол, а серьёзное — сбой в работе а-лайфа с последующим боем сейвов! Тяжесть последствий зависит от того, какие действия вы выполняете внутри цикла кроме удаления строки.

 

Чтобы избежать подобных ситуаций, стройте циклы по таблице лучше следующим образом:

for k, v in pairs(table_name)

 

А удаление строк внутри них делайте вот так:

table_name[k] = nil

 

Циклы же вида for i=1, table.getn(table_name) do следует использовать только для операций, не изменяющих структуру изменяемой таблицы!

 

UPD:

 

Отсюда и гиперспавн и всё остальное. Нельзя делать цикл по таблице используя операторы получения непосредственного количества её текущих строк. Получать количество строк надо перед циклом в отдельную переменную, и потом гонять цикл уже по ней. Кроме того вот этот код:

 

388      -- экстренный спаун минимального количества объектов
389      if table.getn(self.spawned_obj) < self.min_count then 
390        while table.getn(self.spawned_obj) < self.min_count do
391          --sak.dbglog("RESPAWN: [%s] very small object", tostring(self:name()))
392          if self:create(100) == false then
393            return
394          end
395        end
396        return
397      end

 

Просто обязан исполняться бесконечно. Так как table.getn(self.spawned_obj) не меняет в данном цикле значения, self.min_count - тоже. Условие всегда выполняется, это добро будет вертеться до второго пришествия.

 

Это понятно. А где там попытка выйти за границы таблицы (в сообщении от Kolmogor указан именно проблемный кусок кода)?

Почему это table.getn не меняет значения ? Он расчитывается при каждой проверке, в отличии от случая с for sapsan

 

Ага, точно, меняет. Тады тот самый случай и есть

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

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

sapsan

 

В том куске что Колмогор отпостил, там выхода за границы нету, там просто само условие цикла бесконечно выполняется судя по всему. Но! Выход за границы запросто может произойти если где-то в другом месте когда в этот же момент происходит изменение таблички self.spawned_obj.

 

А про table.getn в том контексте вспомнил, что вот так:

 

while table.getn(self.spawned_obj) < self.min_count do

 

Делать вообще нигде крайне нежелательно - вдруг действительно где-то ещё эта табличка сейчас изменяется. Вот так желательно:

 

local tab_size = table.getn(self.spawned_obj)
while tab_size < self.min_count do

 

В этом случае тогда уже можно сделать так:

local tab_size = table.getn(self.spawned_obj)
while tab_size < self.min_count do
tab_size = tab_size + 1

. И это - не более, чем оптимизация. sapsan

 

table.getn вообще в условиях циклов пользовать нельзя категорически.

 

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

Kolmogor

 

По-моему нормальный цикл - собственно этот цикл еще с оригинальной ТЧ идет

 

Роман, я тебя умоляю, где ты видел в оригинальном коде нормальные циклы - я столько там ошибок находил в свое время, причём тупых до ужаса... :lol:

 

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

sapsan

 

Ну не совсем оптимизация. Скорее "обезопаснивание". Потому как если таким образом сделать, то переменная tab_size получит значение однократно перед выполнением тела цикла и на его протяжении значение менять будет только итератором. А если пользовать внутри условия table.getn, то получится что в условии каждый раз проверяемое значение рассчитывается заново. А оно может крайне опасным получиться в случае нештатной ситуации и циклу кранты.

 

 

 

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

Kolmogor

 

Хотя может и наоборот уйти ото всех функций table.*

 

Обоснованно, кстати, было бы. Я давно уже ушёл, прямой инициализации строк присвоением значения по ключу и их обниления вполне достаточно.

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

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

sapsan

 

Ну совсем от функций table не отказаться, всё таки table.sort и table.concat нам нужны ещё однозначно, но вот удаление добавление строк надо просто условиться делать только напрямую, ручками. И считывать число строк только через #.

 

Насчёт родных скриптов от GSC - уходить разумеется вместе с ними, потому как там кое-где есть такие обработки, и я часть из них, показавшихся наиболее подозрительными, у нас в OGSE переделал. Никаких проблем с ними не было после этого.

 

UPD:

 

Вообще же я от циклов for по таблице, с условием заданным на число строк давно уже вообще отказался и где это только возможно использую конструкцию

 

for k,v in pairs(table) do

 

Этой обработке совершенно безразлично что происходит с таблицей, она работает пока не получит последнюю пару из неё.

 

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

dimos

 

Самое интересное, что вылета не было и игра пошла спокойненько дальше, чего-то планировщик там проапдейтил...

 

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

 

-- Крешнуть игру (после вывода сообщения об ошибке в лог)
function abort(fmt, msg)
    local message = tostring(msg)
    local reason = string.format(fmt, message)
    assert("ERROR: " .. reason)
    printf("ERROR: " .. reason)
    dbglog("%s", reason)
    printf("%s")

    local crash
    local ooops = 1/crash
end

 

Добавленные строки - две последних. В итоге если при вызове вылет не случился на операторе printf("%s") (как по идее должен), то игра 100% вывалится на арифметической операции с неинициализированной переменной crash.

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

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

Dennis_Chikin

 

Логи не глядел, так как в Солянке не ковырялся сильно сам, однако касательно удаления монстров вот чего могу сказать из собственного опыта:

 

1) Монстра нельзя безболезненно отрелизить если он жив и уже в онлайне. Если он в онлайне, самым простым методом будет его сначала прибить, любым типом хита с запредельным уроном, чтобы он издох. Затем отрелизить трупик с небольшим интервалом, в треть секунды хотя бы, чтобы удаление происходило когда он точно помер. Попытка отрелизить из онлайна живого монстра на очень многих зверюках вызывает вылеты либо повисания алайфа, что ещё хуже. Из оффлайна же - можно без проблем. Поэтому проверь алгорим прибивания монстров и если надо внеси соотв. коррективы.

 

2) Касательно psy_dog_phantom - это очень странные фантомы собак в игре, и часто глюкавят. Они периодически косо сохраняются в сейвах, причем не одна битая например, а все и сразу. Сейв в итоге не грузится. Я у нас в OGSE не мудрствуя сделал просто их автоприбивание ещё при спавне на загрузке сейва, пси-псина всё равно если рядом находится моментально нарожает новых. Тебе тут возможно стоит сделать их автоприбивание при завершении игры.

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

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

sapsan

 

Ну собственно что я и предлагал. Пользовать для количества строк # и как самый безопасный вариант цикла - in pairs. Проверку же ~nil надо в любом случае выполнять всегда и везде где только можно, и я это обычно всегда и делаю, сразу избавляясь от возможных с этим проблем.

 

Dennis_Chikin

 

Касательно сакрального смысла net_destroy() - этот метод автоматически вызывается когда монстр или непись уходит в оффлайн, а не при смерти. Это нормальное поведение, так и должно быть. И если монстр носится на границе расстояния переключения, то он будет постоянно в онлайн и затем снова в оффлайн прыгать. Касательно того же что там в обработке напихано я если честно вообще половину не пойму зачем туда запихали. Там ещё и перезапись таблички рестрикторов в нетпакете сидит непонятно зачем, конечно оно тормозить будет как сумасшедшее, обработка нетпакетов на запись работает очень медленно, и к тому же её нельзя слишком часто производить, так как есть вероятность повредить данные. И ещё торможения при этих обработках вызывают выводы в лог - они тоже довольно тормозные.

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

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

Monnoroch

 

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

 

UPD: Ну и ещё иногда бывает дурацкий недогляд - когда переменную вместо того чтобы просто присваивать, объявляют как local внутри определённй кодовой ветки.

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

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

Monnoroch

 

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

 

А на проверки да, забывать нельзя. Впрочем я в любом случае их вставляю тогда когда код уже 100% проверен в работе.

 

Насчёт родного кода, да, часто такое ощущение что он построен буквально на взаимоисключающих багах. Кстати, коллбэки родные вот как раз надо пошагово отлаживать местами. Я же таки победил стандартный вылет smart_terrain:1137 - который в функции on_death - он представлял собой как раз вполне реальный пример "плавающего" бага. Это тот который при вызове obj:smart_terrain_id(). Изрядно повеселился, когда выяснилось что код теряет работоспособность при определённом форматировании (!!!). Т.е. логически верен, ни одной опечатки, но стоит изменить отступы и форматирование строк - начинаются сверхстранные вылеты, словно в движке есть какая-то привязка на номер строки кода в этом скрипте. Я это дело и как вылечил в итоге описывал в статейке на stalkerin, сюда кидать не буду, там много очень.

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

sapsan

 

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

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

Kolmogor

sapsan

 

Так, ну вообще это больше всего похоже на отличия между разными версиями самого Lua. А какая именно версия строена в игру? И не изменяли ли её самостоятельно разработчики?

Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей :wink:

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


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

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