Dennis_Chikin 3 658 Опубликовано 24 Апреля 2014 Поделиться Опубликовано 24 Апреля 2014 (изменено) "Что у нее внутри, и как это сделать лучше". Для тех, кто уже разбирается в скриптах, конфигах, текстурах и "других страшных словах" ©, и имеет желание и время действительно делать их лучше.См. подробности в первом посте.Тема НЕ является ни столом заказов, ни службой техподдержки, ни справочным бюро. Изменено 28 Апреля 2014 пользователем Dennis_Chikin 2 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Malandrinus 615 Опубликовано 3 Июня 2010 Поделиться Опубликовано 3 Июня 2010 Kolmogor, Странно, что никто не вспомнил, что в АМК давным давно уже есть детектор зависания биндера актора Метода у меня появилась как побочный результат моих изысканий. Я выяснил природу функций level.set_call и захотелось применить на практике. Интересно, кстати, что природа этих функций та же, что и fastcall-ов. И, как мне кажется, они ставят колбек на шаг решателя физики. Это вероятно для более точного управления физическими объектами, но можно использовать для организации независимых от апдейта актора периодических событий. Вот здесь на этом сделан сторожевой таймер, что на мой взгляд удобнее, чем делать это на апдейтах монстров. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
sapsan 336 Опубликовано 6 Июня 2010 Поделиться Опубликовано 6 Июня 2010 (изменено) Чую недоброе А всё - из-за времени и его старых и кое-где до сих пор существующих проблем с переполнением целого 32-разрядного. Например, заглянул в скрипт level_weathers.script новой версии погоды Beautiful Weather и увидел там использование неподходящей функции времени и тут же - сохранение переменных с значением разного времени нужного для скрипта. А сохранение происходит с помощью функции w_u32, которая сохраняет 32-разрядные значения. Также и в скрипте xr_gulag.script, в котором я было изменил получение времени на 64-разрядный результат, но не заметил, что переменные со значением времени сохраняются с помощью w_u32. Тоесть когда значение станет большим за максимально возможное для 32-разрядного целого, то сохранится результат переполнения, а не нужное нам число. Выходит, что нужно всюду ещё и изменить функции сохранения и чтения переменных с временем на их 64-разрядные варианты... Хотя, обычно, такие значения сравниваются с текущим временем и, если они меньше его, то выполняются какие-то действия и значения устанавливаются равными текущему времени плюс интервал ожидания до следующего действия. В этом случае при загрузке игры каждый раз будут производится такие действия и общая логика игры пострадать не должна. Однако никаких гарантий нормальной работы после переполнения нет. P.S. После изменения функций сохранения/загрузки 32-разрядных чисел на сохраняющие 64-разрядные понадобится новая игра. Shadowman, есть соответсвующие функции: number r_u64(); void w_u64(unsigned __int64); number r_s64(); void w_s64(__int64); Shadowman, да - переписать и требовать новой игры. Или сделать два варианта скрипта: 1. только с 64-разрядной записью; 2. с 64-разрядной записью и чтением. 1-й положить в геймдату, загрузить сохранение, сохранится. 2-й положить в геймдату, загрузить сохранение сделанное с 1-м и играть дальше. И так нужно сделать для каждого такого срипта. Изменено 7 Июня 2010 пользователем sapsan Ссылка на комментарий
Shadowman 939 Опубликовано 6 Июня 2010 Поделиться Опубликовано 6 Июня 2010 (изменено) sapsan, а как сохранить 64-разрядный результат ? Ясно. А переконвертить 32-битные таймеры в 64-битные разве нельзя? Применительно к погоде, эти две ф-ции function WeatherManager:load(F) self.update_level = F:r_stringZ(); self.update_time = F:r_u32(); end function WeatherManager:save(F) F:w_stringZ (self.update_level); F:w_u32 (self.update_time); end переписываем в такой вид: function WeatherManager:load(F) self.update_level = F:r_stringZ(); self.update_time = F:r_u64(); end function WeatherManager:save(F) F:w_stringZ (self.update_level); F:w_u64 (self.update_time); end Или для конвертации сейва, вначале запись, а потом и чтение, как ты написал здесь ? ЗЫ: ";" в конце строк - это ведь в люа необязательно вроде? ========== перенес из "багов и вылетов" =========== [spoiler=продолжение по поводу этого способа решения зависа:] У меня получилось убрать вылет/завис даже без "убийства респавна". Но не имею большого количества зависающих сейвов для проверки. Если прокатит - можно просто милли-патчик сделать. Для проверки нужен сейв перед зависом (вылетом). "Пробуем побороть завис" 0. Делаем копию ...S.T.A.L.K.E.R\gamedata\scripts\se_respawn.script для возможности восстановления. 1. В файле se_respawn.script в function se_respawn:create(prob) ищем строку amk.on_REspawn(obj,self) 2. Перед этой строкой добавляем строку (строка будет выводить имена spawner->object в консоль красным, не смущайтесь, и в лог) get_console():execute("load ~ Spawn now ["..tostring(self:name()).."] -> ["..obj:name().."]") 3. Больше ничего править не нужно - только добавить строку. Никакие проверки не нужны. Сохраняем сделанные изменения, пробуем с сейва ДО зависа/вылета. WhatAbout, хочешь сказать, что вывод отладочной строки в лог предотвращает вылет? Прямо скажем, необычный способ... Если помогает - то очень даже прикольно выходит. Фактически, ведь мы просто делаем крохотную задержку этим выводом в лог перед назначением логики респавнеру, и только. WhatAbout, команда get_console():execute("load ~ Spawn now "... означает, что ты пытаешься загрузить игру с именем, являющимся всем тем, что идет после "load". Поскольку такой сохраненной игры, ясное дело, - нет, в консоль и выдаётся "Cannot load saved game" а дальше - все то, что мы написали в этой отладке. Почему именно load ? Потому что в ругательство тогда выдаётся в точности та строка, которая была задана в качестве имени сохранения Я думаю, что тут еще дело в том, что наша конструкция "<переменная>..<переменная>" собирается последовательно, во столько шагов, сколько есть присоединений "..". Если же просто написать стринг, полученная задержка будет меньше. Для проверки можно попробовать написать в эту отладочную строчку пару других переменных. Например, level:name(), db.actor:id() или еще что-нибудь отвлеченное и точно существующее (game.time(), level.get_game_difficulty()). Полагаю, что, если мое предположение про простую задержку как суть лечения вылета - верно, то эффект будет аналогичным. Изменено 7 Июня 2010 пользователем Shadowman Железо: Intel Core i5 9400F / 16Gb DDR4 2400MHz / SSD NVMe M.2 Samsung 970 EVO Plus 256Gb / GF GTX 1050Ti 4Gb Ось: Win10x64 Ссылка на комментарий
WhatAbout 8 Опубликовано 7 Июня 2010 Поделиться Опубликовано 7 Июня 2010 (изменено) Shadowman, Ну, и я перенес. Повторюсь. Сам удивлен Но пока подтверждается на нескольких версиях Соли, даже прошлогодней... Не очень силен в луа, поэтому объяснить достоверно не могу. Вариант простой задержки - может быть, не очень проверял. Но вывод в консоль той же функцией просто левого текста на дает такого эффекта. Что там "внутре" при вызове метода execute - чесно, не разбирался. Но что-то же дает такой эффект. Может, иннициализация какая внутренняя, или проверка корректности чего-то с подрихтовочкой. Был бы признателен, если бы кто разобрался до конца. А пока - по правилу "работает-не трогай". P.S. Добавлю. Характерно, что использование вместо прямого вызова get_console():execute("load ~ Spawn now ["..tostring(self:name()).."] -> ["..obj:name().."]") функции abort abort("Spawn now ["..tostring(self:name()).."] -> ["..obj:name().."]") не спасает - вылет в smart_terrain.script по attempt to index a function value. Хотя что там у нас в abort? Ну, почти то же, если не считать локальную переменную. Как тут быть с "предположением про простую задержку"? Кстати, параметры в функцию по адресу передаются или по значению? dimos, Я это и имел ввиду туманно написав "или проверка корректности чего-то с подрихтовочкой" dimos, Как проверить сработал ли респавн до конца? Существование объекта проверять? Не имел, к сожалению, достаточно времени, чтобы углубиться. se_respawn.script: вызов amk.on_REspawn(obj,self) - amk.script: вызов mod_call("respawned",obj,respawner) - amk_mod.script local sini = respawner:spawn_ini() - строка, на которой и получался завис - отрабатывает. Больше пока не проверял. Изменено 7 Июня 2010 пользователем 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 Ссылка на комментарий
Dennis_Chikin 3 658 Опубликовано 7 Июня 2010 Поделиться Опубликовано 7 Июня 2010 (изменено) Вешается meceniy_art.art_respawn() Симптоматика: строго после 22 часов; внезапно, либо после перехода между локациями - сразу, при загрузке сохранений - сразу - вешается актор. Поставленный watchdog показывает, что "ушел и не вернулся" именно этот вызов. BTW, в оригинале из OGSM спавн производился только после выброса, только на текущем уровне и только 4-х артов с небольшими задержками между каждым. Workaround: перенес все содержимое meceniy_utils.on_actor_update_callback() непосредственно в биндер актора на 10 секундный апдейт, для спавна "черной энергии" прикрутил оригинальный ogsm_surge.script Тестирую. Из положительных эффектов - ЧЭ перестала спавниться "кучками". Shadowman, У меня больше НЕТ этого конкретного зависа. Остались вылеты по стеку при завершении игры из Лабиринта и при проходе бандюков через ж/д на Кордоне на границе радиуса а-лайфа. Необходимость переделки не отменяет то, что подобный спавн надо переносить в куда-нибудь в новости. В качестве средства диагностики зависа - сообщение в ньюсы перед началом и после отработки. Если видим первое, но нет второго - висим именно здесь. Себе такое поставил, видно абсолютно четко. Виновника вылета, кстати, можно отлавливать так же, если подозрительную функцию вызывать с фиксированной задержкой после сообщения. Со снятием координат есть слабооформленная мысль о том, как без этого обойтись. Когда пойму спавн/отключение аномалий - попробую. Или когда разберусь с путешествиями монстров. Перевесить спавн артефактов прямо на них. Изменено 8 Июня 2010 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Ссылка на комментарий
Shadowman 939 Опубликовано 7 Июня 2010 Поделиться Опубликовано 7 Июня 2010 (изменено) Dennis_Chikin, этот спавн в meceniy_art нужно вообще переделать, потому что там несколько скользких моментов. Во-первых, там рандомное вычисление вертекстов зачем-то, да еще и без последующих проверок на валидность (в ogsm_surge - аналогично, кстати). Нужен просто набор выверенных координат и рандомный выбор между ними перед спавном. Сейчас - там для нескольких лок по одному левел/гейм вертексу. В-вторых, если чего завесилось в биндере, никаких ошибок не выдаётся: просто молча вешается и всё (это особенность движка, нельзя там ошибок допускать). ЗЫ: в ogsm_surge принципиально ничего не отличается - те же кривые моменты. И главное - проверки существования вертексов, полученных путём рандомайзеера - нет. dimos, да в том-то и дело что этой строкой выводится лишь отладка в лог - прочти, что я написал в предыдущем посте. ---- WhatAbout, не думаю. Возможен только вылет в случае, если в строку попытаемся запихать переменную, которая нил 7.9, есть такой момент: при сохранении может, какая-то из функций, запускаемых при этом и приводит в чувство игру. Может, даже простое высвобождение (или заполнение) памяти даёт такое эффект - кто ж его знает, как там внутри движка всё это варится Изменено 7 Июня 2010 пользователем Shadowman Железо: Intel Core i5 9400F / 16Gb DDR4 2400MHz / SSD NVMe M.2 Samsung 970 EVO Plus 256Gb / GF GTX 1050Ti 4Gb Ось: Win10x64 Ссылка на комментарий
WhatAbout 8 Опубликовано 7 Июня 2010 Поделиться Опубликовано 7 Июня 2010 (изменено) Shadowman, Возможно ли такое, что в момент подготовки параметров для вызова get_console():execute каким либо образом валидируются некие данные, относящиеся к объекту-респавнеру. Ну, навроде добавления отсутствующего символа конца строки или иннициализации неопределенного явно свойства, etc. Простите мой французский... Shadowman, WhatAbout, не думаю. Возможен только вылет в случае, если в строку попытаемся запихать переменную, которая нил Тогда мне не совсем понятно такое: 1) get_console():execute("load ~ Spawn now ["..self:name().."]") -- Ok 2) local some = tostring(self:name()) get_console():execute("load ~ Spawn now ["..some.."]") -- висим К слову, 3) get_console():execute("load ~ Spawn now ["..level:name().."]") -- висим зато! 4) get_console():execute("", self:name()) -- Ok dimos, Насколько я смог проверить респавн происходит. Статистики, правда, не набрал. Проверял и по id и по name. Изменено 8 Июня 2010 пользователем 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 Ссылка на комментарий
sapsan 336 Опубликовано 9 Июня 2010 Поделиться Опубликовано 9 Июня 2010 (изменено) Ещё одна оптимизация. [spoiler=Код в _g.script] IAmAStalker = {} function IAmAStalkerInit() IAmAStalker = { [clsid.actor] = true, [clsid.script_stalker] = true } end IAmAMonster = {} function IAmAMonsterInit() IAmAMonster = { [clsid.boar_s] = true, [clsid.bloodsucker_s] = true, [clsid.dog_s] = true, [clsid.flesh_s] = true, [clsid.pseudodog_s] = true, [clsid.psy_dog_s] = true, [clsid.burer_s] = true, [clsid.cat_s] = true, [clsid.chimera_s] = true, [clsid.controller_s] = true, [clsid.fracture_s] = true, [clsid.poltergeist_s] = true, [clsid.gigant_s] = true, [clsid.zombie_s] = true, [clsid.tushkano_s] = true, [clsid.snork_s] = true } end [spoiler=Код в amk.script] -- Эта функция вызывается самой первой. Онлайновые объекты недоступны! db.actor недоступен! function on_game_start() mod_call("on_game_start") ver = get_ver() getStartTime() IAmAStalkerInit() IAmAMonsterInit() end local g, se_obj local sim = alife() local t = profile_timer() t:start() for i = 1, 65535 do se_obj = sim:object(i) if se_obj then g = IsStalker(se_obj) end end t:stop() get_console():execute("load ~~~ TIME SPEND g = IsStalker(se_obj): "..t:time()) t = profile_timer() t:start() for i = 1, 65535 do se_obj = sim:object(i) if se_obj then g = IAmAStalker[se_obj:clsid()] end end t:stop() get_console():execute("load ~~~ TIME SPEND g = IAmAStalker[se_obj:clsid()]: "..t:time()) se_obj = sim:object(0) if se_obj then t = profile_timer() t:start() for i = 1, 65535 do g = IsStalker(se_obj) end t:stop() get_console():execute("load ~~~ TIME SPEND only g = IsStalker(se_obj): "..t:time()) t = profile_timer() t:start() for i = 1, 65535 do g = IAmAStalker[se_obj:clsid()] end t:stop() get_console():execute("load ~~~ TIME SPEND only g = IAmAStalker[se_obj:clsid()]: "..t:time()) end ! Cannot find saved game ~~~ time spend g = isstalker(se_obj): 130028.640625 ! Cannot find saved game ~~~ time spend g = iamastalker[se_obj:clsid()]: 106638.2890625 ! Cannot find saved game ~~~ time spend only g = isstalker(se_obj): 38834.3515625 ! Cannot find saved game ~~~ time spend only g = iamastalker[se_obj:clsid()]: 23809.611328125 В чистом виде имеем ускорение в 1,63 раза. P.S. Но удалять оригинальные функции IsStalker() и IsMonster() нельзя - они используются в оригинальных нераспакованных скриптах. Ещё одна оптимизация. [spoiler=Код в _g.script] iniLines = {} function getIniValueFloat(sect, line, default) if not iniLines[sect] or not iniLines[sect][line] then if not iniLines[sect] then iniLines[sect] = {} end local ini = system_ini() if ini and ini:section_exist(sect) and ini:line_exist(sect, line) then iniLines[sect][line] = ini:r_float(sect, line) else iniLines[sect][line] = default end end return iniLines[sect][line] end local sect = "alife" local line = "time_factor" local g t = profile_timer() t:start() for i = 1, 65535 do local ini = system_ini() if ini and ini:section_exist(sect) and ini:line_exist(sect, line) then g = ini:r_float(sect, line) end end t:stop() get_console():execute("load ~~~ TIME SPEND ini:r_float(sect, line): "..t:time()) t = profile_timer() t:start() for i = 1, 65535 do g = getIniValueFloat(sect, line, 0) end t:stop() get_console():execute("load ~~~ TIME SPEND getIniValueFloat(sect, line, 0): "..t:time()) ! Cannot find saved game ~~~ time spend ini:r_float(sect, line): 158315.3125 ! Cannot find saved game ~~~ time spend getinivaluefloat(sect, line, 0): 9088.375 Новый способ быстрее в 17.4 раза! Это, конечно, лабораторный вариант. В реальности при первом обращении к данным будет происходить точно такое же чтение из конфига и внесение их в таблицу, но при наличии данных в таблице ускорение именно таким и будет. Это больше отразится на "сглаживании" игры (снятии заметных и не очень подтормаживаний), чем на реальных FPS. Хотя кто его знает как там всё написано... P.S. При надобности создаются аналогичные функции для других типов данных (getIniValueString(), getIniValueBool(), getIniValueSInt32(), getIniValueUInt32() и т.д.). Изменено 9 Июня 2010 пользователем sapsan Ссылка на комментарий
dimos 10 Опубликовано 20 Июня 2010 Поделиться Опубликовано 20 Июня 2010 (изменено) WhatAbout, к вопросу про респавн и внутренних корректоров: увидел недавно в логе такую штуку: FATAL ERROR [error]Expression : fatal error [error]Function : CScriptEngine::lua_error [error]File : E:\stalker\patch_1_0004\xr_3da\xrGame\script_engine.cpp [error]Line : 73 [error]Description : <no expression> [error]Arguments : LUA error: e:\s.t.a.l.k.e.r\gamedata\scripts\watcher_act.script:112: bad argument #1 to 'find' (string expected, got nil) stack trace: Scheduler tried to update object esc_stalker_respawn_145328 FATAL ERROR [error]Expression : fatal error [error]Function : CScriptEngine::lua_error [error]File : E:\stalker\patch_1_0004\xr_3da\xrGame\script_engine.cpp [error]Line : 73 [error]Description : <no expression> [error]Arguments : LUA error: e:\s.t.a.l.k.e.r\gamedata\scripts\watcher_act.script:112: bad argument #1 to 'find' (string expected, got nil) stack trace: Scheduler tried to update object gar_stalker_respawn_145322 Самое интересное, что вылета не было и игра пошла спокойненько дальше, чего-то планировщик там проапдейтил... Кто-нибудь в курсе, что это за Sheduler и с чем его едят? Dennis_Chikin, скажи хоть как звать этого нехорошего товарища, народ жалуется на этот вылет и с радостью отправит его к праотцам. А потом уже может и поправят ему мозги. dimos, ошибка по watcher_act.script:112 исправлена и войдёт в следующий патч. sapsan Изменено 21 Июня 2010 пользователем sapsan Цензура ограничивает творчество © by me Ссылка на комментарий
sapsan 336 Опубликовано 21 Июня 2010 Поделиться Опубликовано 21 Июня 2010 [spoiler=Вопрос к знатокам]В _g.script есть функция --// Является ли оbj монстром function is_object_monster(obj) local otype = get_clsid(obj) if(otype == clsid.crow or otype == clsid.zombie or otype == clsid.flesh or otype == clsid.controller or otype == clsid.bloodsucker or otype == clsid.burer or otype == clsid.fracture or otype == clsid.chimera or otype == clsid.boar or otype == clsid.dog_red or otype == clsid.dog_black or otype == clsid.poltergeist or otype == clsid.pseudo_gigant ) then return true end return false end , которая используется лишь один раз в скрипте gulag_military.script в функции ------------------------------------------------------------------------------------------- -- Проверка, имеется ли у гулага враг ------------------------------------------------------------------------------------------- function check_enemy (gulag) for k,v in pairs(gulag.Object) do if v ~= true then if v:best_enemy () ~= nil and v:story_id () ~= 710 and is_object_monster (v:best_enemy ()) == false then return true end end end return false end и больше нигде. В то же время в _g.script есть функция function IsMonster (object, class_id) local id = class_id or get_clsid (object) local monsters = { [clsid.boar_s] = true, [clsid.bloodsucker_s] = true, [clsid.dog_s] = true, [clsid.flesh_s] = true, [clsid.pseudodog_s] = true, [clsid.psy_dog_s] = true, [clsid.burer_s] = true, [clsid.cat_s] = true, [clsid.chimera_s] = true, [clsid.controller_s] = true, [clsid.fracture_s] = true, [clsid.poltergeist_s] = true, [clsid.gigant_s] = true, [clsid.zombie_s] = true, [clsid.tushkano_s] = true, [clsid.snork_s] = true } return monsters[id] or false end , которая используется во всём остальном коде и в которой идёт проверка по совершенно другим классам. Так какие классы корректны ? Ссылка на комментарий
Kolmogor 5 Опубликовано 21 Июня 2010 Поделиться Опубликовано 21 Июня 2010 sapsan, если кратко, то IsMonster рабочая, а is_object_monster всегда будет возвращать false А если длинно, надо проверить в конфигах монстров параметр class, найти его в class_registrator.script, посмотреть соответствующий ему script_clsid, и использовать в скриптах его Например, для кабана в конфиге: class = SM_BOARW ; AI class в class_registrator.script строка: cs_register (object_factory, "CAI_Boar", "se_monster.se_monster", "SM_BOARW", "boar_s") значит в скрипте используется clsid.boar_s Ссылка на комментарий
Malandrinus 615 Опубликовано 21 Июня 2010 Поделиться Опубликовано 21 Июня 2010 (изменено) Dennis_Chikin, Я как-то проводил замеры и выяснил, что выгода в применении таблицы в этом случае начинается при количестве ветвлений где-то в районе 7-8 (точной цифры не помню). Если всего два варианта, как в твоём примере, то смысла использовать таблицу нет, посколку время выборки (с использованием хеш-таблицы, помните?) хоть и почти постоянное, но больше, чем просто ветвление по условию. Но надо не попасть в ловушку. Используя цепочку if-else плюс незатейливый копипаст можно запросто написать что-то в этом роде: if <вычисление длинного выражения> then elseif <вычисление того-же длинного выражения> then и т.д. Ясно, что в этом случае условное выражение будет вычисляться в среднем "длина цепочки пополам" раз, что может эффективно убить всю производительность. Добавил: ожидаю 1 на пару сотен позиций, или штуки 4-6 до полусотни Признаться, не понял, что имеется в виду. Добавил 2: Думал сначала, что понимаю о чём ты, но теперь запутался окончательно. Вот расстреляй меня, но не понимаю твой вопрос. Какой-то запутанный пример, и мысль про "одну большую таблицу", и что имеется в виду под "делить её"... Не въезжаю =( Изменено 21 Июня 2010 пользователем malandrinus Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Malandrinus 615 Опубликовано 22 Июня 2010 Поделиться Опубликовано 22 Июня 2010 (изменено) Dennis_Chikin, Это - развитие упаковщика предметов в инвентаре. Я попробовал с боеприпасами - мне понравилось. Лаги от "патронных" нычек исчезли. Хочу запихать туда остальное. Опять меня запутал. Разве в упаковщике патронов лишние пачки не удаляются? Но ведь с остальными предметами так не выйдет. И при чём здесь таблицы? sapsan, В случае именованных индексов используется хеширование. В случае явного указания числовых индексов при создании таблицы - тоже... Я как-то задался целью выяснить разницу между "прямым" индексированием и "хешированным" в случае использования целочисленных индексов. Вроде как должна была быть разница в случае упорядоченного или случайного заполнения. Никакой разницы не обнаружил. По всей видимости вычисление хеш-функции от целого числа довольно малозатратная операция, как и сравнение на равенство, и существенно вклада в общее действо "операция индексации Lua" не вносит. Ну и естественно, доступ по ключу-строке на порядок медленнее. Медленнее только для случая, если точно знаешь индекс. Если же не знаешь - нужно искать перебором, что будет медленнее доступа по индексу-строке (если перед этим таблица не была отсортирована и не использовать бинарный поиск, хотя, при большой таблице, даже в этом случае хеш-доступ может выиграть за счет одноразового расчета местоположения данных по ключу) sapsan Изменено 22 Июня 2010 пользователем sapsan Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
sapsan 336 Опубликовано 25 Июня 2010 Поделиться Опубликовано 25 Июня 2010 Бывает такой вылет (и не только в Солянке): ловлю на его сейве такой вылет: Expression : fatal error Function : CScriptEngine::lua_error File : E:\stalker\sources\trunk\xr_3da\xrGame\script_engine.cpp Line : 73 Description : <no expression> Arguments : LUA error: j:\s.t.a.l.k.e.r\gamedata\scripts\rx_wmgr.script:596: attempt to get length of field '?' (a nil value) строка: self.weapons[typ][#self.weapons[typ]+1] = {sec = sec,id = id,prm = prm} Текст ошибки отличается от "обычного" из-за того, что в этой строке была проведена оптимизация (была замена table.insert()), но суть остаётся та же - таблица self.weapons[typ] == nil В другом моде по такому вылету советовали пройти проблемное место без нового оружия. Тоесть проблема в конфигах нового оружия. Вот нашел описание типов: параметр ef_weapon_type отвечает в первую очередь за то как NPC будет стрелять из оружия 5 - стрелять одиночными 6 - стрелять очередями 7 - прицелиться и выстрелить одиночным 8 - ОЧЕНЬ долго целиться и выстрелить (снайпер) 9 - стрельба из гранатомета чем выше цифра, тем оружие по идее имеет больший приоритет для NPC , но опять же указано что это будет работать только вместе с ef_main_weapon_type. то есть там тоже надо менять значения на эквивалентные значениям в ef_weapon_type. пояснение значений ef_main_weapon_type: 0 - пистолет 1 - дробовик 2 - автомат 3 - винтовка 4 - гранатомёт Кстати кому интересно вот инфа об отличии классов оружия - что дает каждый. WP_AK74 - автоматическое оружие с возможностью установить ПБС, прицел, гранатомет WP_LR300 - автоматическое оружие с возможностью установить ПБС, прицел, невозможно установить гранатомет (да да подствольник ставить нельзя - а у самой винтовки LR300 стоит класс WP_AK74 - который как раз разрешает подствольник) WP_PM, WP_HPSA, WP_USP45, WP_WALTH - пистолеты, чем отличаются - неизвестно WP_SVD - винтовка, одиночный огонь, установка ПБС невозможна WP_SVU - винтовка, одиночный огонь, ПБС интегрирован WP_VAL - автоматический огонь, возможна установка прицела, ПБС интегрирован, невозможно установить гранатомет WP_RG6 и WP_RPG7 - отличия неизвестны WP_VINT - автоматический огонь Я в этом не гуру, но вот что нашел странного: 1. винтовка Мосина по сути - снайперка и типы у неё правильные ef_main_weapon_type = 3 ef_weapon_type = 8 , а класс class = WP_SHOTG 2. у некоторых пистолетов есть тип стрельбы из оружия ef_weapon_type = 5 , но нет типа оружия ef_main_weapon_type (как я понимаю - в этом случае берётся значение по-умолчанию 0 ?) 3. болт и зомби (m_zombie_e) имеет тип ef_weapon_type = 1 как у всего холодного оружия Есть, конечно, железный метод if-а - отрезать код предварительной проверкой, но в самом коде и так есть abort(), который на данный момент в Солянке не выбрасывает, а лишь в лог сообщение пишет, if rx_utils.item_is_fa(item) then local sec = item:section() local cnd = item:condition() if wm_modes.forbiddens[sec] ~= true and cnd >= wm_modes.min_cond and self:have_ammo(item) then local params = read_wpn_params(sec) local typ = params.typ if not self.weapons[typ] then abort("weapon_manager: not registered weapon type '%s' in [%s]",typ,sec) end local prm = self:get_weapon_prior(item) self.weapons[typ][#self.weapons[typ]+1] = {sec = sec,id = id,prm = prm} end end . Но полного лога нет - достался только хвост. Однако хочется, чтобы всё было настроено нормально, а не методом отсечение кривых конфигов... Кто-то знает где копать ? Сам смотрел конфиги оружия в геймдате Солянки соответсвенно классам function item_is_fa(o,c) if not c then c = o and o:clsid() end local t = { [clsid.wpn_pm_s] = true, [clsid.wpn_walther_s] = true, [clsid.wpn_usp45_s] = true, [clsid.wpn_hpsa_s] = true, [clsid.wpn_bm16_s] = true, [clsid.wpn_shotgun_s] = true, [clsid.wpn_ak74_s] = true, [clsid.wpn_lr300_s] = true, [clsid.wpn_groza_s] = true, [clsid.wpn_val_s] = true, [clsid.wpn_vintorez_s] = true, [clsid.wpn_svu_s] = true, [clsid.wpn_svd_s] = true, [clsid.wpn_rg6_s] = true, [clsid.wpn_rpg7_s] = true, [clsid.wpn_knife_s] = true} if c and t[c] then return true end return false end , но левого типа так и не нашел. Чуть не забыл - в этом моде используются такие типы: 1,5,6,7,8,9. Добавлено через 105 мин.: Нашел интересный геноцид при начале НИ: for a=1000,20000,1 do local obj=alife():object(a) if obj and ((string.find(obj:name(),"af_") and not string.find(obj:name(),"esc_af_") and not string.find(obj:name(),"pri_af_" ) and not string.find(obj:name(),"af_dumm")) or string.find(obj:name(),"esc_wpn")) then alife():release(obj,true) end end Что там за неугодные объекты были ? Ссылка на комментарий
WhatAbout 8 Опубликовано 5 Июля 2010 Поделиться Опубликовано 5 Июля 2010 (изменено) V92, Позволю себе вставить пять копеек. Уже насколько камрадов попадают на вылет Not enough IDs после установки последнего патча (от 27.06). При этом на Соли с предыдущим патчем сейвы, приводящие к вылету (а у некоторых зависанию), работают. Количество объектов в этих сейвах до вылета не очень большое ~30 тыс. Т.е. в какой-то момент имеет место старт создания огромного количества объектов. sapsan, Есть подозрение на нечто se_respawn.script, он ведь правился в последнем патче. Один из пострадавших от Not enough IDs "неспециально" вернул сей скрипт из пред.патча, пока играет. Изменено 5 Июля 2010 пользователем 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 Ссылка на комментарий
sapsan 336 Опубликовано 7 Июля 2010 Поделиться Опубликовано 7 Июля 2010 Найдена скриптовая аномалия! Ссылка на комментарий
Kolmogor 5 Опубликовано 7 Июля 2010 Поделиться Опубликовано 7 Июля 2010 (изменено) В скрипте se_respawn.script строку 351table.insert(self.spawned_obj ,obj.id) заменить на self.spawned_obj[#self.spawned_obj+1] = obj.id , то после тех же действий получаем мёртвый завис и такое в логе Зацикливается походу на этом коде 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 с новым способом вставки в таблицу. Сейчас заменю table.getn на # и проверю. Эта мысль пришла вот только что. sapsan Изменено 7 Июля 2010 пользователем sapsan Ссылка на комментарий
kamikazze 266 Опубликовано 7 Июля 2010 Поделиться Опубликовано 7 Июля 2010 (изменено) 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 Ага, точно, меняет. Тады тот самый случай и есть Изменено 7 Июля 2010 пользователем kamikazze Отладчик и скриптер мода OGSE. Автор схемы "Компаньоны", стреляющего БТРа и многих других полезностей Ссылка на комментарий
sapsan 336 Опубликовано 7 Июля 2010 Поделиться Опубликовано 7 Июля 2010 Таки причина в table.getn(). Заменил его на оператор # и всё встало на свои места. Ссылка на комментарий
Kolmogor 5 Опубликовано 7 Июля 2010 Поделиться Опубликовано 7 Июля 2010 (изменено) kamikazze, Почему не меняется? Меняется - в таблицу добавляется элемент в функции se_respawn:create По-моему нормальный цикл - собственно этот цикл еще с оригинальной ТЧ идет Единственное изменение написано выше(заменен table.insert(t, el) на t[#t+1] = el) Собственно со всем тобой сказанным согласен, но этот цикл из другой оперы - индекс не используется - в таблицу добавляются элементы и проверяется количество элементов в таблице Ну и кстати, я по-моему понял в сталкер старый луа, где getn сделан через внутреннюю переменную n у таблицы. table.insert ее меняет, а t[#t+1] = el не меняет sapsan, если интересно, добавь вывод в лог еще self.spawned_obj.n и верни назад все table.insert А если не вернуть, а заменить table.getn() на #. Этого будет не достаточно ? sapsan По идее на table.remove еще можно нарваться - она по той же идее смотрит на переменную n и должна считать таблицу пустой То есть table.remove(t, pos) надо заменять на t[pos] = nil Мне просто кажется: table.insert, table.remove, table.getn и таблицы-массивы отдельно(здесь именно такая) и таблицы-словари с ключами отдельно Хотя может и наоборот уйти ото всех функций table.* Изменено 7 Июля 2010 пользователем Kolmogor Ссылка на комментарий
Рекомендуемые сообщения