unity3d:GameFramework+xLua+Protobuf+lua-protobuf,生成.cs,.pb工具流

概述

1.区分lua,cs用的proto
2.proto生成cs,使用protogen.exe,通过csharp.xslt修改生成cs样式
3.proto生成lua加载.pb二进制文件,并生成.pb列表文件,用于初始化加载
4.协议id生成cs,lua中枚举

区分cs,lua用proto

cs中序列化使用基于CSPacketBase,SCPacketBase的子类
lua中序列化使用lua-protobuf,需要提前把pb二进制文件加载
cs,lua中不通用协议类型,即某个协议类型只能在cs或者lua的一侧使用
使用两个文件夹区分,cs用的.proto放CS,lua用.proto放Lua文件夹下,在生成工具中分别处理

协议id生成cs,lua中

在NetMsgID.txt中填写所有lua,cs用的协议id(不区分lua用,还是cs用),例如

CSLogin = 100,
SCLogin = 101,
CSPlayerInfo = 102,
SCPlayerInfo = 103,
CSSelectCharacter = 104,
SCSelectCharacter = 105,

生成到对应的模板中,NetMsgIDTmpCS.cs

namespace Network
{
/// <summary>
/// 网络协议ID
/// </summary>
public enum NetMsgID
{
__Content__
}

}

NetMsgIDTmpLua.lua

--网络协议ID
NetMsgID = 
{
__Content__
}

替换__Content__,再复制到工程目录中

static void GenMsgID()
{
    string sMsgContent = File.ReadAllText(m_msgIDFullName);
    string sMsgCS = File.ReadAllText(m_msgIDTmpCSFullName);
    string sMsgLua = File.ReadAllText(m_msgIDTmpLuaFullName);
    sMsgCS = sMsgCS.Replace("__Content__", sMsgContent); 
    File.WriteAllText(m_msgIDCSFullName,sMsgCS);

    sMsgLua = sMsgLua.Replace("__Content__", sMsgContent);
    sMsgLua = sMsgLua.Replace("//", "--");
    File.WriteAllText(m_msgIDLuaFullName, sMsgLua);
}

proto生成cs

使用protogen.exe把.proto生成.cs文件

.net控制台遍历文件夹生成cs

protogen.exe单独使用如下,运行命令行,cd到protogen.exe的盘符,再cd 到protogen.exe的根目录下
把Person.proto放到protogen.exe的同级目录下。

protogen -i:Person.proto -o:Person.cs 

编写.net控制台程序执行
启动cmd并cd到protogen.exe根目录

using (Process p = new Process())
{
    //启动cmd,并设置好相关属性。
    p.StartInfo.FileName = "cmd.exe";
    p.StartInfo.UseShellExecute = false;    //是否使用操作系统shell启动
    p.StartInfo.RedirectStandardInput = true;//接受来自调用程序的输入信息
    p.StartInfo.RedirectStandardOutput = true;//由调用程序获取输出信息
    p.StartInfo.RedirectStandardError = true;//重定向标准错误输出
    p.StartInfo.CreateNoWindow = true;//不显示程序窗口
    p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    p.Start();

    //cmd要到protogen.exe的根目录
    string[] arrDiskName = Directory.GetCurrentDirectory().Split(':');
    string diskName = arrDiskName[0] + ":";
    p.StandardInput.WriteLine(diskName);

    string cdPath = "cd " + m_protoRootPath;
    p.StandardInput.WriteLine(cdPath);

遍历proto/CS目录下proto文件生成到工程目录

//遍历所有cs文件下proto
var files = new DirectoryInfo(m_protoPathCS).GetFiles("*.proto", SearchOption.AllDirectories);
foreach (var f in files)
{
    string fileName = f.Name.Replace(".proto", "");
    //生成cs
    {
        string sIn = " -i:proto/CS/" + f.Name;
        string sOut = " -o:" + m_csPath  + fileName + ".cs";
        string sCmd = protogenPath + sIn + sOut;
        p.StandardInput.WriteLine(sCmd);
    }
}

csharp.xslt修改输出cs样式

XSL 指扩展样式表语言(EXtensible Stylesheet Language), 它是一个 XML 文档的样式表语言。
XSLT 指 XSL 转换
通过 XSLT,您可以向输出文件添加元素和属性,或从输出文件移除元素和属性。

在csharp.xslt中载入自定义.xslt

  <xsl:import href="custom.xslt"/>

修改基类名

csharp.xslt中增加自定义函数getBaseClassName

public partial class <xsl:call-template name="pascal"/> <xsl:call-template name="getBaseClassName"> </xsl:call-template> 

custom.xslt中实现getBaseClassName

 <!-- 定义获取基类名方法 -->
  <xsl:template name="getBaseClassName">
 	 <xsl:variable name = "className" ><xsl:call-template name="pascal"/></xsl:variable>
     <xsl:choose >
     	<xsl:when test="starts-with($className,'SC')"> : SCPacketBase</xsl:when>
     	<xsl:when test="starts-with($className,'CS')"> : CSPacketBase</xsl:when>
     	<xsl:otherwise> 
		</xsl:otherwise>
     </xsl:choose>
  </xsl:template>

如果SC开头的类,增加基类为SCPacketBase,服务器给客户端包
如果SC开头的类,增加基类为CSPacketBase,客户端给服务器包

增加Clear函数

csharp.xslt中增加自定义函数methodClear

<xsl:call-template name = "methodClear" ></xsl:call-template>

custom.xslt中实现methodClear

<!-- 是否显示Clear方法模版 -->
  <xsl:template name="methodClear">
 	 <xsl:variable name = "className" ><xsl:call-template name="pascal"/></xsl:variable>
     <xsl:choose>
     	<xsl:when test="starts-with($className,'SC') or starts-with($className,'CS')"> 
	//网络协议Id
	public override int Id 
	{ 
		get {return (int)Network.NetMsgID.<xsl:value-of select="$className"/>;} 
	} 

	//回到引用池,变量设置初始化。如果是引用型成员变量也要回到引用池
	public override void Clear()
	{
//<xsl:value-of select="$className"/>Clear
	}
     	</xsl:when>
     	<xsl:otherwise>
	//回到引用池,变量设置初始化。如果是引用型成员变量也要回到引用池
	public override void Clear()
	{
//<xsl:value-of select="$className"/>Clear
	}		
		</xsl:otherwise>
     </xsl:choose>
  </xsl:template>

协议类CS,SC开头类,子结构类都是基于引用池,需要实现Clear(),作用是回到引用池时,需要把变量置为初始值,这里先写入注释//className,等待cs进入unity工程时,通过正则再进一步处理
SC,CS协议类需要实现协议ID,这里对应NetMsgID.txt一一对应,例如协议类名为CSLogin,那么NetMsgID.txt有条内容为CSLogin = 100

正则表达式填充Clear中类成员设置默认值

上一步生成的Clear内容为

	public override void Clear()
	{
//CSLgoin
	}

需要对上一步生成Clear函数内填充内容,把类中成员设置为默认值,例如CSLogin填充为

	public override void Clear()
	{
		_account = "" ;
		_password = "" ;

	}

用正则匹配文本规则添加
1.遍历所有packet.cs文件
2.一个packet.cs文件中匹配类名@"public partial class (\w+) : ",一个可能包含1至N个Class。例如包含ClassA,ClassB
3.提取文件中类名开始到Clear结尾时一个类的部分,例如ClassA

    public static string GetClassContent(string fileContent,string className)
    {
        string classContent = "";
        string pattern = "(?s)public partial class " + className + "(.*?)" + className + "Clear";//(?s):这是一个正则表达式的选项,称为“单行”模式(single-line mode),它使 . 匹配所有字符,包括换行符。
        Debug.Log(pattern);
        // // 创建正则表达式对象,使用 RegexOptions.Multiline 选项
        Regex regex = new Regex(pattern, RegexOptions.Multiline);

        // 执行匹配
        Match match = regex.Match(fileContent);

        if (match.Success)
        {
            classContent = match.Groups[1].Value;
            Debug.Log(match.Groups[1].Value);
        }

        return classContent;
    }

4.ClassA中获取private变量,进行取的类型,默认值进行替换

    public static string GetClassMemberClear(string classContent)
    {
        StringBuilder sbClear = new StringBuilder();
        string pattern = "private (.*?);";
        Regex regex = new Regex(pattern);

        // 执行匹配
        MatchCollection matches = regex.Matches(classContent);

        // 遍历所有匹配项
        foreach (Match match in matches)
        {
            // 检查是否有成功的匹配
            if (match.Success)
            {
                // 提取匹配的类名(捕获组1)
                string sMember = match.Groups[1].Value;
                Debug.Log("找到成员:" + sMember);  //string _account = ""
                
                string[] arrMember = sMember.Split(' ');
                string sType = arrMember[0];
                string sVariant = arrMember[1];
                //非引用池类型
                string clear = "";
                for (int i = 1; i < arrMember.Length; i++)
                {
                    clear += arrMember[i];
                    clear += " ";
                }

                clear = clear + ";" + "\n";
                sbClear.Append("		" + clear);
            }
        }
        string sClear = sbClear.ToString();
        Debug.Log($"Clear中{sClear}");
        return sClear;
        
    }

注意,SC,CS类已经是引用池类,类中间不能再嵌套引用池为类成员

可以使用unity监听导入资源,对上一步产生的CS类再加工处理

    private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        if (m_isEnable == false)
        {
            //防止一边导入,一边处理又一边导入了
            return;
        }
        m_isEnable = false;
        for (int i = 0; i < importedAssets.Length; i++)
        {
            string path = importedAssets[i];
            if (path.StartsWith("Assets/GameMain/Scripts/Network/Protobuf/ProtoCS"))
            {
                //对生成cs类,处理clear函数
                PacketGenClear.GenClear2ProtoCS(path);
            }
        }
        m_isEnable = true;
    }

Protoc生成pb二进制文件

使用protoc.exe把.proto生成.pb二进制文件,用于lua中加载
基本使用

protoc -o addressbook.pb addressbook.proto

遍历文件夹生成.pd到工程目录

var luaProtoFiles = new DirectoryInfo(m_protoPathLua).GetFiles("*.proto", SearchOption.AllDirectories);
//遍历所有luaproto
foreach (var f in luaProtoFiles)
{
    string fileName = f.Name.Replace(".proto", "");
    //生成pb
    {
        string sIn = " proto/Lua/" + f.Name;
        string spbFullName = m_pbPath + fileName + ".pb";
        string sOut = " -o " + spbFullName;
        string sCmd = protocPath + sOut + sIn;
        p.StandardInput.WriteLine(sCmd);
    }
    m_sbProtoList.AppendLine(fileName);
}

生成pd列表文件

File.WriteAllText(m_protoListFullName, m_sbProtoList.ToString());

执行效果

在这里插入图片描述

生成cs类

[global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"CSLogin")]
public partial class CSLogin : CSPacketBase
{
public CSLogin() {}


private string _account = "";
[global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=@"account", DataFormat = global::ProtoBuf.DataFormat.Default)]
[global::System.ComponentModel.DefaultValue("")]
public string account
{
  get { return _account; }
  set { _account = value; }
}

private string _password = "";
[global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"password", DataFormat = global::ProtoBuf.DataFormat.Default)]
[global::System.ComponentModel.DefaultValue("")]
public string password
{
  get { return _password; }
  set { _password = value; }
} 
	//网络协议Id
	public override int Id 
	{ 
		get {return (int)Network.NetMsgID.CSLogin;} 
	} 

	//回到引用池,变量设置初始化。如果是引用型成员变量也要回到引用池
	public override void Clear()
	{
		_account = "" ;
		_password = "" ;

	}
 	
}

生成CS,pd流程图
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/691137.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

107.网络游戏逆向分析与漏洞攻防-装备系统数据分析-装备信息更新的处理

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 现在的代码都是依据数据包来写的&#xff0c;如果看不懂代码&#xff0c;就说明没看懂数据包…

DevOps在数字化转型中的作用——实现数字化可视性

DevOps 的出现是为了满足不断增长的市场和消费者对技术应用程序的需求。它旨在在不牺牲软件质量的情况下创建更快的开发环境。DevOps 还专注于在快速开发生命周期中提高软件的整体质量。它依赖于多种技术、平台和工具的组合来实现所有这些目标。 容器化是一项彻底改变了我们开发…

PostgreSQL基础(十):PostgreSQL的并发问题

文章目录 PostgreSQL的并发问题 一、事务的隔离级别 二、MVCC PostgreSQL的并发问题 一、事务的隔离级别 在不考虑隔离性的前提下&#xff0c;事务的并发可能会出现的问题&#xff1a; 脏读&#xff1a;读到了其他事务未提交的数据。&#xff08;必须避免这种情况&#xf…

所谓自律,就是去对抗那些廉价的快乐

所谓自律&#xff0c;就是去对抗那些廉价的快乐 以下文章来源于洞见 &#xff0c;作者洞见 导语 打败内心那只及时享乐的猴子。 董宇辉说过这样一句话&#xff1a;“廉价的快乐是直接给你想要的东西&#xff0c;高等的快乐则会给你设置重重阻碍。” 廉价的快乐&#xff0c;就…

Hack The Box-BoardLight

主机详情 hack the box&#xff1a; 端口扫描&#xff1a; 服务扫描&#xff1a; 对服务的漏洞查找&#xff1a; 基本一无所获&#xff0c;&#xff0c;找到个 2.4.49 的远程命令执行&#xff0c;尝试使用一下 不出意外的不能使用&#xff0c;&#xff0c; web页面&#xff1…

05--Git分布式版本控制系统

前言&#xff1a;给后端工程师使用的版本控制器&#xff0c;本质上类似带时间标记的ftp&#xff0c;使用比较简单&#xff0c;就在这里归纳出来&#xff0c;供参考学习。 git1、概念简介 分布式版本控制系统&#xff08;Distributed Version Control System&#xff0c;DVCS&…

Python cProfile 输出解析及其解决方案

cProfile 是 Python 中用于性能分析的内置模块&#xff0c;它可以帮助你确定程序中哪些部分消耗了最多的时间。通常&#xff0c;使用 cProfile 会输出大量的数据&#xff0c;需要进行解析和分析。下面是关于 cProfile 输出解析及其解决方案的一些提示&#xff1a; 1、问题背景 …

2024-06-08 Unity 编辑器开发之编辑器拓展9 —— EditorUtility

文章目录 1 准备工作2 提示窗口2.1 双键窗口2.2 三键窗口2.3 进度条窗口 3 文件面板3.1 存储文件3.2 选择文件夹3.3 打开文件3.4 打开文件夹 4 其他内容4.1 压缩纹理4.2 查找对象依赖项 1 准备工作 ​ 创建脚本 “Lesson38Window.cs” 脚本&#xff0c;并将其放在 Editor 文件…

力扣经典面试题-旋转链表(Java)

1.题目描述&#xff1a;给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3] 示例 2&#xff1a; 输入&#xff1a;head [0,1,2], k …

壁纸动态-Mac电脑-4K超高清[po破]动态壁纸[解]Dynamic WallPaper 安装使用教程

Mac分享吧 文章目录 效果一、准备工作二、开始安装1、双击运行软件&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功 三、运行调试1、打开软件&#xff0c;选择自己喜欢的壁纸2、调整设置&#xff0c;使多个壁…

消息队列笔记

异步技术 企业级应用中广泛使用的三种异步消息传递技术 原文链接&#xff1a;https://blog.csdn.net/qq_55917018/article/details/122122218 三种异步消息传递技术 JMS (java message service) 一个Java规范&#xff0c;等同于JDBC规范&#xff0c;提供了与消息服务相关的…

语法分析!!!

一、实验题目 根据给定文法编写调试预测分析程序&#xff0c;对任意输入串用预测分析法进行语法分析。 二、实验目的 加深对预测分析法的理解。 三、实验内容 四、实验代码 #include <iostream> #include <stdio.h> #include <string> #include <…

elasticsearch hanlp插件自定义分词配置(停用词)

[Toc](elasticsearch hanlp插件自定义分词配置(停用词)) 既然可以自定义关键词&#xff0c;那么自然也是可以自定义停用词的。 背景 由于在使用elasticsearch hanlp(以下简称es hanlp)的过程中&#xff0c;分词会出现一些无用的词&#xff0c;比如各种标点符号或者没有意义的…

二叉排序树--c++

【相关知识】 二叉排序树&#xff08;也称二叉查找树&#xff09;&#xff1a;或者是一棵空的二叉树&#xff0c;或者是具有下列性质的二叉树&#xff1a; ⑴ 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于根结点的值&#xff1b; ⑵ 若它的右子树不空&#xff0c…

vivado HW_BITSTREAM、HW_CFGMEM

HW_比特流 描述 从比特流文件创建的硬件比特流对象hw_bitstream&#xff0c;用于关联 在Vivado的硬件管理器功能中使用硬件设备对象hw_device 设计套件。 比特流文件是从具有write_bitstream的放置和路由设计创建的 命令硬件位流对象是使用 create_hw_bitstream命令&#xff0c…

【Vue】vuex 的使用 - 创建仓库

通用的地方我们一般会称之为仓库 1.安装 vuex 安装vuex与vue-router类似&#xff0c;vuex是一个独立存在的插件&#xff0c;如果脚手架初始化没有选 vuex&#xff0c;就需要额外安装。 yarn add vuex3 或者 npm i vuex32.新建 store/index.js 专门存放 vuex ​ 为了维护项目…

vue2中如何使用函数式组件

vue2 中如何使用函数式组件 用 render 定义函数式组件如何处理 props如何在函数式组件中触发自定义事件&#xff1f;injection如何使用 computed 和 methods定义一个函数式组件的 MyButton函数式组件有何优势哪种场景适合使用函数式组件函数式组件的问题参考 函数式组件&#x…

MySQL-相关日志

官方文档 1、MySQL支持的日志 MySQL有不同类型日志文件&#xff0c;用来存储不同类型的日志&#xff0c;分别为 二进制日志、错误日志、通用查询日志、慢查询日志、中继日志、数据定义语句日志 慢查询日志&#xff1a;记录所有执行时间超过 long_query_time的所有查询&#xf…

【 技术栈】技术方案到底怎么写?

文章目录 一、背景二、技术方案重要性三、常见的技术方案有哪些内容1、系统用例2、功能整体链路2.1、核心业务流程 3、数据库设计4、接口设计5、非功能设计5.1、性能与稳定性5.2、监控 7、系统风险点评估 四、总结 一、背景 工作中&#xff0c;有一些需求或者技术改造&#xf…

前端开发高频面试题

好的&#xff0c;以下是对您提出的问题的详细回答&#xff1a; 说说vue动态权限绑定渲染列表&#xff08;权限列表渲染&#xff09; Vue中动态权限绑定渲染列表通常涉及以下步骤&#xff1a; 首先&#xff0c;通过API请求从服务器获取当前用户的权限数据。在Vue组件中&#xff…