[lua] Собираем lua с помощью lua

» Раздел: Триггеры и объекты
Если вы пользуетесь подсветкой кода во внешнем редакторе, то наверняка сталкивались с проблемой переноса его в карту. Можно конечно использовать WLPM или его аналоги, но мы пойдём путём наименьшего сопротивления и будем собирать lua с помощью lua.
В данном примере я буду использовать IntelliJ IDEA, но этот способ не привязан к какой либо среде разработки. Если кто опишет как настроить VS Code для запуска lua, то с удовольствием добавлю статью.
Создадим проэкт и настроим подсветку кода:
С картой нам придётся работать в режиме папки так что сохраним её в проэкт. Не забыв переключить в режим lua.
Существует маленькая хитрость, чтоб новая карта носила гордое разрешение .w3x допишите комментарий в Нестандартный код карты.
Создадим папку build в которой будем держать весь код для сборки. Заодно и заполним её тестовым кодом.
Собранный код можно конечно дописывать в конец war3map.lua, чем и занимаются практически все сборщики, мотивируя тем, что можно хукать main. Но мы пойдём другим путём и будем править war3map.wct чтоб собранный код виделся редактором как Нестандартный код карты. Чтобы избавить нас от неблагодарной работы по разбору формата карт Hodor написал custom-code-replacer.exe, который можно положить в папку проэкта и не забыть сказать автору спасибо.
Теперь можно приступать к самой сборке кода. Так как собирать код мы будем с помощью того же языка, на котором и будем его писать, то не буду вас утруждать разжовыванием каждой строчки. Просто скопируйте папку run в корень проэкта:
» /run/lib/DirTree.lua
-- http://lua-users.org/wiki/DirTreeIterator
function DirTree(dir)
	assert(dir and dir ~= "", "directory parameter is missing or empty")
	if string.sub(dir, -1) == "/" then
		dir = string.sub(dir, 1, -2)
	end
	
	local function yieldtree(d)
		for entry in lfs.dir(d) do
			if entry ~= "." and entry ~= ".." then
				entry      = d .. "\\" .. entry
				local attr = lfs.attributes(entry)
				coroutine.yield(entry, attr)
				if attr.mode == "directory" then
					yieldtree(entry)
				end
			end
		end
	end
	
	return coroutine.wrap(function() yieldtree(dir) end)
end
» /run/lib/FileContent.lua
--https://stackoverflow.com/questions/11201262/how-to-read-data-from-a-file-in-lua
local open = io.open
function FileContent(path)
	local file = open(path, 'rb') -- r read mode and b binary mode
	if not file then return nil end
	local content = file:read '*a' -- *a or *all reads the whole file
	file:close()
	return content
end
» /run/build.lua
require 'lfs' -- подключаем LuaFileSystem https://keplerproject.github.io/luafilesystem/manual.html
local param = {
	game       = [[D:\Games\Warcraft III\x86_64]], -- папка с игрой
	map        = [[\map.w3x]], -- папка с картой
	customCode = [[\custom-code.lua]], -- файл, в который собирается весь код
	patcher    = [[\custom-code-replacer.exe]], -- патчер для .wct
	files      = { -- порядок сборки файлов
		[[\build\Ability]],
		[[\build\libs]],
		[[\build\Init.lua]],
	},
	tag        = [[--CUSTOM_CODE]], -- тэг для вставки кода
	current    = lfs.currentdir() -- текущая папка проэкта
}

-- подключаем нужные функции
dofile(param.current .. [[\run\lib\DirTree.lua]])
dofile(param.current .. [[\run\lib\FileContent.lua]])

-- собираем всё в один файл
local customCode = io.open(param.current .. param.customCode, 'w+')
customCode:write(param.tag, '\n')
for i = 1, #param.files do
	local file = param.files[i]
	local path = param.current .. file
	if file:match "[^.]+$" == 'lua' then
		customCode:write(FileContent(path), '\n')
	else
		for filepath, attr in DirTree(path) do
			if (attr.mode == 'file') then
				customCode:write(FileContent(filepath), '\n')
			end
		end
	end
end
customCode:write(param.tag)
customCode:close()

-- заменяем код в war3map.lua
local path    = param.current .. param.map .. '\\war3map.lua'
local war3map = io.open(path, 'r')
customCode    = io.open(param.current .. param.customCode, 'r')
local content = war3map:read('*a')
war3map:close()
war3map           = io.open(path, 'w+')
local repl, count = string.gsub(content, param.tag .. '.*' .. param.tag, customCode:read('*a'))
war3map:write(repl)
war3map:close()
customCode:close()

-- патчим .wct
os.execute('start "" "' .. param.current .. param.patcher .. '" "' .. param.current .. param.map .. '\\war3map.wct" "' .. param.current .. param.customCode .. '"')

-- запускаем игру
if type(IsRunGame) == 'boolean' then
	os.execute('start  "" "' .. param.game .. '\\' .. 'Warcraft III.exe" -loadfile "' .. param.current .. '\\' .. param.map .. '"')
end

-- запускаем редактор
if type(IsRunEditor) == 'boolean' then
	os.execute('start  "" "' .. param.game .. '\\' .. 'World Editor.exe" -loadfile "' .. param.current .. '\\' .. param.map .. '"')
end
Единственно стоит обратить внисание на эту часть /run/build.lua
local param = {
	game       = [[D:\Games\Warcraft III\x86_64]], -- папка с игрой
	map        = [[\map.w3x]], -- папка с картой
	customCode = [[\custom-code.lua]], -- файл, в который собирается весь код
	patcher    = [[\custom-code-replacer.exe]], -- патчер для .wct
	files      = { -- порядок сборки файлов
		[[\build\Ability]],
		[[\build\libs]],
		[[\build\Init.lua]],
	},
	tag        = [[--CUSTOM_CODE]], -- тэг для вставки кода
	current    = lfs.currentdir() -- текущая папка проэкта
}
Теперь проэкт должен выглядеть примерно таким образом.

Если вы дочитали до этого момента и всё ещё задаётесь вопросом, каким же образом мы будем исполнять скрипты lua на компьютере, то ответ будет незамысловат - мы его скачаем с luadist.org
Распаковать скачанный архив можно куда угодно, но я выбрал диск "C:\".
Осталось только настроить запуск скриптов из редактора и можно приступать к первой сборке

Запустим файл /run/build.lua
Если вы всё сделали правильно, то сможете наблюдать файл custom-code.lua в корне проэкта, содержащий весь собранный код.
Теперь можно таким же образом запустить run-in-game.lua и проверить, что карта открывается в игре. И наконец run-in-editor.lua, чтоб убедиться в том, что ваш код попал в редактор.
Важно! Сохраните карту в редакторе и закройте его, чтоб код попал в war3map.lua, при каждой последующей сборке он будет автоматически обновляться.

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

Полезные ссылки

P.S.

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


Views: 3 039

Koladik #1 - 2 years ago (изм. ) 0
Голосов: +0 / -0
Все отлично работает кроме того, что в custom-code-replacer сбилась кодировка и он код в war3map.wct не загружает, это только у меня такая проблема? Вообще было бы отлично, если бы структура кода в build повторяла структуру в карте.
NazarPunk #2 - 2 years ago 0
Голосов: +0 / -0
что в custom-code-replacer сбилась кодировка и он код в war3map.wct не загружает, это только у меня такая проблема?
У меня проблем с ним не возникало, но всёравно я уже пишу статью, как разбирать .wct файлы с помощью .lua.
Вообще было бы отлично, если бы структура кода в build повторяла структуру в карте.
Нет ничего проще, просто укажите
files      = { -- порядок сборки файлов
	[[\build]]
},
и код будет собран в той последовательности, в которой он находится в редакторе.
Koladik #3 - 2 years ago (изм. ) 0
Голосов: +0 / -0
Нет ничего проще, просто укажите
Я имею в виду, что у вас весь код из custom-code.lua в "нестандартный код" в заголовке отправляет в самом редакторе сплошным текстом, а было бы замечательно, если бы в структура папок создавалась.
NazarPunk:
я уже пишу статью, как разбирать .wct файлы с помощью .lua.
С нетерпением жду! Я хотел бы сам себе подобное приписать, но понял, что просто вставлять код внутри .wct не вариант и это сложнее чем кажется. Просить исходник custom-code-replacer, думаю, слишком нагло)
NazarPunk #4 - 2 years ago 0
Голосов: +0 / -0
Я имею в виду, что у вас весь код из custom-code.lua в "нестандартный код" в заголовке отправляет в самом редакторе сплошным текстом, а было бы замечательно, если бы в структура папок создавалась.
Структура папок есть в IDE, не вижу смысла её дублировать в редакторе. Притом в планах прикрутить оптимайзер, который вообще превратит код в нечитаемую кашу.
pro100master #5 - 2 years ago (изм. ) 0
Голосов: +0 / -0
NazarPunk, если изобрели обфускации jass lua =)
NazarPunk #6 - 2 years ago 0
Голосов: +0 / -0
если изобрели обфускации jass lua =)
Lua популярный язык почему ж нет? Инлайнеры точно есть.
umbrella_gaming #7 - 2 years ago 0
Голосов: +0 / -0
Те в карте можно юзать либо lua либо jass? микс нельзя сделать?
NazarPunk #8 - 2 years ago 0
Голосов: +0 / -0
Те в карте можно юзать либо lua либо jass? микс нельзя сделать?
Микс сделать нельзя, но можно использовать cjass2lua и сконвертировать старый jass в lua.
ScorpioT1000 #9 - 2 years ago 0
Голосов: +0 / -0
Почему папка src названа build?)
NazarPunk #10 - 2 years ago (изм. ) 0
Голосов: +0 / -0
Почему папка src названа build?)
Потому что там лежат файлы для сборки, да и структуру папок выбирает сам пользователь, дефолтов же просто нет.
pro100master #11 - 2 years ago 0
Голосов: +0 / -0
src - до сборки то есть оригинальный код который не влияет в игре
build - после сборки и компиляции
Странный у вас метод =) Хотя я же на ноде пишу забудь...
NazarPunk #12 - 2 years ago 0
Голосов: +0 / -0
Странный у вас метод
Какой есть, конструктивные замечания принимаются.
Хотя я же на ноде пишу забудь...
Возьмите с полки пирожок. А пока будете это делать напишите, какое это имеет отношение к этой теме.
Bergi_Bear #13 - 2 years ago 0
Голосов: +0 / -0
А пока будете это делать напишите, какое это имеет отношение к этой теме
это как говорить, что "я веган"
pro100master #14 - 2 years ago 0
Голосов: +0 / -0
NazarPunk, из ts компилит в lua а потом дособирает в карту. Все имеет к отношении!
Zetox #15 - 2 years ago 0
Голосов: +0 / -0
После того собираю проект, запускается не карта, а приложение battle net, можно как-то это избежать ?
Bergi_Bear #16 - 2 years ago 2
Голосов: +2 / -0
Zetox, да, у тебя скорее всего старый сборщик, перекачай и есть новая тема