Dennis_Chikin 3 665 Опубликовано 22 Ноября 2014 А теперь мы попытаемся со всей этой фигней взлететь ! (С)Собственно, чтобы заработало без каких-либо переделок все остальное, что я сюда вкладываю.Скрипт большой и толстый, так что этого слона мы будем есть по частям и долго.Тему создал, чтобы болталась мне здесь немым укором, и напоминала. 3 1 3 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 23 Ноября 2014 (изменено) Поехали.Вообще, удивительно, как нечто уродливое тянут из мода в мод, навешивая на него дополнительных уродств с целью некоей непонятной совместимости с неизвестно с чем, и на подобие тех мышей из анекдота про кактус продолжают попытки совмещений несовмещаемого в одном флаконе, подстраивая все подряд под это самое уродство.Вместо того, чтобы давно написать быструю, удобную, безглючную и, главное, действительно универсальную базу, а потом уже спокойно делать все под нее.Сейчас я начну переписывать скрипт прямо "по живому", так что ревью, тесты и дополнения приветствуются, и, надеюсь, общими усилиями что-нибудь все-таки сделаем.первый кусок: if ( jit == nil ) then profiler.setup_hook() end string_sub, string_gsub = string.sub, string.gsub string_find, string_gfind = string.find, string.gfind string_match, string_gmatch = string.match, string.gmatch string_format, string_char = string.format, string.char string_len = string.len -- dc: а # - некошерно ? table_insert, table_remove, table_foreach = table.insert, table.remove, table.foreach math_random, math_randomseed = math.random, math.randomseed math_sin, math_cos, math_acos = math.sin, math.cos, math.acos math_floor, math_ceil = math.floor, math.ceil math_mod, math_sqrt = math.mod, math.sqrt function round( value ) local min = math_floor( value ) local max = min + 1 -- if value - min > max - value then return max end -- dc: приведем к "<, ==, >=" if ( value - min ) < ( max - value ) then return min end return max end function to_str( v ) if type( v ) == "userdata" then local n = v.name return ( type( n ) == "function" and ( "userdata[" .. v:name() .. "]" ) ) or ( type( n ) == "string" and ( "userdata[" .. n .. "]" ) ) or "*userdata*" elseif v == nil then return "<nil>" -- dc: именно "<nil>" - для извращенцев end return tostring( v ) end function log( ct, fmt, ... ) -- src - откуда вызван, если false - не выводится, категория игнорируется -- get_console():execute( "load ~" .. ct .. "~ [_g] " .. ( fmt or "" ) ) -- для поиска потерянных аргументов и скрипта, их потерявшего get_console():execute( "load ~" .. ct .. "~ [_g] " .. ( ( ... and string_format( fmt, ... ) ) or fmt or "" ) ) if ct == "error" or ct == "log" then get_console():execute( "flush" ) end end function abort( fmt, ... ) get_console():execute( string_format( "load ~error~: %s", ( ... and string_format( fmt, ... ) ) or fmt or "" ) ) get_console():execute( "load ~~~ Обнаружена ошибка. Описание ошибки смотрите выше. Игра остановлена." ) get_console():execute( "load ~~~ Пожалуйста, не надо сообщать об ошибке в строке 50." ) get_console():execute( "load ~~~ Какую-либо ценность имеют 10 строк ДО этого сообщения." ) get_console():execute( "flush" ) local a ; a = 1/a; get_console():execute( "quit" ) exit() end Библиотечные функции, как обычно, определяем локально. Иначе потом, в циклах, из этих микросекунд набегают секунды лагов.function round( value ) -- округление, которого не хватает в библиотеках. Там где-то в файле такая же болтается, но ее находим, выносим в начало, и переписываем, чтобы больше перед глазами не маячила, когда займемся прочим.function to_str( v ) -- преобразование аргумента в строку. Используется потом при работе с логикой, и где-то еще. nil и userdata на вход подавать НЕЛЬЗЯ, что бы там пысы не писали. Точнее, nil - еще можно, из логики, а userdata просто повиснет, или вылетит с бредом в логе. Это функция именно для логики. Для наших собственных нужд потом добавим такую же в отдельном скрипте. Упс ! Я ее, оказывается, уже успел переписать. Но тем не менее, все равно для логики.log() и abort() - отладочные. Соответственно, вывести что-то в лог и крэшнуть игру. Как и в случае с to_str() Здесь используются короткие, "world-wide совместимые" варианты. Более полезные определяются в более другом скрипте. А здесь главное - успеть вывести хоть что-то вменяемое, если где-то отработало что-то, до чего пока не добрались.Продолжение следует.Напоминаю, что куски, которые здесь будут - будут в виде, готовом сразу к переносу в ваши экземпляры скриптов без потери/нарушения работы ваших модов. Ну, я надеюсь.А если нет - надеюсь, мы их здесь все-таки доработаем до определенной универсальности. Изменено 23 Ноября 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 23 Ноября 2014 (изменено) Продолжаем разговор: sys_ini = system_ini() global_time_ms, game_time_ms, game_time_sec, game_time_time = 0, 0, 0, 0 game_time_start, game_time_base = 0, 0 -- игровое время на начало игры, игровое время при загрузке dist_sw_online = 150 dist_sw_offline = 180 IAmAStalker, IAmAMonster = {}, {} -- obsolete IAmAWeapon, IAmAWeaponFireOnly = {}, {} -- obsolete c_npc = false c_actor = false c_mob = {} c_ai = {} c_trader = false c_ibox = false c_wpn = {} c_arms = {} mob_online = {} -- табличка принудительного on/off-line для монстров force_offline = {} -- принудительный оффлайн для неписей и тайников function printf() end function print_table() end function trim( s ) return string_match( s, "^%s*(.*%S)" ) end -- обрезаем пробелы function Parse_StrToTbl( s, div, mode ) -- ( строка, разделитель, [true|число|nil для строк] ) local t = {} local pattern if div then pattern = "%s*([^" .. sDiv .. "]+)%s*" else pattern = "[_%w]+" end if mode then if mode == true then for v in string_gmatch( s, pattern ) do t[v] = true end -- таблица вида [значение] = true elseif type( mode ) == "number" then -- таблица вида [idx] = число for v in string_gmatch( s, pattern ) do table_insert( t, tonumber( v ) or v ) end else abort( "Parse_StrToTbl, ivalid mode: %s", tostring( mode ) ) end else for v in string_gmatch( s, pattern ) do table_insert( t, v ) end end return t end function parse_custom_data( str ) -- комментарии в профилях и cd не использовать ! Сохраняются в cd как есть ! local t = {} if str then --log( "info", "parse_custom_data: [" .. string_sub( str, 1, 150 ) .. "]" ) local ts for sect, data in string_gfind( str, "%s*%[([^%]]*)%]%s*([^%[%z]*)%s*" ) do --log( "info", "parse_custom_data, sect: [" .. sect .. "], data: [" .. string_sub( data or "nil", 1, 120 ) .. "]" ) ts = {} t[sect] = ts for s in string_gfind( data or "", "([^\n]*)\n*" ) do if string_find( s, "=" ) then for k, v in string_gfind( s, "([^=]-)%s*=%s*(.*)" ) do --log( "info", "parse_custom_data, " .. k .. " = " .. string_sub( v or "nil", 1, 120 ) ) if v then k = string_match( k, "^%s*(.*%S)" ) if k and k ~= "" then ts[k] = string_match( v, "^%s*(.*%S)" ) end end end else local k = string_match( s, "^%s*(.*%S)" ) if k and k ~= "" then ts[k] = "<<no_value>>" end end end end end return t end function gen_custom_data( t ) local s = "" for k, v in pairs( t ) do s = s .. "[" .. k .. "]\n" for kk, vv in pairs( v ) do if vv ~= "<<no_value>>" then s = s .. kk .. " = " .. vv .. "\n" else s = s .. kk .. "\n" end end end return s end function parse_names( s ) local t, n = {}, 0 for name in string_gfind( s, "([%w%-%._\\]+)[%,%s]*" ) do n = n + 1; t[n] = name end return t end function parse_key_value( s ) if s then local t = {} local k for n in string_gfind( s, "([%w%-%._\\]+)[%,%s]*" ) do if k then t[k] = n; k = nil else k = n end end return t end end function parse_nums( s ) local t, n = {}, 0 for entry in string_gfind( s, "([%d%.]+)%,*" ) do n = n + 1; t[n] = tonumber( entry ) end return t end sys_ini = system_ini() -- файлы в винде вообще открываются ОЧЕНЬ медленно. А сам сталкер еще и что-то с ними делает, тоже мееедленно, и печально. Отсюда и загрузка солянки по 2 минуты. Так что открываем все один раз, и так в открытом состоянии и держим. global_time_ms, game_time_ms, game_time_sec, game_time_time = 0, 0, 0, 0 game_time_start, game_time_base = 0, 0 -- игровое время на начало игры, игровое время при загрузке В общем, вместо времени 64 бит с многочисленными преобразованиями в таблицы и обратно я использую везде 32. Ага, 31 мая, 32 мая, и т.д. Чтобы такого не было - время всегда считается не от НИ, а от загрузки. И сохраняется дельта. А месяц топтаться на одной оркации без единого сэйва - вряд-ли кто будет. Ну и наконец, вместо медленного вызова функций и пересчета используем быстрое обращение к переменной. А обновляются они раз в 200ms - вполне достаточно, я считаю. dist_sw_online = 150 dist_sw_offline = 180 Опять же, чтобы не перечитывать и не пересчитывать постоянно - переменные. А из alife.ltx берем только при загрузке. IAmAStalker, IAmAMonster = {}, {} -- obsolete IAmAWeapon, IAmAWeaponFireOnly = {}, {} -- obsolete c_npc = false c_actor = false c_mob = {} c_ai = {} c_trader = false c_ibox = false c_wpn = {} c_arms = {} - то, которое устарело - это была одна из солянковских оптимизаций. Устарело. Просто не от всюду еще дочистил. Добавлены константы и таблички с классами. Инициализируются при старте, когда отработает class_regisrator и появится alife(). mob_online = {} -- табличка принудительного on/off-line для монстров force_offline = {} -- принудительный оффлайн для неписей и тайников Как бы понятно из названия. Единые таблицы для ВСЕХ скриптов, где применяется такой фокус. Хотя вообще-то на самом деле это плохой, негодный фокус, и он не нужен, но это уже другая тема. А так - в amk было переодевание и тайники, использовавшее convert_npc={}, еще где-то - need_be_online={} в se_monster, не знаю, зачем... Тысячи их. Все снести, где реально надо - заменить на единые глобальные. printf() и print_table() - затычки для недовычищенного и прочих разных гибридизаций ежа с ужом через 50 скриптов, гордо именуемых "адаптациями". И, наконец, преобразования разных забавных строк в разные забавные таблицы, и обратно. Часть - была и в оригинале, часть - во вножестве экземпляров в разных скриптах, и оригинальных, и которые см. про "адаптацию". Все собрано в одном месте и переписано. Тут важно понимать, что в LUA НЕТ инлайна, а следовательно, все равно все это будет работать медленно и печально, и прежде чем использовать какую-нибудь функцию преобразования чего-попало во что-нибудь еще, особенно, в цикле или апдейте - подумайте, а надо ли ? Ибо нет ничего более нелепого, и вызывающего содрогание, чем конструкция вида: local s = "1, 2, 3" local t = parse_nums( s ) local n1, n2, n3 for k, v in pairs( t ) do if k == 1 then n1 = v end; if k == 2 then n2 = v end; if k == 3 then n3 = v end; end Изменено 23 Ноября 2014 пользователем Dennis_Chikin 3 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 1 Декабря 2014 "возврат указателя CInifile *pSettings" Странно... Впрочем, все равно собирался стресс-тесты устраивать, заодно и проверю. Но ведь народ-то тоже подтверждает наличие эффекта от устранения пессимизации c system_ini() по каждому чиху... Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 1 Декабря 2014 (изменено) Ага: ! Cannot find saved game ~test~ [bind_stalker] ini1: 13829.931640625 ! Cannot find saved game ~test~ [bind_stalker] sys_ini: 7527.0874023438 ! Cannot find saved game ~test~ [bind_stalker] test: 517023.3125 ! Cannot find saved game ~test~ [bind_stalker] test_ini: 4946.7431640625 тупо копипастой наплодил: local pt1 = profile_timer() local ini1, test pt1:start() for i = 1, 10000 do ini1 = system_ini() test = ini1:r_bool( "test1", "bool_t" ) end pt1:stop() log( "test", "ini1: %s", pt1:time() ) local pt1 = profile_timer() local ini1, test pt1:start() for i = 1, 10000 do test = sys_ini:r_bool( "test1", "bool_t" ) end pt1:stop() log( "test", "sys_ini: %s", pt1:time() ) local pt1 = profile_timer() local ini1, test pt1:start() for i = 1, 10000 do ini1 = ini_file( "test.ltx" ) test = ini1:r_bool( "test1", "bool_t" ) end pt1:stop() log( "test", "test: %s", pt1:time() ) local pt1 = profile_timer() local ini1 = ini_file( "test.ltx" ) local test pt1:start() for i = 1, 10000 do test = ini1:r_bool( "test1", "bool_t" ) end pt1:stop() log( "test", "test_ini: %s", pt1:time() ) Собственно, вызов жрет. А вот с просто файлом, не system.ltx - форменный кошмар (это из 5 строк файл). Изменено 1 Декабря 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 1 Декабря 2014 ! Cannot find saved game ~test~ [bind_stalker] ini1: 13623.260742188 ! Cannot find saved game ~test~ [bind_stalker] system_ini(): 12739.622070313 ! Cannot find saved game ~test~ [bind_stalker] sys_ini: 7462.7309570313 Лучше, но не на много. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 1 Декабря 2014 (изменено) Это торговлю-то с 17500 костюмами ? (Сорри за малопонятный большинству юмор.) Правильнее на самом деле уменьшить общее количество как конфигов, так и их объем. Многие просто не нужны, и тянутся/плодятся, по тому, как сказал ZD, что в степи стоит 10 метров стены с вмятинами от голов разработчиков из ПЫС. А вынос system_ini() в _g.script - это ловля 2-х микросекунд, но, в общем, их может быть много. Внезапно много. Изменено 1 Декабря 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 2 Декабря 2014 (изменено) Смысл - ровно в 2 раза. Не, там, где за один раз в игру - не жалко этих 2-х микросекунд. Но ведь отдельные гении ухитряются писать циклы наподобие приведенного, и все это - в 20 миллисекундном апдейте x 100 раз. А потом и получается "не может оно за 5 секунд выполняться, если на моем 16-ядерном пентиуме-666 на 100500Терагерц с терабайтом памяти и плазменной панелью на всю стену в кредит взятой - 2 минуты ! И вообще, кто плазменную панель в кредит не купил - тому надо вообще запретить на форум писать !!!" Короче, замеры - выше, код - выше. Изменено 2 Декабря 2014 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 3 Декабря 2014 В общем, продолжая тему, имеет смысл коснуться печальной истории одной пессимизации, но поскольку касается она в основном Солянки и разнообразных солянкоклонов, то закинул ее в более специализированное место: http://www.amk-team.ru/forum/index.php?showtopic=8830&p=896984 У кого такого нет - то просто имейте в виду, что такие грабли есть, а добавлять и заменять ни где ничего не надо. Из солянки же в конечном итоге ВСЕ это - хоть то, что было, хоть предлагаемое на замену, следует убрать. По мере истребления всех имеющихся ссылок на ЭТО. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 4 Января 2015 (изменено) Ну, во-первых, как бы 1 наносекунда - я за ней и не гонюсь.Если глянуть по ссылке, про "историю пессимизации" - там вообще-то 10-15 секунд загрузки на вычищенных конфигах, и аж минуты на невычищенных. Плюс "хромота" непосредственно в игре.Плюс 10 секунд (до 2-х минут на ОПКМ) той же загрузки на "торговле", + 4 на тайниках, + 4 на ящиках, плюс 10-30 загрузки и лаги непосредственно в игре по 2 сек на звуках.Ага, вот это у нас называется нынче "гоняться за наносекундами", и "делать ничего не надо".Запуск игры по 4 минуты и перезагрузка по полторы-две после каждой смерти... На не самом древнем железе.Ну нифига ж себе - "неважно"...Не говоря о десятках и сотнях мегабайт памяти при разборах этих конфигов.Да, я понимаю, можно сказать и про "сначала конфиги зачисти". Ага, во всех солянках, всех дмх, всех опах и что там еще наплодили, я вот должен бегать и конфиги чистить. Офигеть...Это как бы во-первых.Второе - а вот действительно, объясните мне, ЗАЧЕМ нужен скрипт о 800 килобайт, если 60 там - выше крыши. Ну, нет у вас 800-килобайтных скриптов, а у других - есть. И, что теперь ?Причем, заметим, пока я разбираюсь с этими 800кб простынями, я должен наслаждаться вот этой вот самой загрузкой по 4 минуты по 20 раз за вечер ?И что такого сакрального в 800кб простыни if ... then ..., что ее и тронуть-то нельзя ?Наконец, разгоревщийся сыр-бор про по сути "А кто тебе РАЗРЕШИЛ пихать в _g.script твой богомерзкий sys_ini ?"Типа, если имеешь наглость что-то там делать - ОБЯЗАН делать под КОШЕРНЫЙ 1.0004 и ни как иначе (вот кстати со всеми его ini_file() в цикле, да еще аж по 2 раза), и если что написал под другое - снеси всю свою богомерзкую ересь обратно, и делай "как у всех", через 20 скриптов. Знаете, вот при такой постановке вопроса - ну не читайте вы мои посты, и будет вам счастье. Делов-то... Ну или идите в райотдел, и пишите заявление по "Оскорблению чуйств верующих" - нынче а аж специальную статью придумали, специально для всех оскорбившихся.Модераторское:Еще n страниц флуда ушло в более соответствующее место. Очень жаль, что надо было 6 МЕСЯЦЕВ дожидаться, пока темы перенесут из закрытого раздела в общий, дабы имевшие возможность нафлудится вдосталь, предпочли реализовать именно на публике. Еще более жаль, что имея возможность сказать много полезного, предпочли заняться выяснением "Кто разрешил ?" Изменено 3 Ноября 2015 пользователем Dennis_Chikin 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 20 Ноября 2015 (изменено) Итак, внезапно, не смотря на все идеи в духе "форум - закрыть, всех пишущих - на лесоповал", я, все-таки, продолжу. Просто по тому, что единственный известный мне работающий метод избавиться от невыносимого ужаса - озвучить то, что его вызывает. Собственно, те куски, которые были выше - им в общем-то в _g.script вообще не место. Ну вот плохая это идея - оформлять ОДНУ строку отдельной функцией (особенно, в скриптовом языке), формировать там таблицу, возвращать в виде результата, а потом уныло разбирать возвращенную таблицу. И вообще, даже если что-то очень сильно хочется иметь в глобальном нэймспейсе - есть в конце-концов метод _G.* = Ну да ладно. Перейдем, наконец, уже к той части, которая была в оригинале и в очень классических модах. schemes = {} -- соответствие схем модулям stypes = {} -- типы схем -- Загружает схему из файла на диске и активирует в мотиваторе. function load_scheme( filename, scheme, stype ) -- имя файла без расширения schemes[scheme] = filename stypes[scheme] = stype end if not time_global then time_global = function() return device():time_global() end end function wait_game( time_to_wait ) -- игровые секунды verify_if_thread_is_running() if time_to_wait then get_game_time = game.get_game_time game_time_time = get_game_time() local time_to_stop = game.CTime() -- нужно выставить time_to_stop:setHMS( 0, 0, time_to_wait ) time_to_stop = game_time_time + time_to_stop while time_to_stop >= get_game_time() do coroutine.yield() end else coroutine.yield() end end function wait( time_to_wait ) -- реальные секунды verify_if_thread_is_running() if time_to_wait then local time_to_stop = time_global() + time_to_wait while time_to_stop >= time_global() do coroutine.yield() end else coroutine.yield() end end Ну, с первыми двумя все понятно. Как раз тот случай, когда что-то удобно вызывать из глобального пространства. Первое - просто - табличка загруженных "схем". Тут еше отдельный приятный повод для флэйма по поводу "что такое схема", и зачем оно, но для него существует отдельная тема. Про "логику", да. Второе - избавление от некоей монстровидной строки, путем определения функции с коротким именем. Сама функция - получение времени от начала загрузки игры в миллисекундах. Ну, это все знают. Единственно что, сразу возникает вопрос: а нельзя ли оное упрощение как-то еще больше упростить ? Можно, и выиграть при этом в скорости. Но про это будет позже. А вот две следующие - уже вызывают вопросы. Вот на самом деле не знаю, используются ли они где-либо, и определено ли вообще это самое verify_if_thread_is_running() и coroutine.yield(), ибо невозможно объять необъятное силами одного человека, но очевидно, что используемое в оригинале game.time() здесь не подходит, ибо рассчитано на один игровой месяц. По-этому, сразу переписал. Переписал, кстати, в весьма не самом оптимальном виде, поскольку не уверен в моменте использования этой функции. Вторая - то же самое ожидание, но уже в реальном времени и от загрузки. Поскольку реальный месяц без перезагрузки вряд-ли кто будет ждать - вполне допустимо, и вопрос сводится опять же к тому, определено ли coroutine.yield() Две следующих - реально используются, как минимум монстрами (включая, "почему-то", дохлых), БТРами и, видимо, кем-то еще. Вот лень прямо сейчас поиск делать. Привожу сразу оригинал, и попытку это как-то изменить: function action(obj,...) local arg = {...} local act = entity_action() local i = 1 while true do if (arg[i] ~= nil) then act:set_action(arg[i]) else break end i = i + 1 end if (obj ~= nil) then obj:command(act,false) end return entity_action(act) end function action_first(obj,...) local arg = {...} local act = entity_action() local i = 1 while true do if (arg[i] ~= nil) then act:set_action(arg[i]) else break end i = i + 1 end if (obj ~= nil) then obj:command(act,true) end return entity_action(act) end странным мне здесь представляется все. Во-первых, если в аргументах предусмотрена возможность получить nil - это автоматически приводит к краху таблицы, к которой пытаются обращаться по индексу. При том что аргументы в общем-то должны выглядеть как move( ... ), cond - то есть, объект акции и условие. Во-вторых, я уже поднимал этот вопрос в параллельной теме: 1. получаем новый a = entity_action() 2. выполняем для него a:set_action( arg ) 3. делаем что-то еще 4. а теперь - опять получаем entity_action( a ) - то есть, с аргументом, и тут же его возвращаем вызвавшему. Зачем ? Я бы понял, если бы это надо было зачем-то сохранить оригинал, на случай, когда с копией в дальнейшем пытаются что-то сделать. Но в том и дело, что оригинал - не сохраняется. Его уборщик убирает. Теоретически. Тут бы надо кому-то в исходники глянуть. Но вот вроде бы в таком вот виде: function action( obj, ... ) -- здесь и далее - странная фигня с entity_action( entity_action() ) local a = entity_action() for i, v in ipairs( { ... } ) do a:set_action( v ) end if obj then obj:command( a, false ) end -- return a -- entity_action( a ) -- может быть просто act ? посмотрим... return entity_action( a ) end function action_first( obj, ... ) local a = entity_action() for i, v in ipairs( { ... } ) do a:set_action( v ) end if obj then obj:command( a, true ) end -- return a -- entity_action( a ) -- аналогично предыдущему return entity_action( a ) end - у меня пока все работает без проблем. Ну да всего 2-й день гоняю. Надо, конечно, присмотреться ко всем этим крысам и прочим тушканчикам. Чем отличаются вообще action() и action_first() ? В первой - obj:command( act, false ), во второй - true. Upd: тут дополняют, что вот это вот false/true - это очередь/немедленное исполнение. Что касается entity_action( act ) - лучше все же оставить просто по принципу "как бы чего не вышло". Ибо исходники воскурению весьма таки сопротивляются. И, да, переменную переименовать. Изменено 20 Ноября 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 20 Ноября 2015 На счет act и переименования - там, кстати, дальше будет функция совершенно феерическая. Просто я до нее не дошел еще. В общем, логично, про защиту от странного. Теоретически - не нужна, на практике - вот черт ее знает. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 23 Ноября 2015 (изменено) Едем дальше. function debug_get_level_object( obj_name ) function debug_get_actor() По понятной причине - не работают. Если кому не понятно - в стандартном движке нет wait(). Можно оставить как есть, можно - удалить. Если вруг, в чем я сильно сомневаюсь, случится вылет - найти, и удалить там. function distance_between( obj1, obj2 ) function distance_between_safe( obj1, obj2 ) Собственно, все очевидно, кроме того, что это исключительно для game_object, и ни как иначе. Кроме того, непонятно, зачем делать функцию, которая дает повисание при отсутсвии объекта. Опять же, про однострочные функции уже говорилось - это не тот движок, и не тот язык, где можно позволять себе подобную роскошь. Читаемости вашему коду оно не добавляет. function has_alife_info( info_id ) -- Проверка на инфопоршны, даже если игрока не существует local sim = alife() if sim then return sim:has_info( 0, info_id ) end return false end- в общем, функция много где использующаяся, но смысл ее использовать - сомнителен. По тому что и alife() и actor в абсолютном большинстве случаев - есть, а там, где их нет - там и не нужно. Где нужно - там - actor_proxy более уместен, или более другой вариант, чем info. Да, я помню про smart_terrain и респавнеры, но я не уверен в пользе их работы в ходе загрузки. Там и так есть, чем заняться. То есть, нужна ревизия. Ну а дальше у нас будет несколько классов, весьма, я бы сказал, феерических, и опять же - с неработающим кодом. Изменено 23 Ноября 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 23 Ноября 2015 info_id ~= "" - если подобное вызвали - у нас где-то проблемы. Проверка на sim ЗДЕСЬ - по тому что если вызвали, а alife() еще нет - мы об этом все равно ни как сообщить не сможем - и висяк будет весьма загадочный. "А можно ссылку на бенчмарки, исследования по этому поводу?" - функции в сталкере не инлайнятся. Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 24 Ноября 2015 (изменено) Ты не понял. function return_true( v ) if v == true then return true else return false endВ лучших традициях индусского кода.Теперь, где-то, в другом скрипте, вызываем if return_true( another_var ) == true thenЧто делает вызываемое ? Правильно, ищем, где оно, и смотрим.Нет, функция НЕ бессмысленна. По тому что где-то в xr_logic.чего-то() или еше в каких диалогах требуют строго true или строго false. То есть, сей индусский код имеет право быть.В принципе. Для вот этого вот, где таки требуют.Но не ВЕЗДЕ."function has_info(info_id)return actor and actor:has_info(info_id) or has_alife_info(info_id)end"Вот опять же плохо по той же причине: в 99% случаев у тебя есть актор, и 146% гарантии, что без него до сюда не дойдет. Вот и можно писать сразу actor:has_info(info_id), не через 10 скриптов и 20 проверок.Если не уверен - alife():has_info(0, info_id) сразу. Тоже наглядно, и жрет всяко меньше, чем + 2 вызова функции плюс пачка эзотерических проверок внутри каждой."по крайней мере намного вероятнее, чем вызов из главного меню до загрузки игры" - у меня куча скриптов, где отключен вывод в лог об успешном контроле целостности. По тому как smart_terrain и иже с ним хотят их не только до алайфа, но даже до консоли.Опять же, половина level.* кучу времени выдает nil. Изменено 24 Ноября 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 24 Ноября 2015 (изменено) Так как непонятно, к кому обращается Dennis_Chikin Э-эээ... Распутываю. Текст, естественно, отвечает на предыдущий пост. Кроме того места, где цитата из более раннего. В основной части текста идет речь про то, для чего может быть нужна однострочная функция в глобальном пространстве, в которой к тому же еще и проверяется наличие alife(). То есть, представим момент загрузки какого-нибудь смарта или респавнера (а они грузятся НУ ОЧЕНЬ своеобразно), в котором зачем-то добавили xr_logic.pic_section_непомнючего, которая проверяет это самое инфо. Вот в этот момент действительно есть вероятность (и я такой код ВИДЕЛ), что все встанет колом, если нашу однострочную функцию "упростить". Но при этом мы и не можем адекватно обработать и случай с несуществующим инфо на входе, так что пущай падает/вылетает/рушится и т.д. ССЗБ тот, кто написал такой странный код. В остальных 99% случаев - да, есть и актор, и инфо, и вот эту однострочную проверку лучше выполнять "на месте", явным образом. Также есть места, где нам заведомо известно, что и alife() есть, и несуществующих инфо нет, и эти места выполняются точно и при отсутствии актора. Здесь, опять же, явным образом вписать alife():has_info(0, info_id) - то есть, конструкция сама за себя говорит и о том, что делает, и об условиях, в которых работает. Впрочем, это все на любителя, конечно. Кому и 100500 строк if ... end; if ... end (даже не elseif) милее. Здесь мы вроде бы ВСЕ возможные варианты и условия для них озвучили, вместе с подводными камнями, так что цель разбора вроде как достигнута. Посты с личными разборками, которые здесь были, ушли по назначению. Не зависимо от того, что в них было кроме разборок. Изменено 24 Ноября 2015 пользователем Dennis_Chikin Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 2 Августа 2016 (изменено) Не помню, писал, или нет, но: local set_current_time_t, set_current_time_f function set_current_time_wait() if game.get_game_time() < set_current_time_t then return false end level.set_time_factor( set_current_time_f ) set_current_time_f = false return true end function set_current_time( hour, min, sec ) -- починено, работает. game_time_time = game.get_game_time() -- полное игровое время local new_time = game.CTime() -- нужно выставить new_time:setHMS( hour, min, sec ) local hh, mm, ss _, _, _, hh, mm, ss = game_time_time:get() -- текущее время local c_time = game.CTime() c_time:setHMS( hh, mm, ss ) if new_time == c_time then return -- ничего не делаем elseif new_time < c_time then -- следующие сутки new_time:setHMS( hour + 24, min, sec ) end new_time:setHMS( 0, 0, new_time:diffSec( c_time ) ) set_current_time_t = game_time_time + new_time if not set_current_time_f then set_current_time_f = level.get_time_factor() end level.set_time_factor( 10000 ) level.add_call( set_current_time_wait, dummy_action ) end По-моему, все очевидно. И вот именно через нее надо делать сон и прочее. Для вящей пущести можно в аргументы сам таймфактор добавить, и сильно большими значениями не увлекаться. P.S. Кстати, а вот при старте игры чтение таймфактора из конфига и принудительную установку - следовало бы добавить. На случай очумелых ручек в странных местах. Изменено 3 Августа 2016 пользователем Dennis_Chikin 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение
Dennis_Chikin 3 665 Опубликовано 3 Августа 2016 <<"универсальные" схемы>> - хе-хе. Ну, да, 10 скриптов делают одно и то же, 10 - ровно противоположное, результат работы 100500 тыщ циклов в секунду по 100500 тыщ операторов абсолютно идентичен обыкновенному math.random(), причем все эти скрипты еще и между собой жестко конфликтуют, типа один посылает непися в одну точку, второй - в другую, третий манипулирует с оружием, четвертый в это время это оружие удаляет, пятый его же перемешает в подземный ящик (кто себя здесь узнал - я не виноват, что у вас целых 6 скриптов для того, что делается одной строкой конфига), шестой выставляет mental_state(anim.free), седьмой в это же время - body_state(move.crouch)... И так далее... Впрочем, это не совсем в данную тему, и даже не факт, что в этот раздел. 1 Солянка обезжиренная, диетическая, полезные советы по "солянке", текущий тестовый патч Поделиться этим сообщением Ссылка на сообщение