工程参考/配置文件/Server/StringTable.txt
StringTable.txt 是一张「编号 → 文本」的对照表,服务端各处用 StringTable_GetText(编号) 取文案,例如阵亡提示、邮件兜底文本。样本 310 行、2 列,读入后放进一个 1024 桶的哈希表。
它是纯服务端文案,不影响客户端界面;客户端有自己的资源与脚本文本。
文件位置与生效
| 项目 | 说明 |
|---|---|
| 运行文件 | ./Config/StringTable.txt,由 FileDef.h 的 FILE_STRING_TABLE 定义,相对于读取进程的工作目录。World 与 Server 都要有一份。
|
| 读取程序 | World 与 Server 各自调用 StringTable_LoadFromFile(FILE_STRING_TABLE, …) 加载一份;Common/StringTable.cpp 负责解析,LuaFn 与各处的 StringTable_GetText 调用方按编号取文本 |
| 生效方法 | 正常保存并停服后重启对应进程。World 与 Server 各读一份,改动后两个都要重启。当前检索未发现单独重载本文件的可用入口。 |
| 样本格式 | GB18030-compatible,CRLF。保留原编码;源码读取器不会自动把中文转为 UTF-8。
|
加载流程在 Common/StringTable.cpp:StringTable_LoadFromFile 用 DBC 读取器打开文件后交给 StringTable_T::LoadFromMemory,逐行读第 1 列编号与第 2 列文本,装进哈希表。
| 约束 | 行为 |
|---|---|
| 编号重复 | 哈希分桶时如果发现同号节点,直接 return false——整张表算加载失败。 |
| 文案为空 | 该行跳过,不进表;按编号取不到就返回 "Unknown String"。 |
| 取不到编号 | GetStringByID 返回 "Unknown String",不会抛错也不会空指针。
|
| 表没就绪 | 构造函数把 m_bReady 置为 false,加载失败时所有调用都返回 "Unknown String"。 |
很多条目并不是普通句子,而是带 %s 之类占位符的格式串(样本 0 号就是「你被玩家: %s 杀死」)。替换参数由调用方自己传,本表只负责存格式串本身。
文件格式
首行是类型行:INT STRING;第二行是列名行,程序跳过其内容;从第三行开始读取数据。各列用真实 Tab 分隔,保留首列、空列及行尾,不用空格代替。数据行行首 # 表示注释,分号不是此表的注释规则。首列为空的行被跳过;不足的尾列补为空值,多余列不参与本页字段读取。空整数和非数字经 atoi 可能成为 0,空字符串仍为空,不把解析成功当作业务合法。第一列须唯一,重复会触发读取器错误。
字段详解
列号从 1 开始,源码列下标从 0 开始。
| 字段与类型 | 含义与修改要点 | 缺项或特殊值 |
|---|---|---|
第 1 列INT:ID |
文案编号,也是 StringTable_GetText 的查询键。分桶用 nID & 1023(负数改成取绝对值再取模),所以编号本身不必连续。
|
必须唯一;空值解析为 0 也会占一个位置。重复编号会让 LoadFromMemory 提前返回 false,结果是整张表加载失败、所有文案变成 Unknown String。 |
第 2 列STRING:字符串$1$ |
文案正文。常见形态是带占位符的格式串(%s、%d),也可能是一句完整的话。 | 空串会被整行跳过,等价于这个编号不存在(取用时返回 Unknown String)。 |
常用修改实例
示例为局部教学片段,先备份原运行文件,再修改对应行并保留其余配置。教学值不是程序默认值。 以下代码块保留真实 Tab;本地示例文件保留原样本编码、中文表头与 CRLF。网页文字使用浏览器编码显示,不表示运行文件应改存 UTF-8。
把 0 号文案换成英文,同时保留 %s 占位符
修改前:
INT STRING ID 字符串$1$ 0 你被玩家: %s 杀死
修改后:
INT STRING ID 字符串$1$ 0 You were killed by %s.
按第 1 列定位 0 号。原文案是「你被玩家: %s 杀死」,改成英文后仍然保留 %s 占位符——调用方会用自己的参数去填它,占位符的个数和顺序必须和调用方的实参一致。
这一列建议整条替换:只删掉一半占位符不会报错,但运行时会把玩家名字拼到错误的位置。改完重启 World 与 Server;客户端不需要动。示例块含类型行、列名行与该行原文,不能覆盖整表。
验证与排查
World 与 Server 启动后分别会用到这张表;取不到文案时表现为 "Unknown String" 而不是崩溃,所以要主动验证:在样本里新增一个编号并把调用它的代码路径走一遍。故意把两行写成同一个编号,可以看到整表加载失败、所有文案都变成 Unknown String。改错久案不会有明确报错,保存时注意保留编码与 CRLF。
本地化参数是被丢掉的
StringTable_LoadFromFile(szFile, nLocalizationID) 的第二参数在函数体里只有一句 (VOID)nLocalizationID;,也就是说语言 ID **没有参与任何逻辑**——不会去读 StringTable.txt.en、StringTable.txt.cn 之类的本地化副本。
FileNameAfterLocalization 确实会拼出例如 StringTable.txt.cn 这样的名字,但它唯一的作用是**拼报错信息**:加载失败时日志里显示那个名字,并不代表程序试图打开过它。想做多语言必须自己改代码。
重复编号是整表级故障
哈希插入时遇到同号会 return false,整个 LoadFromMemory 提前退出。后果是**所有**文案取不到,不是只影响重复的那一行。
空串等于不存在
长度为 0 的行会被 continue 跳过,既不占位也不报错,等同于删掉这一行。
World 与 Server 各持一份
两个进程分别加载自己工作目录下的文件,两份内容不一致时会出现「同一句提示在不同进程里文案不同」的现象。
关联与依据
其他配置及整理状态 · 服务端外网设置 · Lua 接口索引
Common/StringTable.cpp:实际加载、解析、字段使用或示例关联依据Common/StringTable.h:实际加载、解析、字段使用或示例关联依据Server/Server/WorldServer/World/Main/WorldTable.cpp:实际加载、解析、字段使用或示例关联依据Server/Server/GameServer/Server/Main/GameTable.cpp:实际加载、解析、字段使用或示例关联依据Common/ServerBase/FileDef.h:实际加载、解析、字段使用或示例关联依据Common/DataBase/TLBB_DBC.cpp:实际加载、解析、字段使用或示例关联依据Common/DataBase/TLBB_DBC.h:实际加载、解析、字段使用或示例关联依据Server/Server/GameServer/Server/Script/LuaFnTbl_Mission.h:实际加载、解析、字段使用或示例关联依据resources/Server/Config/StringTable.txt:实际加载、解析、字段使用或示例关联依据
本页核对 StringTable_LoadFromFile / LoadFromMemory / GetStringByID 的实现与 FILE_STRING_TABLE 宏,以及本地化参数被丢弃的事实;未启动服务端与游戏,实际提示文案需要实测。