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

Рефакторинг: _g.script


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

(изменено)

Поехали.

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

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

первый кусок:

 

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

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

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

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


Ссылка на сообщение
(изменено)

Продолжаем разговор:

 

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

Изменено пользователем Dennis_Chikin
  • Не согласен 3

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


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

"возврат указателя CInifile *pSettings"

 

Странно... Впрочем, все равно собирался стресс-тесты устраивать, заодно и проверю. Но ведь народ-то тоже подтверждает наличие эффекта от устранения пессимизации c system_ini() по каждому чиху...

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


Ссылка на сообщение
(изменено)

Ага:

! 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 строк файл).

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

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


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

! 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

 

Лучше, но не на много.

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


Ссылка на сообщение
(изменено)

Это торговлю-то с 17500 костюмами ? (Сорри за малопонятный большинству юмор.)

 

Правильнее на самом деле уменьшить общее количество как конфигов, так и их объем. Многие просто не нужны, и тянутся/плодятся, по тому, как сказал ZD, что в степи стоит 10 метров стены с вмятинами от голов разработчиков из ПЫС.

 

А вынос system_ini() в _g.script - это ловля 2-х микросекунд, но, в общем, их может быть много. Внезапно много.

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

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


Ссылка на сообщение
(изменено)

Смысл - ровно в 2 раза. Не, там, где за один раз в игру - не жалко этих 2-х микросекунд.

Но ведь отдельные гении ухитряются писать циклы наподобие приведенного, и все это - в 20 миллисекундном апдейте x 100 раз.

 

А потом и получается "не может оно за 5 секунд выполняться, если на моем 16-ядерном пентиуме-666 на 100500Терагерц с терабайтом памяти и плазменной панелью на всю стену в кредит взятой - 2 минуты ! И вообще, кто плазменную панель в кредит не купил - тому надо вообще запретить на форум писать !!!"

 

Короче, замеры - выше, код - выше.

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

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


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

В общем, продолжая тему, имеет смысл коснуться печальной истории одной пессимизации, но поскольку касается она в основном Солянки и разнообразных солянкоклонов, то закинул ее в более специализированное место: http://www.amk-team.ru/forum/index.php?showtopic=8830&p=896984

 

У кого такого нет - то просто имейте в виду, что такие грабли есть, а добавлять и заменять ни где ничего не надо. Из солянки же в конечном итоге ВСЕ это - хоть то, что было, хоть предлагаемое на замену, следует убрать. По мере истребления всех имеющихся ссылок на ЭТО.

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


Ссылка на сообщение
(изменено)

Ну, во-первых, как бы 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 МЕСЯЦЕВ дожидаться, пока темы перенесут из закрытого раздела в общий, дабы имевшие возможность нафлудится вдосталь, предпочли реализовать именно на публике. Еще более жаль, что имея возможность сказать много полезного, предпочли заняться выяснением "Кто разрешил ?"

Изменено пользователем Dennis_Chikin
  • Нравится 1

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


Ссылка на сообщение
(изменено)

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

 

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

И, да, переменную переименовать.

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

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


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

На счет act и переименования - там, кстати, дальше будет функция совершенно феерическая. Просто я до нее не дошел еще.

 

В общем, логично, про защиту от странного. Теоретически - не нужна, на практике - вот черт ее знает.

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


Ссылка на сообщение
(изменено)

Едем дальше.

 

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

 

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

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

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


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

info_id ~= "" - если подобное вызвали - у нас где-то проблемы. Проверка на sim ЗДЕСЬ - по тому что если вызвали, а alife() еще нет - мы об этом все равно ни как сообщить не сможем - и висяк будет весьма загадочный.

 

"А можно ссылку на бенчмарки, исследования по этому поводу?" - функции в сталкере не инлайнятся.

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


Ссылка на сообщение
(изменено)

Ты не понял. 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.

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

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


Ссылка на сообщение
(изменено)

Так как непонятно, к кому обращается Dennis_Chikin

Э-эээ... Распутываю. Текст, естественно, отвечает на предыдущий пост. Кроме того места, где цитата из более раннего.

 

В основной части текста идет речь про то, для чего может быть нужна однострочная функция в глобальном пространстве, в которой к тому же еще и проверяется наличие alife().

То есть, представим момент загрузки какого-нибудь смарта или респавнера (а они грузятся НУ ОЧЕНЬ своеобразно), в котором зачем-то добавили xr_logic.pic_section_непомнючего, которая проверяет это самое инфо.

Вот в этот момент действительно есть вероятность (и я такой код ВИДЕЛ), что все встанет колом, если нашу однострочную функцию "упростить". Но при этом мы и не можем адекватно обработать и случай с несуществующим инфо на входе, так что пущай падает/вылетает/рушится и т.д. ССЗБ тот, кто написал такой странный код.

 

В остальных 99% случаев - да, есть и актор, и инфо, и вот эту однострочную проверку лучше выполнять "на месте", явным образом.

Также есть места, где нам заведомо известно, что и alife() есть, и несуществующих инфо нет, и эти места выполняются точно и при отсутствии актора. Здесь, опять же, явным образом вписать alife():has_info(0, info_id) - то есть, конструкция сама за себя говорит и о том, что делает, и об условиях, в которых работает.

 

Впрочем, это все на любителя, конечно. Кому и 100500 строк if ... end; if ... end (даже не elseif) милее.

Здесь мы вроде бы ВСЕ возможные варианты и условия для них озвучили, вместе с подводными камнями, так что цель разбора вроде как достигнута.

 

 

Посты с личными разборками, которые здесь были, ушли по назначению. Не зависимо от того, что в них было кроме разборок.

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

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


Ссылка на сообщение
(изменено)

Не помню, писал, или нет, но:

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

Изменено пользователем Dennis_Chikin
  • Полезно 1

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


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

<<"универсальные" схемы>> - хе-хе. Ну, да, 10 скриптов делают одно и то же, 10 - ровно противоположное, результат работы 100500 тыщ циклов в секунду по 100500 тыщ операторов абсолютно идентичен обыкновенному math.random(), причем все эти скрипты еще и между собой жестко конфликтуют, типа один посылает непися в одну точку, второй - в другую, третий манипулирует с оружием, четвертый в это время это оружие удаляет, пятый его же перемешает в подземный ящик (кто себя здесь узнал - я не виноват, что у вас целых 6 скриптов для того, что делается одной строкой конфига), шестой выставляет mental_state(anim.free), седьмой в это же время - body_state(move.crouch)... И так далее...

 

Впрочем, это не совсем в данную тему, и даже не факт, что в этот раздел.

  • Полезно 1

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


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

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

AMK-Team.ru

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