提交测试3
leaf 代码阅读模块介绍 https://github.com/name5566/leaf/wiki/Leaf-代码阅读
leaf TUTORIAL_ZH.md https://github.com/name5566/leaf/blob/master/TUTORIAL_ZH.md
模块不要构建太多也不要太频繁的交互. 模块在 Leaf 中被设计出来主要是用来划分功能而非多核, Leaf 认为在模块内按需使用 goroutine 才是多核利用率问题的解决之道.
LeafServer 包含3个模块,分别是:
gate 模块, 负责游戏客户端的接入(建立连接和断开连接)
login 模块, 负责登录流程game 模块, 负责游戏主逻辑
Leaf ChanRPC
由于 Leaf 中,每个模块跑在独立的 goroutine 上,为了模块间方便的相互调用就有了基于 channel 的 RPC 机制。一个 ChanRPC 需要在游戏服务器初始化的时候进行注册(注册过程不是 goroutine 安全的),例如 LeafServer 中 game 模块注册了 NewAgent 和 CloseAgent 两个 ChanRPC.
使用 skeleton 来注册 ChanRPC. RegisterChanRPC 的第一个参数是 ChanRPC的名字, 第二个参数是 ChanRPC 的实现.
ChanRPC 的调用方法有三种:
同步, 调用并等待 ChanRPC 返回
异步, 调用并提供回调函数, 回调函数会在 ChanRPC 返回后被调用
Go, 调用并立即返回, 忽略任何返回值和错误
调用示例:
game.ChanRPC.Go("NewAgent", a) 这里调用 NewAgent 并传递参数 a, 在 game 的 rpcNewAgent 的参数 args[0] 中可以取得 a.
Leaf timer
Leaf 提供相同的 AfterFunc 函数:
skeleton.AfterFunc(5 * time.Second, func() {
// ...
})
AfterFunc 在等待参数一时长后调用 f函数,这里的 f 函数将在另一个 goroutine 中执行.
Leaf log
Leaf log 系统支持多种日志级别:
- Debug
- Release
- Error
- Fatal
在 LeafServer 中,bin/conf/server.json 可以配置日志级别,低于配置的日志级别的日志将不会输出。Fatal 日志比较特殊,每次输出 Fatal 日志之后游戏服务器进程就会结束,通常来说,只在游戏服务器初始化失败时使用 Fatal 日志。
我们还可以通过配置 LeafServer conf/conf.go 的 LogFlag 来在日志中输出文件名和行号:
LogFlag = log.Lshortfile
Leaf recordfile
Leaf 的 record file 是基于 CSV 格式. 用于管理游戏配置数据. 在 LeafServer 中使用 recordfile 非常简单:
CSV 格式如下:
数字索引 字符串索引 数字类型 字符串类型 数组类型 嵌套数组 变长数组 结构体类型 map类型
1 one 0 knife "[1, 2]" "[[0,1], [1,2], [2,3]]" "[1, 2, 3]" "{""name"": ""name5566"", ""num"": 1}" "{""key1"": 1, ""key2"": 2}"
2 two 0 cat "[3, 4]" "[[1,2], [2,3], [3,4]]" "[4, 5]" "{""name"": ""name5566"", ""num"": 2}" "{""key3"": 3, ""key4"": 4}"
3 three 0 book "[5, 6]" "[[2,3], [3,4], [4,5]]" [6] "{""name"": ""name5566"", ""num"": 3}" "{""key5"": 5, ""key6"": 6}"
- 将 CSV 文件放置在 bin/gamedata 目录中
- 在 gamedata模块 中调用函数 readRf 读取 CSV 文件
范例:
// 确保 bin/gamedata 目录中存在 Test.txt 文件
// 文件名必须和此结构体名称相同(大小写敏感)
// 结构体的一个实例映射 recordfile 中的一行
type Test struct {
// 将第一列按 int 类型解析
// "index" 表明在此列上建立唯一索引
Id int "index"
// 将第二列解析为长度为 4 的整型数组
Arr [4]int
// 将第三列解析为字符串
Str string
}
// 读取 recordfile Test.txt 到内存中
// RfTest 即为 Test.txt 的内存镜像
var RfTest = readRf(Test{})
func init() {
// 按索引查找
// 获取 Test.txt 中 Id 为 1 的那一行
r := RfTest.Index(1)
if r != nil {
row := r.(*Test)
// 输出此行的所有列的数据
log.Debug("%v %v %v", row.Id, row.Arr, row.Str)
}
}
