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')"> //网络协议Idpublic 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; }
} //网络协议Idpublic override int Id { get {return (int)Network.NetMsgID.CSLogin;} } //回到引用池,变量设置初始化。如果是引用型成员变量也要回到引用池public override void Clear(){_account = "" ;_password = "" ;}}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Splashtop正式入驻长三角(杭州)制造业数字化能力中心,赋能企业向数字化转型

2024年6月&#xff0c;Splashtop正式入驻长三角&#xff08;杭州&#xff09;制造业数字化能力中心。作为全球领先的远程桌面控制软件供应商&#xff0c;Splashtop致力于提供适用于远程办公、IT 和 MSP 远程支持等多场景的高性能远程桌面控制软件和解决方案&#xff0c;赋能企业…

Qt之QGraphicsView —— 笔记3:矩形图元连接(附完整源码)

效果 完整源码 注意:在ui文件中拖入一个QGraphicsView类窗口控件,然后用MyGraphicsView提升该类。 main.cpp #include "widget.h" #include <QApplication>int main(

SpringBoot+Vue学生宿舍管理系统(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 角色对应功能 学生宿管员管理员 功能截图

基于非下采样小波包分析的滚动轴承故障诊断(MATLAB R2021B)

小波变换具有良好的时频局部化特性和多分辨率特性&#xff0c;可准确定位信号的突变点并可在不同尺度上描述信号的局部细节特征&#xff0c;被广泛应用于信号降噪。但标准正交小波变换不具有平移不变性&#xff0c;采用标准正交小波对信号消噪后&#xff0c;会在脉冲尖峰处产生…

Polar Web【简单】PHP反序列化初试

Polar Web【简单】PHP反序列化初试 Contents Polar Web【简单】PHP反序列化初试思路EXP手动脚本PythonGo 运行&总结 思路 启动环境&#xff0c;显示下图中的PHP代码&#xff0c;于是展开分析&#xff1a; 首先发现Easy类中有魔术函数 __wakeup() &#xff0c;实现的是对成员…

VisionPro的应用和入门教程

第1章 关于VisionPro 1.1 康耐视的核心技术 1. 先进的视觉系统 康耐视的视觉系统结合了高性能的图像传感器、复杂的算法和强大的计算能力&#xff0c;能够实时捕捉、分析和处理高分辨率图像。其视觉系统包括固定式和手持式两种&#xff0c;适用于各种工业环境。无论是精密电…

[经验] 场效应管是如何发挥作用的 #知识分享#学习方法#职场发展

场效应管是如何发挥作用的 在现代电子技术领域&#xff0c;场效应管&#xff08;MOSFET&#xff09;是一种重要的半导体元器件。它的作用非常广泛&#xff0c;例如在集成电路中扮演着关键的角色。在本文中&#xff0c;我们将详细探讨场效应管的作用及其在实际应用中的意义。 简…

SpringBoot+Vue企业客户管理系统(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 角色对应功能 员工管理员 功能截图

git根据历史某次提交创建新分支

有时候项目在做版本管理的时候&#xff0c;忘记了创建某次版本的分支&#xff0c;而直接在主分支上进行开发了&#xff0c;这个时候&#xff0c;想要对某次提交单独拉出来一个版本分支&#xff0c;就需要用到这个功能&#xff1a; git checkout -b 新分支名 某次提交的id 找到…

LLM技术

LLM 是利用深度学习和大数据训练的人工智能系统&#xff0c;专门设计来理解、生成和回应自然语言。这些模型通过分析大量的文本数据来学习语言的结构和用法&#xff0c;从而能够执行各种语言相关任务。以 GPT 系列为代表&#xff0c;LLM 以其在自然语言处理领域的卓越表现&…

53.ReentrantLock原理

ReentrantLock使用 ReentrantLock 实现了Lock接口&#xff0c; 内置了Sync同步器继承了AbstractQueuedSynchronizer。 Sync是抽象类&#xff0c;有两个实现NonfairSync非公平&#xff0c;FairSync公平。 所以ReentrantLock有公平锁和非公平锁。默认是非公平锁。 public sta…

线性预测器的等价性

摘要 尽管线性模型很简单&#xff0c;但它在时间序列预测中表现良好&#xff0c;即使是在与更深入、更昂贵的模型竞争时也是如此。已经提出了许多线性模型的变体&#xff0c;通常包括某种形式的特征归一化&#xff0c;以提高模型的泛化。本文分析了用这些线性模型体系结构可表…

Mysql使用中的性能优化——索引数对插入操作性能的影响

表的索引可以给数据检索提升效率&#xff0c;但是也给表的增删改操作带来代价。本文我们将关注&#xff0c;索引数量对INSERT操作的影响。 结论 索引数的新增会造成INSERT操作效率下降&#xff0c;约每增一个索引会降低10%效率。 实验数据 可以看到0个索引的效率是7个索引效…

【C++题解】1265. 爱因斯坦的数学题

问题&#xff1a;1265. 爱因斯坦的数学题 类型&#xff1a;简单循环 题目描述&#xff1a; 爱因斯坦出了一道这样的数学题&#xff1a;有一条长阶梯&#xff0c;若每步跨 2 阶&#xff0c;则最最后剩一阶&#xff0c;若每步跨 3 阶&#xff0c;则最后剩 2 阶&#xff0c;若每…

分布式事务大揭秘:使用MQ实现最终一致性

本文作者:小米,一个热爱技术分享的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! 大家好,我是小米,一个热爱分享技术的29岁程序员,今天我们来聊聊分布式事务中的一种经典实现方式——MQ最终一致性。这是一个在互联网公司中广…

Wow Tab插件,一款能让你的Edge浏览器开挂的插件,微软官方出品

首先问你个问题&#xff0c;你的浏览器起始页是什么样的界面&#xff1f;是默认的界面还是极简的界面&#xff1f;又或者是既简洁又功能丰富的新型起始页呢&#xff1f;如果你的起始页是浏览器默认的&#xff0c;从来都没有更改过的话&#xff0c;建议你可以尝试一些第三方的起…

6.切蛋糕

上海市计算机学会竞赛平台 | YACSYACS 是由上海市计算机学会于2019年发起的活动,旨在激发青少年对学习人工智能与算法设计的热情与兴趣,提升青少年科学素养,引导青少年投身创新发现和科研实践活动。https://www.iai.sh.cn/problem/71 题目描述 一个圆型的蛋糕,切 𝑛n 刀…

【C++第九课 - vector】vector介绍、vector使用,vector的底层实现、杨辉三角、全排列、只出现一次的数字

目录 一、vector的介绍二、vector的使用1、vector的构造函数2、vector的插入和三种遍历方式3、开空间4、insert5、find6、erase补充 三、vector的底层实现1、成员变量2、构造函数3、push_back4、访问方式5、pop_back6、insert - pos位置插入x7、resize8、拷贝构造9、赋值10、er…

工具:Linux如何挂载NTFS移动硬盘

从windows平台迁移数据至Linux平台&#xff0c;有时候会用到NTFS文件系统的硬盘&#xff0c;但Linux的file system一般又无法直接兼容NTFS系统。这个就需要用到ntfs-3g插件。 NTFS-3G是一个开源项目&#xff0c; NTFS-3G是为Linux, Android, Mac OS X, FreeBSD, NetBSD, OpenSo…

玩转Matlab-Simscape(初级)- 09 - 在Simulink中创建曲柄滑块机构的控制模型

** 玩转Matlab-Simscape&#xff08;初级&#xff09;- 09 - 在Simulink中创建曲柄滑块机构的控制模型 ** 目录 玩转Matlab-Simscape&#xff08;初级&#xff09;- 09 - 在Simulink中创建曲柄滑块机构的控制模型 前言一、问题描述二、创建模型2.1 识别机构中的刚体2.2 确定刚…