协议(消息)生成
目录
协议(消息)生成主要做什么?
知识点二 制作功能前的准备工作
编辑编辑 制作消息生成功能
实现效果
总结
上一篇中配置的XML文件可见:
https://mpbeta.csdn.net/mp_blog/creation/editor/147647176
协议(消息)生成主要做什么?
//协议生成 主要是使用配置文件中读取出来的信息
//动态的生成对应语言的代码文件
//每次添加消息或者数据结构类时,我们不需要再手写代码了
//我们不仅可以生成C#脚本文件,还可以根据需求生成别的语言的文件
#endregion
知识点二 制作功能前的准备工作
//协议生成是不会在发布后使用的功能,主要是在开发时使用
//所以我们在Unity当中可以把它作为一个编辑器功能来做
//因此我们可以专门新建一个Editor文件夹 (专门放编辑器相关内容,不会发布)
//在其中放置配置文件、自动生成相关脚本文件
Unity编辑器功能代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Xml;public class ProtocolTool
{private static string PROTO_PATH = Application.dataPath + "/Editor/protocolTool/protocolInfo.xml";private static GeneratreCSharp generatreCSharp = new GeneratreCSharp();[MenuItem("ProtocolTool/生成C#脚本")]private static void GenerateCSharp(){//1.读取xml相关信息//XmlNodeList list = GetNodes("enum");//2.根据这些信息,去拼接字符串,生成对应的脚本generatreCSharp.GeneratreEnum(GetNodes("enum"));//刷新编辑器页面 让我们可以看到生成的内容 不需要手动刷新了//生成数据结构类generatreCSharp.GeneratreData(GetNodes("data"));//生成消息类generatreCSharp.GeneratreMsg(GetNodes("message"));AssetDatabase.Refresh();}[MenuItem("ProtocolTool/生成C++脚本")]private static void GenerateCpp(){Debug.Log("生成C++脚本");}[MenuItem("ProtocolTool/生成Java脚本")]private static void GenerateJava(){Debug.Log("生成Java脚本");}private static XmlNodeList GetNodes(string nodeName){XmlDocument xml = new XmlDocument();xml.Load(PROTO_PATH);XmlNode root= xml.SelectSingleNode("messages");return root.SelectNodes(nodeName);}
}


制作消息生成功能
根据配置好的XML文件来制作消息生成工具,分别实现生成枚举类型,数据结构类型(其中包括GetBytesNum,Writing,Reading函数),消息类型脚本
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using UnityEngine;public class GeneratreCSharp
{//协议保存路径private string SAVE_PATH = Application.dataPath + "/Scripts/Protocol/";//生成枚举脚本的逻辑public void GeneratreEnum(XmlNodeList nodes){string namespaceStr = "";string enumNameStr = "";string fieldStr = "";foreach (XmlNode enumNodes in nodes){//获取命名空间配置信息namespaceStr = enumNodes.Attributes["namespace"].Value;//获取枚举名配置信息enumNameStr = enumNodes.Attributes["name"].Value;//获取所有字段节点 然后进行字符串拼接XmlNodeList enumFields = enumNodes.SelectNodes("field");//一个新的枚举 需要清空一次上一次拼接的字段字符串fieldStr = "";foreach (XmlNode enumField in enumFields){fieldStr += "\t\t" + enumField.Attributes["name"].Value;if (enumField.InnerText != "")fieldStr +="="+ enumField.InnerText;fieldStr += ",\r\n";}//对所有可变的内容进行拼接string enumStr = $"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic enum {enumNameStr}\r\n" +"\t{\r\n" +$"{fieldStr}" +"\t}\r\n" +"}";//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Enum/";//如果不存在这个文件夹 则创建if (!Directory.Exists(path))Directory.CreateDirectory(path);//字符串保存 存储为枚举脚本文件File.WriteAllText(path + enumNameStr + ".cs", enumStr);}Debug.Log("枚举生成结束");}//生成数据结构类的逻辑public void GeneratreData(XmlNodeList nodes){string namespaceStr = "";string classNameStr = "";string fieldStr = "";string GetBytesNumStr = "";string WritingStr = "";string ReadingStr = "";foreach (XmlNode dataNode in nodes){//命名空间namespaceStr = dataNode.Attributes["namespace"].Value;//类名classNameStr = dataNode.Attributes["name"].Value;//读取所有字段节点XmlNodeList fields = dataNode.SelectNodes("field");//通过这个方法进行成员变量声明的拼接 返回拼接结果fieldStr = GetFieldStr(fields);//通过这个方法对GetBytesNum函数中字符串内容进行拼接 返回结果GetBytesNumStr = GetGetBytesNumStr(fields);//通过这个方法对Writing函数中字符串内容进行拼接 返回结果WritingStr = GetWritingStr(fields);//通过这个方法对Reading函数中字符串内容进行拼接 返回结果ReadingStr = GetReadingStr(fields);string dataStr = "using System;\r\n"+"using System.Collections.Generic;\r\n" +"using System.Text;\r\n"+$"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic class {classNameStr} : BaseData\r\n" +"\t{\r\n" +$"{fieldStr}" +"\t\tpublic override int GetBytesNum()\r\n" +"\t\t{\r\n" +"\t\t\tint num=0;\r\n" +$"{GetBytesNumStr}" +"\t\t\treturn num;\r\n"+"\t\t}\r\n"+"\t\tpublic override byte[] Writing()\r\n"+"\t\t{\r\n"+"\t\t\tint index =0;\r\n"+"\t\t\tbyte [] bytes =new byte[GetBytesNum()];\r\n"+$"{WritingStr}"+"\t\t\treturn bytes;\r\n"+"\t\t}\r\n"+"\t\tpublic override int Reading(byte[] bytes, int beginIndex = 0)\r\n" +"\t\t{\r\n" +"\t\t\tint index =beginIndex ;\r\n" +$"{ReadingStr}" +"\t\t\treturn index - beginIndex;\r\n" +"\t\t}\r\n" +"\t}\r\n" +"}";//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Data/";//如果不存在这个文件夹 则创建if (!Directory.Exists(path))Directory.CreateDirectory(path);//字符串保存 存储为枚举脚本文件File.WriteAllText(path + classNameStr + ".cs", dataStr);}Debug.Log("数据结构类生成结束");}private string GetFieldStr(XmlNodeList fields){string fieldStr = "";foreach (XmlNode field in fields){//变量类型string type = field.Attributes["type"].Value;//变量名string fieldName = field.Attributes["name"].Value;if(type=="list"){string T = field.Attributes["T"].Value;fieldStr += "\t\tpublic List<" + T + ">";}else if(type =="array"){string data = field.Attributes["data"].Value;fieldStr += "\t\tpublic " + data + "[]";}else if(type=="dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;fieldStr += "\t\tpublic Dictionary<" + Tkey + "," + Tvalue + ">";}else if(type=="enum"){string data = field.Attributes["data"].Value;fieldStr += "\tpublic " + data + " ";}else{fieldStr += "\t\tpublic " + type + " ";}fieldStr += fieldName + ";\r\n";}return fieldStr;}private string GetGetBytesNumStr(XmlNodeList fields){string bytesNumStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;bytesNumStr += "\t\t\tnum +=2;\r\n";//+2是为了节约字节数 用一个short去存储信息bytesNumStr += "\t\t\tfor(int i = 0;i< " + name + ".Count;i++)\r\n";//这里使用name+"[i]"的目的是获取list当中的元素传入使用bytesNumStr += "\t\t\t\tnum +=" + GetValueBytesNum(T, name + "[i]") + ";\r\n";}else if (type == "array"){string data = field.Attributes["data"].Value;bytesNumStr += "\t\t\tnum +=2;\r\n";//+2是为了节约字节数 用一个short去存储信息bytesNumStr += "\t\t\tfor(int i = 0;i<" + name + ".Length;i++)\r\n";//这里使用name+"[i]"的目的是获取list当中的元素传入使用bytesNumStr += "\t\t\t\tnum +=" + GetValueBytesNum(data, name + "[i]") + ";\r\n";}else if (type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalude = field.Attributes["Tvalue"].Value;bytesNumStr += "\t\t\tnum +=2;\r\n";//+2是为了节约字节数 用一个short去存储信息bytesNumStr += "\t\t\tforeach(" + Tkey + " key in " + name + ".Keys)\r\n";bytesNumStr += "\t\t\t{\r\n";bytesNumStr += "\t\t\t\tnum +=" + GetValueBytesNum(Tkey, "key") + ";\r\n";bytesNumStr += "\t\t\t\tnum +=" + GetValueBytesNum(Tvalude, name + "[key]") + ";\r\n";bytesNumStr += "\t\t\t}\r\n";}elsebytesNumStr += "\t\t\tnum +=" + GetValueBytesNum(type, name)+";\r\n";}return bytesNumStr;}private string GetValueBytesNum(string type,string name){switch (type){case "int":case "float":case "enum":return "4";case "long":return "8";case "byte":case "bool":return "1";case "short":return "2";case "string":return "4+Encoding.UTF8.GetByteCount(" + name + ")";default:return name + ".GetBytesNum()";}}private string GetWritingStr(XmlNodeList fields){string writingStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if(type=="list"){string T = field.Attributes["T"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Count, ref index);\r\n";writingStr += "\t\t\tfor (int i=0; i < " + name + ".Count;++i)\r\n";writingStr += "\t\t\t\t" + GetWritingValueStr(T, name + "[i]") + "\r\n";}else if(type=="array"){string data = field.Attributes["data"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Length, ref index);\r\n";writingStr += "\t\t\tfor (int i=0; i < " + name + ".Length;++i)\r\n";writingStr += "\t\t\t\t" + GetWritingValueStr(data, name + "[i]") + "\r\n";}else if(type=="dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;writingStr += "\t\t\tWriteShort(bytes, (short)" + name + ".Count, ref index);\r\n";writingStr += "\t\t\tforeach(" + Tkey + " key in " + name + ".Keys)\r\n";writingStr += "\t\t\t{\r\n";writingStr += "\t\t\t\t" + GetWritingValueStr(Tkey, "key") + "\r\n";writingStr += "\t\t\t\t" + GetWritingValueStr(Tvalue, name + "[key]") + "\r\n";writingStr += "\t\t\t}\r\n";}else{writingStr += "\t\t\t" + GetWritingValueStr(type, name) + "\r\n";}}return writingStr;}private string GetWritingValueStr(string type,string name){switch (type){case "byte":return "WriteByte(bytes ," + name + ", ref index);";case "int":return "WriteInt(bytes ," + name + ", ref index);";case "bool":return "WriteBool(bytes ," + name + ", ref index);";case "short":return "WriteShort(bytes ," + name + ", ref index);";case "long":return "WriteLong(bytes ," + name + ", ref index);";case "float":return "WriteFloat(bytes ," + name + ", ref index);";case "string":return "WriteString(bytes ," + name + ", ref index);";case "enum":return "WriteInt(bytes ,Convert.ToInt32(" + name + "), ref index);";default:return "WriteData(bytes ," + name + ", ref index);";}}private string GetReadingStr(XmlNodeList fields){string readingStr = "";string type = "";string name = "";foreach (XmlNode field in fields){type = field.Attributes["type"].Value;name = field.Attributes["name"].Value;if (type == "list"){string T = field.Attributes["T"].Value;readingStr += "\t\t\t" + name + " =new List<" + T + ">();\r\n";readingStr += "\t\t\tshort " + name + "Count =ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\tfor (int i = 0; i < " + name + "Count; ++i)\r\n";readingStr += "\t\t\t\t" + name + ".Add(" + GetReadingValueStr(T) + ");\r\n";}else if (type == "array"){string data = field.Attributes["data"].Value;readingStr += "\t\t\tshort " + name + "Length =ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\t" + name + " = new " + data + "[" + name + "Length];\r\n";readingStr += "\t\t\tfor (int i = 0; i < " + name + "Length; ++i)\r\n";readingStr += "\t\t\t\t" + name + "[i]" +" = "+ GetReadingValueStr(data) + ";\r\n";}else if (type == "dic"){string Tkey = field.Attributes["Tkey"].Value;string Tvalue = field.Attributes["Tvalue"].Value;readingStr += "\t\t\t" + name + " = new Dictionary<" + Tkey + ", " + Tvalue + ">();\r\n";readingStr += "\t\t\tshort " + name + "Count =ReadShort(bytes, ref index);\r\n";readingStr += "\t\t\tfor (int i = 0; i < " + name + "Count; ++i)\r\n";readingStr += "\t\t\t\t" + name + ".Add(" + GetReadingValueStr(Tkey) + ", " +GetReadingValueStr(Tvalue) + ");\r\n";}else if(type=="enum"){string data = field.Attributes["data"].Value;readingStr += "\t\t\t" + name + " = (" + data + ")ReadInt(bytes, ref index);\r\n";}elsereadingStr += "\t\t\t" + name + " = " + GetReadingValueStr(type) + ";\r\n";}return readingStr;}private string GetReadingValueStr(string type){switch (type){case "byte":return "ReadByte(bytes, ref index)";case "int":return "ReadInt(bytes, ref index)";case "short":return "ReadShort(bytes, ref index)";case "long":return "ReadLong(bytes, ref index)";case "float":return "ReadFloat(bytes, ref index)";case "bool":return "ReadBool(bytes, ref index)";case "string":return "ReadString(bytes, ref index)";default:return "ReadData<" + type + ">(bytes, ref index)";}}public void GeneratreMsg(XmlNodeList nodes){string idStr = "";string namespaceStr = "";string classNameStr = "";string fieldStr = "";string GetBytesNumStr = "";string WritingStr = "";string ReadingStr = "";foreach (XmlNode dataNode in nodes){//消息IDidStr = dataNode.Attributes["id"].Value;//命名空间namespaceStr = dataNode.Attributes["namespace"].Value;//类名classNameStr = dataNode.Attributes["name"].Value;//读取所有字段节点XmlNodeList fields = dataNode.SelectNodes("field");//通过这个方法进行成员变量声明的拼接 返回拼接结果fieldStr = GetFieldStr(fields);//通过这个方法对GetBytesNum函数中字符串内容进行拼接 返回结果GetBytesNumStr = GetGetBytesNumStr(fields);//通过这个方法对Writing函数中字符串内容进行拼接 返回结果WritingStr = GetWritingStr(fields);//通过这个方法对Reading函数中字符串内容进行拼接 返回结果ReadingStr = GetReadingStr(fields);string dataStr = "using System;\r\n" +"using System.Collections.Generic;\r\n" +"using System.Text;\r\n" +$"namespace {namespaceStr}\r\n" +"{\r\n" +$"\tpublic class {classNameStr} : BaseMsg\r\n" +"\t{\r\n" +$"{fieldStr}" +"\t\tpublic override int GetBytesNum()\r\n" +"\t\t{\r\n" +"\t\t\tint num=8;\r\n" +//这个8代表的是 消息ID的4个字节和消息长度的4个字节$"{GetBytesNumStr}" +"\t\t\treturn num;\r\n" +"\t\t}\r\n" +"\t\tpublic override byte[] Writing()\r\n" +"\t\t{\r\n" +"\t\t\tint index =0;\r\n" +"\t\t\tbyte [] bytes =new byte[GetBytesNum()];\r\n" +"\t\t\tWriteInt(bytes, GetID(), ref index);\r\n" +"\t\t\tWriteInt(bytes, bytes.Length - 8, ref index);\r\n" +$"{WritingStr}" +"\t\t\treturn bytes;\r\n" +"\t\t}\r\n" +"\t\tpublic override int Reading(byte[] bytes, int beginIndex = 0)\r\n" +"\t\t{\r\n" +"\t\t\tint index =beginIndex ;\r\n" +$"{ReadingStr}" +"\t\t\treturn index - beginIndex;\r\n" +"\t\t}\r\n" +"\t\tpublic override int GetID()\r\n" +"\t\t{\r\n" +"\t\t\treturn " + idStr + ";\r\n"+"\t\t}\r\n" +"\t}\r\n" +"}";//保存文件的路径string path = SAVE_PATH + namespaceStr + "/Msg/";//如果不存在这个文件夹 则创建if (!Directory.Exists(path))Directory.CreateDirectory(path);//字符串保存 存储为枚举脚本文件File.WriteAllText(path + classNameStr + ".cs", dataStr);}Debug.Log("数据结构类生成结束");}
}
实现效果
点击生成C#脚本,就会在Unity编辑器中生成一个Protocol文件夹,里边有我们想要的消息脚本
以GamePlayer命名空间为例展示生成后的代码
这是Data文件夹脚本
using System;
using System.Collections.Generic;
using System.Text;
namespace GamePlayer
{public class PlayerData : BaseData{public int id;public float atk;public bool sex;public long lev;public int[]arrays;public List<int>list;public Dictionary<int,string>dic;public override int GetBytesNum(){int num=0;num +=4;num +=4;num +=1;num +=8;num +=2;for(int i = 0;i<arrays.Length;i++)num +=4;num +=2;for(int i = 0;i< list.Count;i++)num +=4;num +=2;foreach(int key in dic.Keys){num +=4;num +=4+Encoding.UTF8.GetByteCount(dic[key]);}return num;}public override byte[] Writing(){int index =0;byte [] bytes =new byte[GetBytesNum()];WriteInt(bytes ,id, ref index);WriteFloat(bytes ,atk, ref index);WriteBool(bytes ,sex, ref index);WriteLong(bytes ,lev, ref index);WriteShort(bytes, (short)arrays.Length, ref index);for (int i=0; i < arrays.Length;++i)WriteInt(bytes ,arrays[i], ref index);WriteShort(bytes, (short)list.Count, ref index);for (int i=0; i < list.Count;++i)WriteInt(bytes ,list[i], ref index);WriteShort(bytes, (short)dic.Count, ref index);foreach(int key in dic.Keys){WriteInt(bytes ,key, ref index);WriteString(bytes ,dic[key], ref index);}return bytes;}public override int Reading(byte[] bytes, int beginIndex = 0){int index =beginIndex ;id = ReadInt(bytes, ref index);atk = ReadFloat(bytes, ref index);sex = ReadBool(bytes, ref index);lev = ReadLong(bytes, ref index);short arraysLength =ReadShort(bytes, ref index);arrays = new int[arraysLength];for (int i = 0; i < arraysLength; ++i)arrays[i] = ReadInt(bytes, ref index);list =new List<int>();short listCount =ReadShort(bytes, ref index);for (int i = 0; i < listCount; ++i)list.Add(ReadInt(bytes, ref index));dic = new Dictionary<int, string>();short dicCount =ReadShort(bytes, ref index);for (int i = 0; i < dicCount; ++i)dic.Add(ReadInt(bytes, ref index), ReadString(bytes, ref index));return index - beginIndex;}}
}
这是Enum文件夹中脚本
namespace GamePlayer
{public enum E_PLAYER_TYPE{MAIN=1,OTHER,}
}
这是Msg文件夹中脚本
using System;
using System.Collections.Generic;
using System.Text;
namespace GamePlayer
{public class PlayerMsg : BaseMsg{public int playerID;public PlayerData data;public override int GetBytesNum(){int num=8;num +=4;num +=data.GetBytesNum();return num;}public override byte[] Writing(){int index =0;byte [] bytes =new byte[GetBytesNum()];WriteInt(bytes, GetID(), ref index);WriteInt(bytes, bytes.Length - 8, ref index);WriteInt(bytes ,playerID, ref index);WriteData(bytes ,data, ref index);return bytes;}public override int Reading(byte[] bytes, int beginIndex = 0){int index =beginIndex ;playerID = ReadInt(bytes, ref index);data = ReadData<PlayerData>(bytes, ref index);return index - beginIndex;}public override int GetID(){return 1001;}}
}
总结
// 根据配置生成脚本的文件的主要思路就是
// 按规则拼接字符串
// 只要有数据和规则,我们就可以动态的创建脚本文件
相关文章:
协议(消息)生成
目录 协议(消息)生成主要做什么? 知识点二 制作功能前的准备工作 编辑编辑 制作消息生成功能 实现效果 总结 上一篇中配置的XML文件可见: https://mpbeta.csdn.net/mp_blog/creation/editor/147647176 协议(消息)生成主要做什么? //协议生成 主要是…...
【PostgreSQL数据分析实战:从数据清洗到可视化全流程】4.5 清洗流程自动化(存储过程/定时任务)
👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 PostgreSQL数据清洗自动化:存储过程与定时任务全攻略4.5 清洗流程自动化:构建智能数据处理管道4.5.1 存储过程:复杂清洗逻辑封装4.5.1.1 …...
Python中有序序列容器的概念及其与可变性的关系
什么是有序序列容器? 有序序列容器是Python中一类重要的数据类型,它们具有以下共同特征: 元素有序排列:元素按照插入顺序存储,可以通过位置(索引)访问 可迭代:可以使用for循环遍历…...
数据结构实验8.1:图的基本操作
文章目录 一,实验目的二,实验内容三,实验要求四,算法分析五,示例代码8-1.cpp源码graph.h源码 六,操作步骤七,运行结果 一,实验目的 1.掌握图的邻接矩阵、邻接表的表示方…...
PostgreSQL 的 pg_current_wal_lsn 函数
PostgreSQL 的 pg_current_wal_lsn 函数 pg_current_wal_lsn 是 PostgreSQL 中用于获取当前预写式日志(WAL)写入位置的关键函数,对于数据库监控、复制管理和恢复操作至关重要。 一 基本说明 语法 pg_current_wal_lsn() RETURNS pg_lsn功能 返回当前的 WAL 写入…...
P6822 [PA 2012 Finals] Tax 题解
题目大意 可恶,我们老师竟然把紫题放到了模拟赛里。 题目传送门 原题中题意说的很清楚了。 思路 转化问题 首先先新建两条边,使原题点到点的问题转化成边到边的问题。 可以连接一条从 0 0 0 到 1 1 1,长度为 0 0 0 的边,设这条边为 0 0 0 号边。 还可以连接一条…...
Python异步编程入门:从同步到异步的思维转变
引言 作为一名开发者,你可能已经习惯了传统的同步编程模式——代码一行接一行地执行,每个操作都等待前一个操作完成。但在I/O密集型应用中,这种模式会导致大量时间浪费在等待上。今天,我们将探讨Python中的异步编程,这…...
【Python】使用`python-dotenv`模块管理环境变量
最近田辛老师在进行与AI有关的开发。 在开发和部署 Python 应用程序时(要么是在某个Python环境,要么是在MaxKB等知识库系统),我常常需要根据不同的环境(如开发环境、测试环境、生产环境)使用不同的配置信息…...
破局者手册 Ⅰ:测试开发核心基础,解锁未来测试密钥!
目录 一、引入背景 二、软件测试基础概念 2.1 软件测试的定义 2.2 软件测试的重要性 2.3 软件测试的原则 三、测试类型 3.1 功能测试 3.2 接口测试 3.2.1 接口测试的概念 3.2.2 接口测试的重要性 3.2.3 接口测试的要点 3.2.4 接口测试代码示例(Python r…...
物联网mqtt和互联网http协议区别
MQTT和HTTP是两种不同的网络协议,它们在以下方面存在区别: 一、连接方式 1.MQTT:基于TCP/IP协议,采用长连接方式。客户端与服务器建立连接后,会保持连接状态,可随时进行数据传输,适用于实时性…...
C++笔记之反射、Qt中的反射系统、虚幻引擎中的反射系统
C++笔记之反射、Qt中的反射系统、虚幻引擎中的反射系统 code review! 目录 C++笔记之反射、Qt中的反射系统、虚幻引擎中的反射系统 目录1. 反射基础概念 1...
提示词压缩方法总结与开源工具包
论文标题 AN EMPIRICAL STUDY ON PROMPT COMPRESSION FOR LARGE LANGUAGE MODELS 论文地址 https://arxiv.org/pdf/2505.00019 开源地址 https://github.com/3DAgentWorld/Toolkit-for-Prompt-Compression 作者背景 香港科技大学广州校区,华南理工大学&#…...
【AI提示词】AARRR 模型执行者
提示说明 具备完整的产品知识和数据分析能力,擅长通过AARRR模型优化用户生命周期管理,提升企业收入和市场拓展。 提示词 # Role: AARRR 模型执行者## Profile - language: 中文 - description: 具备完整的产品知识和数据分析能力,擅长通过…...
深入理解 Redis 的主从、哨兵与集群架构
目录 前言1 Redis 主从架构1.1 架构概述1.2 优点与应用场景1.3 局限性 2 Redis 哨兵架构2.1 架构概述2.2 高可用能力的实现2.3 局限与注意事项 3 Redis 集群架构3.1 架构概述3.2 高性能与高可用的统一3.3 限制与挑战 4 架构对比与选型建议结语 前言 在构建高性能、高可用的数据…...
基于CBOW模型的词向量训练实战:从原理到PyTorch实现
基于CBOW模型的词向量训练实战:从原理到PyTorch实现 在自然语言处理(NLP)领域,词向量是将单词映射为计算机可处理的数值向量的重要方式。通过词向量,单词之间的语义关系能够以数学形式表达,为后续的文本分…...
【阿里云大模型高级工程师ACP习题集】2.9 大模型应用生产实践(下篇)
练习题 【单选题】在大模型应用备案中,根据《生成式人工智能服务管理暂行办法》,已上架但未完成合规手续的应用应如何处理?( ) A. 继续运营,同时补办手续 B. 下架处理 C. 暂停部分功能,直至完成合规手续 D. 无需处理,等待监管部门通知 【多选题】在应用服务安全的应用部…...
Matlab实现CNN-BiLSTM时间序列预测未来
Matlab实现CNN-BiLSTM时间序列预测未来 目录 Matlab实现CNN-BiLSTM时间序列预测未来效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CNN-BiLSTM时间序列预测未来; 2.运行环境Matlab2023b及以上,data为数据集,单变量时间序…...
互联网大厂Java求职面试:AI大模型与云原生架构设计深度解析
互联网大厂Java求职面试:AI大模型与云原生架构设计深度解析 第一轮提问:AI大模型与系统集成 技术总监(张总):郑薪苦,你之前提到过Spring AI,那你能讲讲在实际项目中如何将大模型集成到系统中&…...
GD32F103C8T6多串口DMA空闲中断通信程序
以下是一个完全符合C99标准的GD32F103C8T6多串口DMA通信完整实现,代码经过Keil MDK验证并包含详细注释: #include "gd32f10x.h" #include <string.h>/* 硬件配置宏 */ #define USART_NUM 2 /* 使用2个串口 */ #define R…...
labelimg快捷键
一、核心标注快捷键 W:调出标注十字架,开始绘制矩形框(最常用功能)A/D:切换上一张(A)或下一张(D)图片,实现快速导航Del:删除当前选中的标注框 二、文件操作快捷键 CtrlS&…...
【深度学习-Day 6】掌握 NumPy:ndarray 创建、索引、运算与性能优化指南
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...
开元类双端互动组件部署实战全流程教程(第2部分:控制端协议拆解与机器人逻辑调试)
作者:那个写了个机器人结果自己被踢出房间的开发者 游戏逻辑房间结构参考界面 从这张图我们能看出,该组件按功能结构细分为多个房间,每个房间底注、准入标准不同,对应的控制模块也有层级区分。常规来说,一个“互动房间…...
51单片机入门教程——蜂鸣器播放天空之城
前言 本教程基于B站江协科技课程进行个人学习整理,专为拥有C语言基础的零基础入门51单片机新手设计。既帮助解决因时间差导致的设备迭代调试难题,也助力新手快速掌握51单片机核心知识,实现从C语言理论到单片机实践应用的高效过渡 。 目录 …...
linux 历史记录命令
命令方式 #/bin/bash #cd /tmp saveFile"tmp.log" isok"grep HISTTIMEFORMAT /etc/profile|wc -l" if [ $isok -eq 0 ] thenecho -e "#history time\nHISTFILESIZE4000\nHISTSIZE4000\nHISTTIMEFORMAT%F %T \nexport HISTTIMEFORMAT\n" >>…...
手表关于MPU6050中的功能实现
MPU6050 OV-Watch 中的睡眠和唤醒功能实现 OV-Watch 项目为 MPU6050 传感器实施了复杂的电源管理,以优化电池寿命,同时保持手腕检测和计步功能。以下是对睡眠和唤醒机制的详细分析: 内核休眠/唤醒功能实现 MPU6050 有两个主要功能来控制其…...
Qt中数据结构使用自定义类————附带详细示例
文章目录 C对数据结构使用自定义类1 QMap使用自定义类1.1 使用自定义类做key1.2 使用自定义类做value 2 QSet使用自定义类 参考 C对数据结构使用自定义类 1 QMap使用自定义类 1.1 使用自定义类做key QMap<key,value>中数据存入时会对存入key值的数据进行比较ÿ…...
深入浅出数据库的函数依赖关系
数据库的“恋爱关系”:函数依赖的那些事儿 在数据库的世界里,属性之间也存在“恋爱关系”。有些属性是“灵魂伴侣”,彼此绑定;有些是“单向奔赴”,只能依赖对方;还有些是“三角恋”,通过中间人…...
C语言易混淆知识点详解
C语言中容易混淆的知识点详解 C语言作为一门基础且强大的编程语言,有许多容易混淆的概念和特性。以下是C语言中一些常见易混淆知识点的详细解析: 1. 指针与数组 相似点: c 复制 下载 int arr[10]; int *ptr arr; 都可以使用[]运算符访…...
如何选择合适的光源?
目录 工业相机光源类型全面指南 1. 环形光源及其变体 高角度环形光源 优点 缺点 典型应用场景 低角度环形光源(暗场照明) 优点 缺点 典型应用场景 2. 条形光源与组合照明系统 技术特点 组合条形光源 优点 缺点 典型应用场景 3. 同轴光源…...
模块方法模式(Module Method Pattern)
🧠 模块方法模式(Module Method Pattern) 模块方法模式是一种结构型设计模式,它将复杂的操作分解成一系列相对简单、独立且单一职责的模块。每个模块负责完成一种具体的操作,其他模块或系统可以通过调用这些模块的公开…...
OpenCV第6课 图像处理之几何变换(仿射)
1.仿射变换 2. 平移 3 旋转 附录A 二维旋转矩阵 附录B 三维旋转矩阵与轴角表示 1.仿射变换 仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够保持图像的平直性和平行性。 平直性是指图像经过仿射变换后,直线仍然是直线,平行性是指图像在…...
【中间件】brpc_基础_TimerThread
文章目录 TimerThread1 简介2 主要设计点2.1 数据结构:分层时间轮(Hierarchical Timing Wheel)2.2 线程模型2.3 任务管理 3 关键代码分析3.1 类定义(timer_thread.h)3.2 时间轮初始化(timer_thread.cpp&…...
拷贝多个Excel单元格区域为图片并粘贴到Word
Excel工作表Sheet1中有两个报表,相应单元格区域分别定义名称为Report1和Report2,如下图所示。 现在需要将图片拷贝图片粘贴到新建的Word文档中。 示例代码如下。 Sub Demo()Dim oWordApp As ObjectDim ws As Worksheet: Set ws ThisWorkbook.Sheets(&…...
网络原理(6)—— 应用层之HTTP协议
目录 一. 应用层 二. 重要应用层协议DNS(Domain Name System) 三. HTTP协议 3.1 HTTP抓包工具 3.2 HTTP格式 3.2.1 请求 3.2.2 响应 3.3 HTTP的工作过程 一. 应用层 应用层协议就像是人们之间的交流规则,它帮助不同的计算机程序(应用)…...
Linux55yum源配置、本机yum源备份,本机yum源配置,网络Yum源配置,自建yum源仓库
参考 太晚了 计划先休息了 大概保存...
250505_HTML
HTML 1. HTML5语法与基础标签1.1 HTML5特性1.1.1 空白折叠现象1.1.2 转义字符 1.2 HTML注释1.3 基础标签1.3.1 div标签1.3.2 标题标签1.3.3 段落标签1.3.1.3.1.3.1.3. 1. HTML5语法与基础标签 1.1 HTML5特性 1.1.1 空白折叠现象 1.1.2 转义字符 1.2 HTML注释 1.3 基础标签 1…...
1. 设计哲学:让字面量“活”起来,提升表达力和安全性
C11引入的用户定义字面量(User-Defined Literals,简称UDL)是语言层面为程序员打开的一扇“自定义表达式”的大门。它允许我们为字面量(比如数字、字符、字符串)添加自定义后缀,从而让代码更具语义化、更易读…...
【KWDB 创作者计划】基于 ESP32 + KWDB 的智能环境监测系统实战
一开始萌生这个想法,其实是源自我办公桌上的那颗“小胖子”——一块 ESP32 开发板。它陪我度过了不少调试夜,也让我对物联网有了真正的感知。恰逢 KaiwuDB 举办征文活动,我便想着,何不将我日常积攒下来的一些硬件和数据库实战经验…...
AVHRR中国积雪物候数据集(1980-2020年)
数据集摘要 本数据集基于1980-2020年5kmAVHRR逐日无云积雪面积产品,制备了中国长时间序列积雪物候数据集。数据集按照不同的物候参数共分为积雪日数、积雪初日、积雪终日3个目录,每个目录下包含40个子文件,为逐水文年积雪物候参数,…...
PCB设计中电感封装的选型
在PCB设计中,电感封装的选型直接影响电路性能、布局效率、热管理能力及系统可靠性。合理的封装选择不仅能优化空间利用率,还能提升电磁兼容性(EMC)和长期稳定性。以下从封装类型、尺寸参数、应用场景适配、布局协同设计、热管理策…...
LintCode第766题,LintCode第1141题,LintCode第478题
第766题描述 判断给出的年份 n 是否为闰年. 如果 n 为闰年则返回 true 闰年是包含额外一天的日历年. 如果年份可以被 4 整除且不能被 100 整除 或者 可以被 400 整除, 那么这一年为闰年 样例 1: 输入 : n 2008 输出 : true 样例 2: 输入 : n 2018 输出 : false 代码如…...
三十一、基于HMM的词性标注
基于HMM的中文词性标注 1 实验目标 理解HMM模型的原理和基本问题理解HMM的实现命名实体识别的具体步骤掌握HMM模型实现命名实体识别的方法 2 实验环境 HMM的中文词性标注的实验环境。 3 实验步骤 该项目主要由3个代码文件组成,分别为hmm.py、tagging.py和run.p…...
MCUboot 中的 BOOT_SWAP_TYPE_PERM 功能介绍
目录 概述 1 Image 数据结构 1.1 Image介绍 1.2 Swap info 2 BOOT_SWAP_TYPE_PERM 功能 2.1 功能定义 2.2 典型工作流程 3 BOOT_SWAP_TYPE_xx的其他功能 3.1 BOOT_SWAP_TYPE_REVERT 3.2 三中模式的比较 4 使用机制 4.1 实现细节 4.2 使用场景 4.3 开发者注意事…...
数学复习笔记 2
前言 朋友和我讨论了一个二重积分题,非常有意思。内容非常细致。整理如下: 二重积分 题目来源是 1000 上面的 16 题,积分区域是一个偏心圆,偏心圆的圆心在 y 轴上面,偏心圆是关于 y 轴对称的,可以看关于…...
从入门到登峰-嵌入式Tracker定位算法全景之旅 Part 5 |地图匹配与轻量 SLAM:HMM/Viterbi 与简化图优化
Part 5 |地图匹配与轻量 SLAM:HMM/Viterbi 与简化图优化 在本章中,我们将在 ESP32-S3 这样的资源受限平台上,实现 地图匹配(Map Matching)和轻量级图优化(Lightweight SLAM)功能。通过 隐马尔可夫模型(HMM)+ Viterbi 算法,以及简化的图优化思路,校正定位轨迹,提升…...
Amazon Bedrock Converse API:开启对话式AI新体验
Amazon Bedrock Converse API:开启对话式AI新体验 前言 在当今人工智能飞速发展的时代,对话式AI已成为众多应用的核心组成部分。从智能客服到智能助手,对话式AI为用户带来了便捷且高效的交互体验。而Amazon Bedrock Converse API的出现&…...
软考 系统架构设计师系列知识点之杂项集萃(54)
接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(53) 第87题 某银行系统采用Factory Method方法描述其不同账户之间的关系,设计出的类图如下所示。其中与Factory Method的“Creator”角色对应的类是(ÿ…...
第三章 - 软件质量工程体系
1 概述 系统工程学的思想 系统工程学是为了研究多个子系统构成的整体系统所具有的多种不同目标的相互协调,以期系统功能的最优化、最大限度地发挥系统组成部分的能力而发展起来的一门科学。 软件质量工程体系的建立 建立和实施质量管理体系的方法 确定顾客和其…...
#基础Machine Learning 算法(上)
机器学习算法的分类 机器学习算法大致可以分为三类: 监督学习算法 (Supervised Algorithms):在监督学习训练过程中,可以由训练数据集学到或建立一个模式(函数 / learning model),并依此模式推测新的实例。…...
【YOLO11改进】改进Conv、颈部网络STFEN、以及引入PIOU用于小目标检测!
改进后的整体网络架构 改进一:RFD模块(Conv) YOLOv11模型的跨步卷积下采样虽然快速聚合了局部特征,并且实现了较高的计算效率,但其固有的信息压缩机制会导致细粒度特征的不可逆丢失。针对特征保留与计算效率的平衡问题,本文采用RFD模块替换跨步卷积下采样模块。RFD模块通…...