• Unity Xlua热更新框架(九):网络部分


    14. 编译Xlua第三方库

    14-1. 编译Xlua第三方库

    通信协议:

    • protobuf、sproto、pbc、pblua、json、cjson……用于服务器与客户端通信的数据格式

    Xlua不带这些第三方库
    需要把第三方库下载下来,编译,,,对于网络部分已经有现成的项目https://github.com/chexiongsheng/build_xlua_with_libs
    查看Xlua文档添加第三库
    image.png

     public void Init()
    {
        //初始化虚拟机
        LuaEnv = new LuaEnv();
        
        //添加第三方库扩展
        LuaEnv.AddBuildin("rapidjson", XLua.LuaDLL.Lua.LoadRapidJson);
        
        //外部调用require时,会自动调用loader来获取文件
        LuaEnv.AddLoader(Loader);
    
        m_LuaScripts = new Dictionary<string, byte[]>();
    
    #if UNITY_EDITOR
        if (AppConst.GameMode == GameMode.EditorMode)
            EditorLoadLuaScript();
        else
    #endif
            LoadLuaScript();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    然后使用Cmake安装
    上面的第三方库的build文件夹中有CMakeList,里面有#Begin lua-rapidjson的字段,因此直接编译即可。
    编译过程见博客。
    https://blog.csdn.net/weixin_42264818/article/details/128116856
    将编译好的dll放入Xlua项目的Asset/Plugins/x86_64路径下面,覆盖原有的xlua.dll

    function Main()
      print("hello main")
    
      local rapidjson = require('rapidjson')
      local t = rapidjson.decode('{"a":123}')--解码json字符串为table
      print(t.a)
      t.a = 456
      local s = rapidjson.encode(t)--编码json字符串
      print('json', s)
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image.png

    14-2. 网络客户端(C#)

    需要完成:服务器连接、消息发送、消息接收、数据解析
    创建Framework/Network/NetClient脚本

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using UnityEngine;
    
    public class NetClient
    {
        private TcpClient m_Client;//基于TCP封装好的socket
        private NetworkStream m_TcpStream;//可以从TCP里获取网络流
        private const int BufferSize = 1024 * 64;//接收数据的大小
        private byte[] m_Buffer = new byte[BufferSize];//接收数据的缓存区
        private MemoryStream m_MemStream;//MemoryStream类用于向内存而不是磁盘读写数据
        private BinaryReader m_BinaryReader;//用特定的编码将基元数据类型读作二进制值
    
        //构造方法,实例化m_MemStream、m_BinaryReader
        public NetClient()
        {
            m_MemStream = new MemoryStream();
            m_BinaryReader = new BinaryReader(m_MemStream);
        }
        
        /// 
        /// 连接服务器
        /// 
        /// ip地址
        /// 端口号
        public void OnConnectServer(string host, int port)
        {
            try
            {
                IPAddress[] addresses = Dns.GetHostAddresses(host);
                if (addresses.Length == 0)
                {
                    Debug.LogError("host invalid");
                    return;
                }
                //判断地址族是ipv6还是ipv4
                if (addresses[0].AddressFamily == AddressFamily.InterNetworkV6)
                    m_Client = new TcpClient(AddressFamily.InterNetworkV6);
                else
                    m_Client = new TcpClient(AddressFamily.InterNetwork);
                //判断好地址族后,设置参数
                m_Client.SendTimeout = 1000;
                m_Client.ReceiveTimeout = 1000;
                m_Client.NoDelay = true;
                //开始连接,连接成功发起异步回调OnConnect
                m_Client.BeginConnect(host, port, OnConnect, null);
            }
            catch (Exception e)
            {
                Debug.LogError(e.Message);
            }
        }
    
        //连接成功后执行
        private void OnConnect(IAsyncResult asyncResult)
        {
            //判断是否连接成功
            if (m_Client == null || !m_Client.Connected)
            {
                Debug.LogError("Connect server error!");
                return;
            }
            Manager.Net.OnNetConnected();
            m_TcpStream = m_Client.GetStream();
            //开始接收数据
            m_TcpStream.BeginRead(m_Buffer, 0, BufferSize, OnRead, null);
        }
    
        //开始接收数据执行
        private void OnRead(IAsyncResult asyncResult)
        {
            try
            {
                if (m_Client == null || m_TcpStream == null)
                    return;
                //判断是否读取到了空消息,这个是有效字节数
                int length = m_TcpStream.EndRead(asyncResult);
                
                if (length < 1)
                {
                    OnDisConnected();
                    return;
                }
                ReceiveData(length);//解析数据
                lock (m_TcpStream)//还需要下一次接收数据,需要清空
                {
                    Array.Clear(m_Buffer, 0, m_Buffer.Length);
                    m_TcpStream.BeginRead(m_Buffer, 0, BufferSize, OnRead, null);
                }
            }
            catch (Exception e)
            {
                Debug.LogError(e.Message);
                OnDisConnected();
            }
        }
        
        /// 
        /// 解析数据
        /// 
        private void ReceiveData(int len)
        {
            m_MemStream.Seek(0, SeekOrigin.End);//从末尾追加数据,因为前面可能有残余
            m_MemStream.Write(m_Buffer, 0, len);//写数据
            m_MemStream.Seek(0, SeekOrigin.Begin);//移动指针到前面开始读数据
            //如果服务器有延迟等情况,发过来了很多条,需要通过While语句一条一条解析
            //如果剩余字节数>8,说明消息是完整的,,,=8说明只要id和len没有消息内容是空消息
            while (RemainingBytesLength() >= 8)
            {
                //定义了消息msgId和消息长度msgLen,,读两个int,指针往后走
                int msgId = m_BinaryReader.ReadInt32();
                int msgLen = m_BinaryReader.ReadInt32();
                if (RemainingBytesLength() >= msgLen)//说明消息完整
                {
                    byte[] data = m_BinaryReader.ReadBytes(msgLen);//读这么多个字节的消息
                    string message = System.Text.Encoding.UTF8.GetString(data);//从服务器读出来是json转成字符串
                    
                    //转到lua
                    Manager.Net.Receive(msgId, message);
                }
                else//说明消息不完整或没有消息导致读取有问题,要把前面的id和len这8个字节还回去,然后break出去。
                {
                    m_MemStream.Position = m_MemStream.Position - 8;
                    break;
                }
            }
            //剩余字节,重新写入m_MemStream
            byte[] leftover = m_BinaryReader.ReadBytes(RemainingBytesLength());
            m_MemStream.SetLength(0);
            m_MemStream.Write(leftover, 0, leftover.Length);
        }
        
        //剩余长度,字节数
        private int RemainingBytesLength()
        {
            return (int)(m_MemStream.Length - m_MemStream.Position);
        }
        
        /// 
        /// 发送消息
        /// 
        /// 消息id
        /// json格式数据消息
        public void SendMessage(int msgID, string message)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                ms.Position = 0;
                BinaryWriter bw = new BinaryWriter(ms);
                byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
                //协议id
                bw.Write(msgID);
                //消息长度
                bw.Write((int)data.Length);
                //消息内容
                bw.Write(data);
                bw.Flush();
                if (m_Client != null && m_Client.Connected)
                {
                    byte[] sendData = ms.ToArray();//把内存流的数据拿出来变为Byte发送出去
                    m_TcpStream.BeginWrite(sendData, 0, sendData.Length, OnEndSend, null);//发送
                }
                else
                {
                    Debug.LogError("服务器未连接");
                }
            }
        }
    
        //结束发送
        void OnEndSend(IAsyncResult ar)
        {
            try
            {
                m_TcpStream.EndWrite(ar);
            }
            catch (Exception ex)
            {
                OnDisConnected();
                Debug.LogError(ex.Message);
            }
        }
    
        //断开连接
        public void OnDisConnected()
        {
            if (m_Client != null && m_Client.Connected)
            {
                m_Client.Close();
                m_Client = null;
                
                m_TcpStream.Close();
                m_TcpStream = null;
            }
            Manager.Net.OnDisConnected();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201

    创建Framework/Manager/NetManager脚本

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class NetManager : MonoBehaviour
    {
        private NetClient m_NetClient;//客户端
        //消息队列,用来接收消息
        private Queue<KeyValuePair<int, string>> m_MessageQueue = new Queue<KeyValuePair<int, string>>();
        private XLua.LuaFunction ReceiveMessage;//接收的消息发给lua的方法
    
        public void Init()
        {
            m_NetClient = new NetClient();
            //因为要从lua获取这个方法,肯定要在lua完成初始化之后,而lua的初始化是start之后才初始化好
            ReceiveMessage = Manager.Lua.LuaEnv.Global.Get<XLua.LuaFunction>("ReceiveMessage");
        }
    
        //发送消息
        public void SendMessage(int messageId, string message)
        {
            m_NetClient.SendMessage(messageId,message);
        }
        
        //连接服务器
        public void ConnectServer(string post, int port)
        {
            m_NetClient.OnConnectServer(post, port);
        }
        
        //下面两个可以自己添加逻辑,当连接到网络或者断开连接,执行逻辑
        //网络连接
        public void OnNetConnected()
        {
            
        }
        
        //被服务器断开连接
        public void OnDisConnected()
        {
            
        }
        
        //接收数据
        public void Receive(int msgId, string message)
        {
            //接收到的消息放到队列
            m_MessageQueue.Enqueue(new KeyValuePair<int, string>(msgId, message));
        }
    
        private void Update()
        {
            if (m_MessageQueue.Count > 0)
            {
                KeyValuePair<int, string> msg = m_MessageQueue.Dequeue();
                ReceiveMessage?.Call(msg.Key, msg.Value);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    Manager中添加NetManager

    private static NetManager _net;
    public static NetManager Net
    {
        get { return _net; }
    }
    public void Awake()
    {
        _resource = this.gameObject.AddComponent<ResourceManager>();
        _lua = this.gameObject.AddComponent<LuaManager>();
        _ui = this.gameObject.AddComponent<UIManager>();
        _entity = this.gameObject.AddComponent<EntityManager>();
        _scene = this.gameObject.AddComponent<MySceneManager>();
        _sound = this.gameObject.AddComponent<SoundManager>();
        _event = this.gameObject.AddComponent<EventManager>();
        _pool = this.gameObject.AddComponent<PoolManager>();
        _net = this.gameObject.AddComponent<NetManager>();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    14-3. 网络客户端(Lua)

    • 功能模块化:消息注册、消息发送、消息接收
    • 模块管理器:模块初始化、模块获取、消息接收、消息发送

    不同的客户端都有类似的功能,需要在lua中将这些功能抽象出来,放进父类中。对于这么多模块,需要做一个模块管理器,将这些模块作为接口进行使用。同样这个模块管理器需要和C#进行交互

    function Class(super)--super是传进来的父类
        local class = nil;--实例都是table
        --构建实例时有父类,那么实例必然有super,如果实例没有父类,实例就有一个构造函数ctor
        if super then
            class = setmetatable({}, {__index = super})--设置传入的父类为元表
            class.super = super;
        else
            class = {ctor = function()  end}--如果没有父类传进来,就直接构造一个新的表,只有一个构造函数ctor
        end
        class.__index = class--__index指向自己
        
        --new的方法为了调用子类和父类的构造函数
        function class.new(...)
            local instance = setmetatable({}, class)
            local function create(inst, ...)
                --判断如果传进来的实例有没有父类,有父类就递归,知道没有父类了,用他自己的构造方法
                if type(inst.super) == "table" then
                    create(inst.super, ...);
                end
                --判断如果构造函数ctor是个方法,就用构造方法
                if type(inst.ctor) == "function" then
                    inst.ctor(instance, ...);
                end
            end
            create(instance, ...);
            return instance;
        end
        return class;
    end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    --这个是消息的父类
    local base_msg = Class();--使用Class()创建了一个类
    
    --消息注册方法,,,,添加请求和接收,,...是参数列表,需要向服务器发送的key,没有value
    function base_msg:add_req_res(msg_name, msg_id, ...)
        local keys = {...};
        
        --Class本质是个table,self[]是相当于给这个table加了一个键值
        --消息请求,,这个方法是发起request请求时调用的方法,传入一些参数
        self["req_"..msg_name] = function(self, ...)
            local values = {...};
            if #keys ~= #values then
                Log.Error("参数不正确:", msg_name);
            end
            local send_data = {};
            --keys values一一对应上,然后发送
            for i =1, #keys do
                send_data[keys[i]] = values[i];
            end
            msg_mgr.send_msg(msg_id, send_data);--manager发送消息
        end
        
        --消息接收的注册,,,,必须要先写进message接收模块,这里是直接判断,没有定义
        if type(self["res_".. msg_name]) == "function" then--如果定义了这个消息接收方法,就调用mgr注册进回调
            msg_mgr.register(msg_id,
                function(data)
                    local msg = Json.decode(data);--Json解析为table
                    --检查错误码
                    if msg.code ~= 0 then
                        Log.Error("错误码:", msg.code);
                        return;
                    end
                    self["res_"..msg_name](self, msg);--将信息传入这个消息接收方法(message注册的模块方法)
                end)
        else
            Log.Error("请注册消息返回回调:".. msg_name);--有请求一定有接收
        end
    end
    
    return base_msg;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    Json = require('rapidjson')
    Log = require('log')
    
    • 1
    • 2
    local msg_mgr = {}
    
    local msg_module_list = {} --模块的列表,存放所有模块
    local msg_responses = {} --接收消息的回调的列表
    
    --手动添加每个模块名字
    local msg_name_list =
    {
        --"msg_test",
    }
    
    --遍历一下模块名字列表,require后new一下,将实例存到模块列表中
    function msg_mgr.init()
        for k,v in pairs(msg_name_list) do
            msg_module_list[v] = require("message"..v).new()
        end
    end
    
    --获取消息,,通过传入模块名字,获取模块
    function msg_mgr.get_msg(key)
        if not msg_module_list[key] then
            Log.Error("脚本不存在:"..key)
            return
        end
        return msg_module_list[key]
    end
    
    --注册,也就是base_msg调用的register,收到消息时调用的回调方法
    function msg_mgr.register(msg_id, func)
        if msg_responses[msg_id] then
            Log.Error("消息已注册:"..msg_id)
            return
        end
        msg_responses[msg_id] = func;
    end
    
    --接收消息
    function ReceiveMessage(msg_id, message)--NetManager调用了,如果有消息传进来就调用
        Log.Info("receive:<<<<<<<<<<<<<<<<<<<<<<<<:id = "..msg_id.." : "..message.."");
        --如果定义了接收这个消息的方法
        if type(msg_responses[msg_id]) == "function" then
            msg_responses[msg_id](message)
        else
            Log.Error("此消息么有res: ", msg_id)
        end
    end
    
    --发送消息
    function msg_mgr.send_msg(msg_id, send_data)--base_msg调用,如果有消息请求,kv一致时,发送信息
        local str = Json.encode(send_data)
        Log.Info("receive:>>>>>>>>>>>>>>>>>>>>>>>>:id = "..msg_id.." : "..str.."");
        Manager.Net:SendMessage(msg_id, str)
    end
    
    return msg_mgr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    因为测试的时候,需要打日志,用lua打断点不方便,如果删掉日志,真机包看不见不方便,
    希望日志存在,正式环境不存在,通过一个开关控制,,,,因此Debug.log不行。需要自己封装打印日志的方法,并且可以输出打印lua的table的方法。

    local Log = {}
    
    local function read_table(tab,tab_count)
        local function get_symbol(count)
            local symol = "";
            for i = 1,count do
                symol = symol .. "    ";
            end
            return symol;
        end
        local symbol = get_symbol(tab_count);
        local str = "";
        for k,v in pairs(tab) do
            if type(v) == "table" then
                str = str .. symbol .. k .. ":\n" .. symbol .."{\n"..read_table(v,tab_count + 1)..symbol.."}\n";
            elseif type(v) == "userdata" then
                str = str ..symbol .. k ..  " = userdata,\n";
            elseif type(v) == "function" then
                str = str ..symbol .. k ..  " = function,\n";
            else
                str = str ..symbol .. k ..  " = " .. tostring(v)..",\n";
            end
        end
        return str;
    end
    
    
    local function get_log_string(...)
        local str = "";
        local pram = {...};
        for k,v in pairs(pram) do
            if type(v) == "table" then
                str = str .. "{\n".. read_table(v,1) .."}\n";
            elseif type(v) == "function" then
                str = str .. v ..  "function,\n";
            elseif type(v) == "userdata" then
                str = str .. "userdata,\n";
            else
                str = str .. tostring(v) .. "  ";
            end
        end
        return str;
    end
    
    
    function Log.Info(...)
        if not AppConst.OpenLog then
            return
        end
        CS.Log.Info(get_log_string(...));
    end
    
    function Log.Warning(...)
        if not AppConst.OpenLog then
            return
        end
        CS.Log.Warning(get_log_string(...));
    end
    
    function Log.Error(...)
        if not AppConst.OpenLog then
            return
        end
        local str = get_log_string(...);
        CS.Log.Error(str .. debug.traceback());
    end
    
    return Log;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    还需要去Unity中添加一下调用,因为C#中有时候也要调用,,创建Framework/Util/Log

    public static class Log
    {
        public static void Info(string msg)
        {
            if (!AppConst.OpenLog)
                return;
            Debug.Log(msg);
        }
    
        public static void Warning(string msg)
        {
            if (!AppConst.OpenLog)
                return;
            Debug.LogWarning(msg);
        }
    
        public static void Error(string msg)
        {
            if (!AppConst.OpenLog)
                return;
            Debug.LogError(msg);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    public static bool OpenLog = true;
    
    • 1
    public class GameStart : MonoBehaviour
    {
        public bool OpenLog;
    
        // Start is called before the first frame update
        void Start()
        {
            AppConst.OpenLog = this.OpenLog;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    14-4. 测试网络部分

    修改main.bytes开始调用网络接口。
    创建LuaScripts/message/msg_test.bytes脚本用于消息测试,同时msg_mgr中给msg_name_list添加模块名字

    local msg_name_list =
    {
        "msg_test",
    }
    
    • 1
    • 2
    • 3
    • 4
    Manager = CS.Manager --引用C#里面定义的类
    PathUtil = CS.PathUtil
    Vector3 = CS.UnityEngine.Vector3
    Input = CS.UnityEngine.Input
    KeyCode = CS.UnityEngine.KeyCode
    Time = CS.UnityEngine.Time
    AppConst = CS.AppConst
    Log = require('log')
    Json = require('rapidjson')
    require('class')
    base_msg = require('message.base_msg')
    msg_mgr = require('message.msg_mgr')
    
    --定义UI层级
    local ui_group = 
    {
        "Main",
        "UI",
        "Box",
    }
    
    local entity_group = 
    {
        "Player",
        "Monster",
        "Effect",
    }
    
    Manager.UI:SetUIGroup(ui_group)
    Manager.Entity:SetEntityGroup(entity_group)
    function Main()
        msg_mgr.init();--加载所有message模块
        Manager.Net:Init();--初始化TCP客户端
        Manager.Net:ConnectServer("127.0.0.1", 8000);--连接网络
        --连接网络ConnectServer调用的NetClient的OnConnectServer,设置端口
        --在调用OnConnect中GetStream和BeginRead获取网络数据流,读取到数据调用OnRead,OnRead中有ReceiveData解析数据
        --解析完继续BeginRead,获取后面的数据
        
        
        --print("hello main")
        Manager.UI:OpenUI("TestUI", "UI", "ui.TestUI")
        --Manager.Scene:LoadScene("Test01","scene.Scene01")
    end
    
    --Main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    image.png这是项目服务器
    protocl是数据类型
    修改TestUI

    btn_pooltest:OnClickSet(
          function()
                --Manager.UI:OpenUI("Login/LoginUI", "UI", "ui.TestUI");
                --发送消息请求
                --main.bytes用msg_mgr:Init后,根据msg_name_list就require('msg_test').new()给msg_module_list赋值,require('msg_test')先执行Class(base_msg)
                --Class(base_msg)返回一个class实例,此时msg_test = class实例,msg_test.super = base_msg,然后用require('msg_test').new()就是msg_test.new()
                --new的时候创建了一个instance表,元表是msg_test,然后开始调用create(instance,...),instance没有super去找msg_test.super,由于base_msg = Class()没有super父类,因此base_msg = {ctor = function()  end},不需要递归
                --执行第二个if,instance的ctor是msg_test的ctor,msg_test脚本中创建了ctor,因此执行了构造方法。
                --在ctor中执行add_req_res("first_test", 1000, "id", "user", "password", "listTest")
                --add_req_res方法设置好了msg_test的req_first_test请求方法,然后判断res_first_test的响应回调是否已经定义,只有定义了响应回调,收到消息的时候才能ReceiveMessage执行时,响应回调
                --运行最后面的req_first_test方法,方法内调用msg_mgr的send_msg,再调用Manager.Net:SendMessage向服务器发送消息
                msg_mgr.get_msg("msg_test"):req_first_test(99999, "zhe1123", "******", {1,3,5});
            end
      )
    
      btn_close:OnClickSet(
          function()
              --self:Close();
          end
      )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    FormRender使用场景及原理简介
    驱动LSM6DS3TR-C实现高效运动检测与数据采集(4)----上报匿名上位机实现可视化
    java实现获取钉钉的签到记录
    MachineLearning 13. 机器学习之降维方法UMAP及可视化 (umap)
    【探讨C++中的临时对象:一时之物还是永恒之道?】
    idea+git回退已经push到远端仓库的分支代码到某个历史版本
    Django ORM查询
    NoClassDefFoundError产生原因,及解决办法
    操作符详解(上) (C语言)
    11-网络篇-DNS步骤
  • 原文地址:https://blog.csdn.net/weixin_42264818/article/details/128211439