一、文档范围
本说明中的“配置文件”主要指项目中以 Excel 为数据源、转换为 JSON 文本后由客户端或服务端读取的游戏静态配置,例如:
- 角色配置
- 地图配置
- 传送点配置
- 道具配置
- 装备配置
- 商店配置
- 技能配置
- Buff 配置
- 任务配置
- 怪物刷新配置
本说明中的“网络协议”指:
message.proto
- 自动生成的
message.cs
- 编译后的
Protocol.dll
- 消息分发、发送和接收代码
本文不讨论
log4net.xml、App.config、Unity ProjectSettings 等运行环境配置。二、项目中存在的两条生产链
1. 游戏配置生产链
对应目录:
2. 网络协议生产链
对应目录:
配置和协议属于两套不同的系统:
- 配置解决“游戏中有哪些内容,以及这些内容的参数是什么”。
- 协议解决“客户端与服务端通过网络传递什么消息,以及消息如何编码”。
三、当前项目的配置文件现状
1. 当前 Excel 配置表
Src\Data\Tables 中目前有以下 Excel:共 15 个 Excel 配置源文件。
2. 当前生成的配置文本
Src\Data\Data 中目前有:其中
SpawnPointDefine.txt 没有对应的 SpawnPointDefine.xlsx,说明它不是当前 Excel 转换链直接生成的普通配置。客户端
DataManager 中提供了:这两个函数可以把内存中的数据序列化为客户端
Data 目录下的文本文件。但是当前项目没有找到调用这两个保存函数的代码入口,因此
SpawnPointDefine.txt 的正式编辑来源目前并不完全明确。修改它之前应先确认它是手工维护、场景工具生成,还是课程后续才会补充生成入口。3. 当前真正处于加载状态的配置
客户端当前主动读取:
服务端当前主动读取:
其他配置虽然存在于 Excel 和
.txt 目录中,但当前加载代码大部分处于注释状态,或者没有对应的 Common.Data 类型。因此:
配置文件存在,不代表当前程序已经在运行时使用它。
当前真正处于有效加载链路中的配置主要是:
配置 | 客户端加载 | 服务端加载 | Common 类型 |
MapDefine | 是 | 是 | 有 |
CharacterDefine | 是 | 是 | 有 |
TeleporterDefine | 是 | 是 | 有 |
SpawnPointDefine | 是 | 否 | 有 |
SpawnRuleDefine | 否 | 否 | 有 |
其他 Excel 配置 | 当前未启用或未完成 | 当前未启用或未完成 | 当前多数没有对应类型 |
四、配置文件各层分别负责什么
1. Excel 负责配置内容
例如:
Excel 适合人工维护,因为:
- 内容直观。
- 方便批量编辑。
- 策划或开发人员不需要修改 C# 代码。
- 容易增加数据行。
- 可以统一管理多个游戏对象。
Excel 是配置的编辑源,不是程序直接运行时读取的文件。
2. .txt 负责运行时数据
虽然扩展名是
.txt,但内容实际上是 JSON。例如:
程序使用:
将 JSON 文本转换成 C# 对象。
使用 JSON 的原因:
- 比直接读取 Excel 简单。
- 不需要在客户端和服务端引入 Excel 解析库。
- 文件结构清晰。
- 可以直接反序列化成字典和对象。
- 适合客户端和服务端共同读取。
3. Common.Data 负责配置结构
例如:
JSON 是动态文本,C# 是静态类型。
反序列化时,Json.NET 会根据属性名称,将 JSON 字段写入对应的 C# 属性:
因此:
- Excel 和 JSON 决定“有什么数据”。
Common.Data决定“代码能以什么类型读取这些数据”。
如果 JSON 里有字段,但 C# 类中没有对应属性,Json.NET 默认会忽略该字段。
如果 C# 类中有属性,但 JSON 里没有对应字段,该属性通常会保持默认值:
如果字段类型不兼容,则可能在反序列化时报错。
4. DataManager 负责加载和保存
客户端:
服务端:
它们负责:
- 读取指定
.txt文件。
- 将 JSON 转换成配置对象。
- 将结果保存在字典中。
- 供后续业务系统查询。
例如:
后续代码可以通过:
获得对应地图配置。
5. Common.dll 负责向客户端提供共享类型
Common.Data 源代码位于 Unity 项目的 Assets 目录之外,Unity不会直接编译这些源文件。因此需要:
客户端最终使用的是:
而不是直接使用
Src\Lib\Common\Data 中的 .cs 文件。五、只修改现有配置数据的流程
适用于:
- 修改名称。
- 修改描述。
- 修改数值。
- 修改资源名称。
- 修改地图音乐。
- 修改角色速度。
- 在字段结构不变的情况下新增一条配置数据。
操作流程:
这种修改不需要:
- 修改
Common.Data类。
- 修改
message.proto。
- 重新编译
Common.dll。
- 重新编译
Protocol.dll。
原因是配置的数据结构没有变化,代码仍然可以使用原来的属性读取新值。
可以记成:
六、新增代码需要读取的配置字段
例如 Excel 新增:
并且代码需要使用这个字段。
完整流程:
例如 Excel 新增:
则
MapDefine.cs 中需要增加:如果 Excel 使用:
则 C# 可以定义为:
字段名称和类型应保持一致。
底层原因是:
因此:
不需要把
MapDefine.cs 等源文件复制到 Unity。需要复制或自动更新的是编译后的 Common.dll。七、新增一张全新的配置表
例如新增:
仅创建 Excel 不够,还需要把整个数据链补全。
操作流程
第一步:创建 Excel
创建:
定义字段、类型、Key 和数据行。
第二步:创建 Common 配置类
创建:
例如:
第三步:确认项目包含新类
当前
Common.csproj 是旧式非 SDK 项目,源文件需要出现在项目的 <Compile Include="..."> 列表中。如果通过 Visual Studio 在项目中创建文件,通常会自动加入项目。
如果从文件系统直接创建,则需要确认
Common.csproj 中包含:否则文件虽然存在,但不会被编译进
Common.dll。第四步:在 DataManager 中增加容器和读取逻辑
根据需要在客户端或服务端增加:
以及:
只在真正需要这一配置的一端加载:
- 客户端需要显示或表现数据,就加入客户端。
- 服务端需要进行权威判定,就加入服务端。
- 两边都需要,就两边都加入。
第五步:更新配置复制脚本
在
Excel2Json.cmd 中加入客户端或服务端复制命令:不要默认复制到两端,应根据实际消费者决定。
第六步:运行转换并重新生成解决方案
第七步:验证
检查:
DungeonDefine.txt是否生成。
- 需要使用它的客户端或服务端目录中是否存在该文件。
Common.dll是否更新。
DataManager是否成功加载。
- 业务代码是否能通过 ID 查询。
- 缺失、重复或错误 ID 是否会得到明确错误。
八、重命名或删除配置字段
重命名字段不能只修改 Excel。
例如:
改成:
需要同步检查:
- Excel 表头。
- 对应
Common.Data属性。
- 所有读取该属性的客户端代码。
- 所有读取该属性的服务端代码。
- 生成后的 JSON 字段名。
- 测试或编辑器工具。
- 文档和调试输出。
删除字段时也必须先搜索代码引用。
否则可能出现:
- 代码编译失败。
- 属性始终得到默认值。
- 旧 JSON 字段被静默忽略。
- 客户端和服务端使用不同字段。
- 业务逻辑在没有报错的情况下使用错误默认值。
因此,删除或重命名前应先进行全项目引用搜索。
九、配置 ID 的修改原则
配置 ID 通常会被多个系统引用,例如:
- 数据库中的角色
MapID。
- 其他配置表中的外键。
- 网络协议中的对象 ID。
- 客户端字典键。
- 服务端字典键。
- 存档数据。
因此,已经被使用的 ID 不应随意修改或复用。
推荐规则:
- 可以修改名称和描述。
- 可以修改表现资源。
- 不要轻易修改已进入数据库或存档的 ID。
- 删除配置时,不要马上把旧 ID 分配给另一种对象。
- 检查
Key和对象内部ID是否一致。
- 检查引用该 ID 的其他配置是否仍然有效。
底层上,ID 相当于不同系统之间的外键。
十、当前 Excel2Json 脚本并没有同步所有配置
当前
Excel2Json.cmd 会先转换 Tables 中的 Excel,但复制清单不是完整的。当前自动复制到客户端的配置
当前自动复制到服务端 Debug 的配置
当前脚本没有自动复制的配置
此外:
SpawnRuleDefine.txt当前只复制到客户端,没有复制到服务端。
RideDefine.txt当前只复制到客户端,没有复制到服务端。
- 服务端和客户端实际都会读取
TeleporterDefine.txt,但脚本没有复制它。
- 客户端实际会读取
SpawnPointDefine.txt,但脚本没有复制它。
当前检查时,统一生成目录、客户端目录和服务端 Debug 目录中的所有
.txt 内容仍然一致。但这只能证明当前文件没有不同步,不能证明下一次运行脚本后仍然全部同步。
例如修改
TeleporterDefine.xlsx 后:因此,在现有脚本没有补全以前,修改脚本复制清单之外的配置后,需要手动同步对应
.txt,并验证三个目录中的文件一致。十一、服务端 Release 配置问题
当前脚本只复制到:
不会自动复制到:
因此:
- 使用 Debug 服务端时,按当前脚本路径检查。
- 使用 Release 服务端时,需要单独准备
bin\Release\Data。
- 不要认为“重新生成 Release”会自动携带所有 Data 文件。
配置文件是运行时外部数据,不会因为 C# 编译成功就自动进入输出目录。
十二、网络协议文件分别负责什么
1. message.proto
路径:
负责定义:
- 枚举。
- 基础数据结构。
- 请求消息。
- 响应消息。
NetMessageRequest。
NetMessageResponse。
- 字段类型。
- Protobuf 字段编号。
例如:
其中:
字段编号是二进制协议的一部分,不只是显示顺序。
2. genproto.cmd
路径:
负责将:
转换为:
3. message.cs
这是自动生成的代码。
不要直接修改,因为下一次执行
genproto.cmd 时会被覆盖。应修改:
然后重新生成。
4. Protocol.dll
message.cs 仍然只是 C# 源代码。重新生成
Protocol 项目后才会得到:Unity 客户端需要的目标文件是:
5. PackageHandler
路径:
负责:
- 使用 Protobuf 将
NetMessage序列化为二进制。
- 在数据前增加 4 字节长度。
- 通过 TCP 发送。
- 接收后根据长度拆包。
- 使用 Protobuf 反序列化为
NetMessage。
- 将消息交给消息分发器。
数据包结构可以理解为:
增加长度字段是因为 TCP 是字节流,不保留应用层消息边界。接收方需要先知道一条消息有多长,才能处理粘包和半包。
6. MessageDistributer
路径:
负责:
- 接收已经反序列化的
NetMessage。
- 区分 Request 和 Response。
- 将消息交给
MessageDispatch。
- 根据具体消息类型调用已注册的处理函数。
7. MessageDispatch
路径:
负责把外层消息拆成具体业务消息,例如:
当前文件顶部写着:
但当前仓库里没有找到负责重新生成
MessageDispatch.cs 的脚本。现有
genproto.cmd 只生成 message.cs,不会更新 MessageDispatch.cs。因此,以当前仓库为准:
- 给现有消息增加普通字段,不需要改
MessageDispatch.cs。
- 新增一种顶层 Request 或 Response,需要让
MessageDispatch.cs能识别它。
- 如果没有补充对应生成器,就需要手动增加分发代码。
- 修改
MessageDispatch.cs后需要重新生成Common.dll。
十三、给现有网络消息增加字段
例如给角色进入地图响应增加:
完整流程:
通常不需要修改
MessageDispatch.cs,因为消息类型仍然是原来的:只是消息内部增加了字段。
这种情况下核心产物是:
如果 Common 代码也使用了新字段,则还应重新生成并更新:
十四、新增一种全新的网络消息
例如新增:
完整流程如下。
第一步:在 message.proto 中定义具体消息
第二步:加入外层消息容器
在
NetMessageRequest 中增加:在
NetMessageResponse 中增加:外层容器决定这条具体消息能否被包装进统一的
NetMessage 中。第三步:运行协议生成
生成新的:
第四步:更新消息分发
在
MessageDispatch.cs 的 Request 分发中增加:在 Response 分发中增加对应逻辑。
原因是:
- Protobuf 只负责把二进制转换成对象。
MessageDispatch才负责把对象交给具体业务处理函数。
- 如果没有分发代码,消息可能已经成功收到和反序列化,但业务层收不到事件。
第五步:实现发送方
例如客户端创建:
第六步:实现接收方
例如服务端订阅:
并实现:
响应方向同理:
- 服务端构造
DungeonEnterResponse。
- 客户端订阅
DungeonEnterResponse。
- 客户端处理响应。
第七步:重新生成解决方案
新增顶层协议后通常需要生成:
原因:
Protocol.dll包含新消息类型。
Common.dll包含新的消息分发代码。
GameServer.exe包含新的服务端处理逻辑。
- Unity 会重新编译
Assets下的客户端业务脚本。
十五、修改协议枚举或共享数据结构
例如修改:
或者:
流程与修改现有消息字段相同:
协议枚举的数字值也属于网络契约。
不要因为显示顺序变化就随意调整已有枚举值,否则旧客户端和新服务端可能把同一个数字解释成不同含义。
十六、Protobuf 字段编号规则
1. 已发布字段不要随意改编号
例如:
不要直接改成:
字段名主要影响生成代码,字段编号决定二进制数据如何解释。
2. 新字段使用新的唯一编号
例如已有:
新增字段应使用:
3. 删除字段后不要马上复用旧编号
否则旧客户端发送的旧字段可能被新服务端解释成另一种新字段。
4. 重命名字段但保留编号
在二进制层面通常仍可能兼容,但生成的 C# 属性名称会变化,因此所有代码引用都需要更新。
5. 不要随意修改字段类型
例如:
直接改成:
可能造成协议兼容问题。
如果语义发生明显变化,通常更适合新增一个字段编号,并逐步停用旧字段。
十七、协议修改后需要复制哪些文件
只修改现有协议字段
必须更新:
新增顶层消息并修改 MessageDispatch
需要更新:
来源:
客户端目标:
不要把这些源文件复制到 Unity:
Unity 使用的是编译后的 DLL。
十八、当前项目的 DLL 自动复制
当前
Common.csproj 配置了构建后复制:目标为:
因此,重新生成包含
Common 的解决方案后,通常会自动更新:需要注意:
- 如果只单独生成
Protocol项目,Common的构建后复制可能不会执行。
- 如果
Common被判断为无需重新生成,构建后命令也可能不执行。
- 最稳妥的方法是重新生成整个
GameServer.sln,然后检查客户端 DLL。
.pdb是调试符号,不是运行必需文件。
- 覆盖 DLL 时保留 Unity 已有的
.meta文件,不要删除后重新导入。
十九、不同修改对应的实际流程
修改内容 | 更新 .txt | 修改 Common 配置类 | 修改 Proto | 修改 MessageDispatch | 重新编译 Common | 重新编译 Protocol |
修改配置值 | 是 | 否 | 否 | 否 | 否 | 否 |
新增同结构配置行 | 是 | 否 | 否 | 否 | 否 | 否 |
新增代码需要读取的配置字段 | 是 | 是 | 否 | 否 | 是 | 否 |
新增全新配置表 | 是 | 是 | 否 | 否 | 是 | 否 |
给现有协议消息增加字段 | 否 | 通常否 | 是 | 否 | 视 Common 使用情况 | 是 |
新增顶层 Request 或 Response | 否 | 否 | 是 | 是 | 是 | 是 |
修改 Common 网络基础代码 | 否 | 否 | 视情况 | 视情况 | 是 | 视情况 |
修改客户端业务处理脚本 | 否 | 否 | 否 | 否 | 否 | 否 |
修改服务端业务处理代码 | 否 | 否 | 否 | 否 | 否 | 否,但需要重新生成 GameServer |
二十、为什么配置和协议不能混为一谈
例如新增一个地图字段:
如果它只用于客户端界面或服务端本地判断:
不需要修改协议。
只有当服务端需要通过网络把它发送给客户端,并且客户端不能通过本地配置查询时,才需要加入协议。
反过来,协议中新增:
这些数据是运行时网络状态,不属于静态配置,不应该写进 Excel。
判断方式:
二十一、修改后必须验证的内容
配置修改验证
- Excel 已保存。
Excel2Json.cmd执行成功。
- 没有忽略覆盖提示。
- 统一生成目录中的
.txt已更新。
- 客户端和服务端需要的
.txt已同步。
- JSON 字段名与 C# 属性名一致。
- JSON 字段类型与 C# 属性类型兼容。
- Key 和对象 ID 唯一且一致。
- 引用其他配置的 ID 确实存在。
- 对应 DataManager 已加载该配置。
- 修改配置结构后,
Common.dll已更新。
- 服务端和客户端已经重启。
协议修改验证
message.proto语法正确。
- 新字段编号没有与已有字段冲突。
- 没有随意修改旧字段编号。
genproto.cmd执行成功。
message.cs的修改时间已经更新。
Protocol.dll已重新生成。
- 客户端
Protocol.dll已更新。
- 新增顶层消息时,
MessageDispatch已更新。
- 发送端已经正确构造外层
NetMessage。
- 接收端已经订阅具体消息类型。
- 服务端业务处理代码已经重新生成。
- 客户端和服务端使用同一版协议。
- 请求和响应都经过实际联调。
二十二、常见错误判断
错误一:运行 Excel2Json 后,DLL 也会自动更新
错误。
Excel2Json.cmd 只负责:- Excel 转 JSON。
- 复制配置文本。
它不会:
- 修改 C# 类。
- 编译 Common。
- 编译 Protocol。
- 生成 DLL。
错误二:Excel 中有字段,代码就能自动读取
错误。
只有
Common.Data 中存在匹配属性时,代码才能访问该字段。错误三:修改了 message.proto,直接复制 message.cs 到 Unity
错误。
Unity需要的是:
不是
message.cs。错误四:新增协议消息后,只运行 genproto 就完成了
错误。
还需要:
- 把新消息加入外层 Request 或 Response。
- 更新消息分发。
- 实现发送端。
- 实现接收端。
- 重新编译。
- 同步 DLL。
- 联调。
错误五:配置文件存在,就说明系统已经使用它
错误。
还需要确认:
- 有对应 Common 类型。
- DataManager 读取了它。
- 文件被复制到正确运行目录。
- 业务代码查询并使用了它。
错误六:客户端和服务端都应该加载所有配置
错误。
应该根据职责加载:
- 服务端加载权威判定需要的配置。
- 客户端加载表现和本地查询需要的配置。
- 两边都需要的配置才同时加载。
二十三、推荐的统一操作顺序
当不确定本次改动属于哪一类时,按以下顺序判断。
第一步:判断修改的是数据、结构还是网络消息
第二步:执行对应流程
数据修改
配置结构修改
现有协议消息增加字段
新增协议消息
二十四、最终记忆方式
完整职责可以记成: