读一读

加密:

在自定义的PostProcessBundles方法中,对复制到ServerData的bundle进行读取,然后加密后写入覆盖。

//加密
byte[] datas = File.ReadAllBytes(targetPath);
File.WriteAllBytes(targetPath, AES.AESEncrypt(datas, KEY));



解密:

在自定义的AssetBundleProvider中的BeginOperation,所有的bundle的加载都先拿到byte数组先,不能直接用AssetBundle.LoadFromFile什么的了。

第三个网络下载的,后面会走第二个缓存bundle的逻辑。

private void BeginOperation()
{
    string path = m_ProvideHandle.Location.InternalId;
    var url = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location);
    string bundleName = Path.GetFileName(url);
    // if a path starts with jar:file, it is an android embeded resource. The resource is a local file but cannot be accessed by 
    // FileStream(called in LoadWithDataProc) directly
    // Need to use webrequest's async call to get the content.
    if (AssetBundleManager.Instance.buildInData != null && AssetBundleManager.Instance.buildInData.BuildInBundleNames.Contains(bundleName))//本地资源
    {
        string streamPath = UnityEngine.AddressableAssets.Addressables.RuntimePath + "/" + PlatformMappingService.GetPlatformPathSubFolder() + "/" + bundleName;
        Debug.Log("LoadOne:" + streamPath);
        var crc = m_Options == null ? 0 : m_Options.Crc;
        var req = CreateWebRequest(streamPath);
        req.disposeDownloadHandlerOnDispose = false;
        m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req);
        if (m_WebRequestQueueOperation.IsDone)
        {
            m_RequestOperation = m_WebRequestQueueOperation.Result;
            m_RequestOperation.completed += StreamWebRequestOperationCompleted;
        }
        else
        {
            m_WebRequestQueueOperation.OnComplete += asyncOp =>
            {
                m_RequestOperation = asyncOp;
                m_RequestOperation.completed += StreamWebRequestOperationCompleted;
            };
        }
    }
    else if (AssetBundleManager.Instance.IsCache(bundleName))
    {
        string cachePath = Path.Combine(AssetBundleManager.Instance.GetBundleCachePath(), bundleName);
        Debug.Log("LoadTwo:" + cachePath);
        var crc = m_Options == null ? 0 : m_Options.Crc;
        m_RequestOperation = AssetBundle.LoadFromMemoryAsync(AES.AESDecrypt(File.ReadAllBytes(cachePath), AES.GenBundleKey()));
        m_RequestOperation.completed += LocalRequestOperationCompleted;
    }
    else if (ResourceManagerConfig.ShouldPathUseWebRequest(path))
    {
        Debug.Log("DownloadThree:" + url);
        var req = CreateWebRequest(m_ProvideHandle.Location);
        req.disposeDownloadHandlerOnDispose = false;
        m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req);
        if (m_WebRequestQueueOperation.IsDone)
        {
            m_RequestOperation = m_WebRequestQueueOperation.Result;
            m_RequestOperation.completed += WebRequestOperationCompleted;
        }
        else
        {
            m_WebRequestQueueOperation.OnComplete += asyncOp =>
            {
                m_RequestOperation = asyncOp;
                m_RequestOperation.completed += WebRequestOperationCompleted;
            };
        }
    }
    else
    {
        m_RequestOperation = null;
        m_ProvideHandle.Complete<MyAssetBundleResource>(null, false, new Exception(string.Format("Invalid path in AssetBundleProvider: '{0}'.", path)));
    }
}

private void StreamWebRequestOperationCompleted(AsyncOperation op)
{
    UnityWebRequestAsyncOperation remoteReq = op as UnityWebRequestAsyncOperation;
    var webReq = remoteReq.webRequest;
    if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult))
    {
        m_downloadHandler = webReq.downloadHandler;
        CompleteBundleLoad(AssetBundle.LoadFromMemory(AES.AESDecrypt(m_downloadHandler.data, KEY)));
    }
    webReq.Dispose();
}



需要准备:

  1. tolua_runtime源码 下载

  2. lua_protobuf 下载

  3. 配置好的环境msys2 链接 提取码:6666


一、将lua-protobuf的pb.h和pb.c替换tolua_runtime根目录的pb.c

二、打开pb.c替换luaop_pb函数

LUALIB_API int luaopen_pb(lua_State *L) {
        luaL_Reg libs[] = {
            { "pack",     Lbuf_pack     },
            { "unpack",   Lslice_unpack },
    #define ENTRY(name) { #name, Lpb_##name }            ENTRY(clear),
            ENTRY(load),
            ENTRY(loadfile),
            ENTRY(encode),
            ENTRY(decode),
            ENTRY(types),
            ENTRY(fields),
            ENTRY(type),
            ENTRY(field),
            ENTRY(typefmt),
            ENTRY(enum),
            ENTRY(defaults),
            ENTRY(hook),
            ENTRY(tohex),
            ENTRY(result),
            ENTRY(option),
            ENTRY(state),
    #undef  ENTRY            { NULL, NULL }
        };
        luaL_Reg meta[] = {
            { "__gc", Lpb_delete },
            { "setdefault", Lpb_state },
            { NULL, NULL }
        };
        if (luaL_newmetatable(L, PB_STATE)) {
            luaL_setfuncs(L, meta, 0);
            lua_pushvalue(L, -1);
            lua_setfield(L, -2, "__index");
        }
        #if LUA_VERSION_NUM < 502        luaL_register(L, "pb", libs);
        #else        luaL_newlib(L, libs);
        #endif
        return 1;
    }


三、编译

  1. 打开msys2 32编译环境 cd到tolua_runtime位置

  2. ./build_win32.sh 编译win的tolua.dll,目录在Plugins/x86下

  3. 打开编辑android相关的(build_arm.sh、build_x86.sh、link_arm64.bat),把NDK路径改为本电脑路径,我用的版本是android-ndk-r10e-windows-x86_64,然后在msys2 的32位下执行 ./build_arm.sh,执行./build_x86.sh。编译后的tolua.so在Plugins/Android下

  4. arm64的参考上面步骤,使用msys2 64编译环境

  5. ios和mac没试,需要MAC系统,没条件的可以搞虚拟机弄弄,用终端直接执行相关脚本。


四、库的使用

1.在Unity中找到LuaDLL.cs,找到lua_open_pb位置,替换代码

/*
** third party library
*/
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_pb(IntPtr L);

[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_pb_io(IntPtr L);

[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_pb_conv(IntPtr L);

[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_pb_buffer(IntPtr L);

[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int luaopen_pb_slice(IntPtr L);


2.在你创建虚拟机的地方添加打开Lib

// m_LuaState 为 LuaState 对象
m_LuaState.OpenLibs(LuaDLL.luaopen_pb_io);
m_LuaState.OpenLibs(LuaDLL.luaopen_pb_conv);
m_LuaState.OpenLibs(LuaDLL.luaopen_pb_buffer);
m_LuaState.OpenLibs(LuaDLL.luaopen_pb_slice);


3.复制lua-protobuf的protoc.lua和serpent.lua到你的lua脚本目录


今天遇到一个让人哭笑不得的问题,就是我在lua中调用Physics.OverlapSphereNonAlloc一直都检测不到范围内的Collider物体,缓冲的数组也有10的大小,不可能说获取不到的。后面想了想,那应该就是生成Wrap有问题了,结果一看

[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
	static int OverlapSphereNonAlloc(IntPtr L)
	{
		try
		{
			int count = LuaDLL.lua_gettop(L);

			if (count == 3)
			{
				UnityEngine.Vector3 arg0 = ToLua.ToVector3(L, 1);
				float arg1 = (float)LuaDLL.luaL_checknumber(L, 2);
                UnityEngine.Collider[] arg2 = null;

                int o = UnityEngine.Physics.OverlapSphereNonAlloc(arg0, arg1, arg2);
				LuaDLL.lua_pushinteger(L, o);
				return 1;
			}
			else
			{
				return LuaDLL.luaL_throw(L, "invalid arguments to method: UnityEngine.Physics.OverlapSphereNonAlloc");
			}
		}
		catch (Exception e)
		{
			return LuaDLL.toluaL_exception(L, e);
		}
	}


哈哈哈,它把缓冲数组直接传null了,并不是我传过来的数组,改成这样就好了

UnityEngine.Collider[] arg2 = ToLua.CheckObjectArray<UnityEngine.Collider>(L, 3);



function Exist2(v)
    return v == 2
end

function IsEven(v)
    return v % 2 == 0
end

function NotExist(v)
    return false
end

function Compare(a, b)
    if a > b then 
        return 1
    elseif a == b then
        return 0
    else
        return -1
    end
end

function Test(list, list1)  list为List<int>,数据1,2,4       
    list:Add(123)
    print('Add result: list[0] is '..list[0]) --123
    list:AddRange(list1)
    print(string.format('AddRange result: list[1] is %d, list[2] is %d', list[1], list[2]))

    local const = list:AsReadOnly()
    print('AsReadOnley:'..const[0])  --123  

    index = const:IndexOf(123) --0
                
    if index == 0 then
        print('const IndexOf is ok')
    end

    local pos = list:BinarySearch(1) --1
    print('BinarySearch 1 result is: '..pos)

    if list:Contains(123) then
        print('list Contain 123')
    else
        error('list Contains result fail')
    end

    if list:Exists(Exist2) then
        print('list exists 2')
    else
        error('list exists result fail')
    end                    
                
    if list:Find(Exist2) then
        print('list Find is ok')
    else
        print('list Find error')
    end

    local fa = list:FindAll(IsEven)

    if fa.Count == 2 then
        print('FindAll is ok')
    end

    --注意推导后的委托声明必须注册, 这里是System.Predicate<int>
    local index = list:FindIndex(System.Predicate_int(Exist2))

    if index == 2 then
        print('FindIndex is ok')
    end

    index = list:FindLastIndex(System.Predicate_int(Exist2))

    if index == 2 then
        print('FindLastIndex is ok')
    end                
                
    index = list:IndexOf(123)
                
    if index == 0 then
        print('IndexOf is ok')
    end

    index = list:LastIndexOf(123)
                
    if index == 0 then
        print('LastIndexOf is ok')
    end

    list:Remove(123)

    if list[0] ~= 123 then
        print('Remove is ok')
    end

    list:Insert(0, 123)

    if list[0] == 123 then
        print('Insert is ok')
    end

    list:RemoveAt(0)

    if list[0] ~= 123 then
        print('RemoveAt is ok')
    end

    list:Insert(0, 123)
    list:ForEach(function(v) print('foreach: '..v) end)
    local count = list.Count      

    list:Sort(System.Comparison_int(Compare))
    print('--------------sort list over----------------------')
                                
    for i = 0, count - 1 do
        print('for:'..list[i])
    end

    list:Clear()
    print('list Clear not count is '..list.Count)
end

function Test()
    local str = System.String.New('男儿当自强A')
    local index = str:IndexOfAny('儿自') 
    print('and index is: '..index) --1
    local buffer = str:ToCharArray()
    print('str type is: '..type(str)..' buffer[5] is ' .. buffer[5])  --65
    local luastr = tolua.tolstring(buffer)
    print('lua string is: '..luastr..' type is: '..type(luastr)) --string
    luastr = tolua.tolstring(str)
    print('lua string is: '..luastr)  --男儿当自强A              
end

local utf8 = utf8

function Test()        
    local l1 = utf8.len('你好') --计算长度
    print('chinese string len is: '..l1)     

    local s = '遍历s字符串'                                        

    --i为每个字符的开始位置 next为下一个字符的开始位置
    for i in utf8.byte_indices(s) do            
        local next = utf8.next(s, i) 
        print(s:sub(i, next and next - 1)) --截取出来就是当前的字符
    end   

    local s1 = '天下风云出我辈风云'        
    print('风云 count is: '..utf8.count(s1, '风云')) --计算风云在字符串中的个数
    s1 = s1:gsub('风云', '風雲') --替换并返回整个串
    print(s1)
end

local json = require 'cjson' --用cjson库

function Test(str)
    local data = json.decode(str) --解析json
    print(data.glossary.title)
    s = json.encode(data) --序列化为json
    print(s)
end

local num1 = int64.new('789545665421245')
local num = int64.new(201,1) --第一个参数为低32位,第二个参数为高32位

local l,h = int64.tonum2(num1)  --转化为低高位的表达方式

local n = int64.new(123,0) --123
local m = int64.new(123,0) --123
if int64.equals(n,123) then --int64和数值对比
    print(tostring(n))
end
if n==m then --int64和int64对比
    print(tostring(m))
end

using UnityEngine;
using LuaInterface;

public class TestOutArg : MonoBehaviour 
{            
    string script =
        @"                                
                                                                            
            function TestPick(pos)    
                local camera = UnityEngine.Camera.main
                local ray  = camera:ScreenPointToRay(pos)
                local _layer = 2 ^ LayerMask.NameToLayer('Default')                        
                local flag, hit = UnityEngine.Physics.Raycast(ray, nil, 5000, _layer)                                            
                                
                if flag then
                    print('pick from lua, point: '..tostring(hit.point))                                        
                end
            end
        ";

    LuaState state = null;
    LuaFunction func = null;

    void Start () 
    {
        new LuaResLoader();
        state = new LuaState();
        LuaBinder.Bind(state);
        state.Start();
        state.DoString(script, "TestOutArg.cs");
        func = state.GetFunction("TestPick");

    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            func.BeginPCall();
            func.Push(Input.mousePosition);
            func.PCall();
            func.EndPCall();
        }

        state.CheckTop();
        state.Collect();
    }

    void OnDestroy()
    {
        func.Dispose();
        func = null;

        state.Dispose();
        state = null;
    }
}


涉及out参数的,直接传nil,它会以返回值的形式返回,注意第一个返回值为状态,第二个开始才是out的返回值


ToLua中有一个LuaFileUtils.cs负责去哪里寻找lua文件,然后读取里面的bytes内容。当出现一些不同的加载方式想自定义,就可以创建一个Loader然后继承LuaFileUtils,重写里面的方法。

using UnityEngine;
using System.Collections;
using System.IO;
using LuaInterface;

public class LuaLoader : LuaFileUtils {

    // Use this for initialization
    public LuaLoader(bool isZip = false) {
        instance = this;//外面只要new就好了,单例模式的实例为当前实例了
        beZip = isZip;
    }
    
    public void AddBundle(string bundleName) {
    string url = "";
#if !UNITY_EDITOR
    url = LuaConst.luaResDir + "/" + bundleName.ToLower();
#else
    url = LuaConst.luaWinTestResDir + "/" + bundleName.ToLower();
#endif
    if (File.Exists(url)) {
            
        AssetBundle bundle = AssetBundle.LoadFromFile(url);
            if (bundle != null)
            {
                bundleName = bundleName.Replace("lua/", "").Replace(".mp", "");
                base.AddSearchBundle(bundleName.ToLower(), bundle);
            }
        }
    }
    
    public override byte[] ReadFile(string fileName) {
        //怎么读取,可以自己重写
        return base.ReadFile(fileName);     
    }
}

使用方法就是在你的LuaManager中,初始化new一个自定义的LuaLoder就可以了。