加密方式
lua 代码加密方式很很多种,这里只讲最简单的一种,就是把代码编译成二进制字节码。lua 是一门脚本语言,不需要经过编译就可以使用 lua 解释器来执行;但我们也可以把 lua 代码事先编译成二进制文件,这样就达到了加密的方式。
使用 lua 编译器
最简单也最直接的方式就是使用 lua 编译器来编译 lua 代码,编译的方式很简单,打开命令行,输入下面的命令
lucac -o dest.lua source.lua
-o 参数指定生成的目标文件名,可以用 .lua 作后缀,也可以用其它后缀,其本质上是一个二进制文件,与文件的后缀并没有什么关系。但是,为了可以使用 require 来加载这个文件,文件的后缀还是必须使用 .lua。一般可以使用原来的文件名,这样使用到这个文件的其它文件就不用修改代码。
下面写一个打印星星的例子,测试编译的过程和编译后能否正确执行
-- test.lua
local str = [=[ hello ]=]
print(str)
local function printStar(n)
local str
for i = 1, math.ceil(n / 2), 1 do
str = " "
for j = 1, math.ceil(n / 2) - i do
str = str .. " "
end
for j = 1, 2 * (i - 1) + 1 do
str = str .. "*"
end
print(str)
end
for i = math.ceil(n / 2) - 1, 1, -1 do
str = " "
for j = 1, math.ceil(n / 2) - i do
str = str .. " "
end
for j = 1, 2 * (i - 1) + 1 do
str = str .. "*"
end
print(str)
end
end
printStar(10)
打开 cmd,输入
luac -o out.lua test.lua
用记事本打开 out.lua,发现是乱码,说明它是个二进制文件。继续在 cmd 中输入 lua
,进入到 lua 解释器环境,输入
require("out")
看到运行下面的结果则表示 lua 解释器成功执行了二进制文件
hello
*
***
*****
*******
*********
*******
*****
***
*
关于 lua 解释器和编译器可以参考这篇文章
使用 luajit
使用 lua 编译器来编译 lua 代码虽然简单方便,但编译后的字节码却不能在 cocos2d-x 中使用,原因是 cocos2d-x 使用的是 luajit。luajit 是一套合适 c 语言写的 lua 解释器代码,是一种即时编译器(JIT),与原生的 lua 编译器有所区别。也就量说同一个文件使用 luajit.exe 编译后的字节码和使用 luac.exe 编译后字节码是不一样,因此使用 luac.exe 编译后的字节码不能在 cocos2d-x 中正确加载,正确的方式是使用 luajit 来编译 lua 源代码。
获取 luajit
看了网上的教程,都是说在 %COCOS_ROOT%\scripting\lua\luajit\LuaJIT-2.0.1\src 目录下有个 msvcbuild.bat 批处理文件,然后执行这个脚本就可以生成一个 luajit.exe 文件。但这是 cocos2d 2.x 的做法,在 3.x 中并没有找到这个脚本文件。网上的教程抄袭成风,找了好几篇博客,内容全是一模一样的,都是讲 2.x 的做法。最后自己使用 everything 工具搜索资源管理器,发现在 %COCOS_ROOT%\tools\cocos2d-console\plugins\plugin_luacompile\bin 下面就有一个现在的 luajit.exe;从 2.x 到 3.x 总要有些进步的,现在已经不用自己去生成 luajit 了,引擎已经帮我们集成好了。
使用 luajit 来编译源代码
使用 luajit 编译 lua 源代码和使用原生的 lua 编译器差不多,在 luajit 所在目录打开命令行,输入
luajit.exe -b dest.lua source.lua
注意源文件和目标文件要加上路径。
使用脚本批处理
一个项目的源文件有几十个甚至几百个,一个个手动在命令行输入命令编译显然是不现实的,因此需要一个脚本来批量处理。
@echo off
rem @表示不显示 echo off 这一行,echo off 表示不显示 echo off 这一行之后所有命令
rem 定义标号
:input
rem 定义变量
set luapath=:
rem 提示用户输入并把输入的内容赋给变量
set /p luapath= 拖入要编译的lua文件夹:
rem 如果输入的内容为空则重新输入
if "%luapath%"==":" goto input
rem 如果输入的路径找不到则重新输入
if not exist "%luapath%" goto input
for %%i in ("%luapath%") do if /i "%%~di"==%%i goto input
set out=%luapath%\out
rem 如果存在文件夹 out,则删除
if exist %out% rd /s /q %out%
rem 重新创建目录 out
mkdir %out%
pushd %cd%
cd /d "%luapath%">nul 2>nul || exit
set cur_dir=%cd%
popd
rem 定义变量并赋初值
set /a num = 0
set luajit=D:\engine\cocos\Cocos2d-x\cocos2d-x-3.10\tools\cocos2d-console\plugins\plugin_luacompile\bin\luajit
for /f "delims=" %%i in ('dir /b /a-d /s "%luapath%"') do (set /a num += 1 & %luajit% -b %%~fsi %out%/%%~nxi & echo %%~nxi)
echo 编译脚本数量:%num%
pause
这是一个 bat 脚本示例,这个脚本要求用户输入一个目标文件夹,然后把文件夹下面所有的 lua 文件进行编译,保留在目标文件夹下面的 out 文件夹里面。这个脚本还很不完善,首先它要求目标文件夹下面的所有文件必须是 lua 文件,其次它把生成的所有文件放在一个目录下,如果目标文件夹下面有嵌套目录的话,生成后的文件就无法保持原来的目录结构了。由于我没学过 bat 和 shell 脚本,只能等以后学了再写一个更好的脚本。
2017-08-10 补充
下面是一个 shell 脚本,这个脚本使用 luajit 把 src-raw 下面的所有 lua 源代码编译成字节码,保存在 src 目录下,保持原来的目录结构。
#!/bin/bash
luajit=/d/engine/cocos/Cocos2d-x/cocos2d-x-3.10/tools/cocos2d-console/plugins/plugin_luacompile/bin/luajit.exe
cd ..
if [ ! -d src-raw ]; then
echo "src-raw 目录不存在!"
read -p "输入回车键结束..."
exit 1
fi
if [ ! -d src ]; then
mkdir src
fi
function compile()
{
for sub in $1/*
do
if [ -f $sub ]; then
if [[ ${sub##*.} = lua ]]; then
dest=${sub/src-raw/src}
if [[ ! -f $dest ]] || [[ $sub -nt $dest ]]; then
echo compile $sub
`$luajit -b $sub $dest`
fi
fi
elif [ -d $sub ]; then
dest=${sub/src-raw/src}
if [ ! -d $dest ]; then
mkdir $dest
fi
compile $sub
fi
done
}
compile ./src-raw
read -p "输入回车键结束..."
exit 0