go leaf 从入坑到起飞
之所以使用go leaf是因为其轻便,开发效率高不高,都是看个人的,好不好用,也是看个人的,咱们不予以置评,开始干活。
关于go leaf的下载
GitHub - name5566/leaf: A game server framework in Go (golang)
框架介绍
go leaf的框架介绍,网上可以搜索,这里跳过.
重要的事情,不妨多讲几遍。以下是个人想强调的。
首先go leaf里有样东西特别重要,这样东西叫:Module。
而这个Module是个interface类型,包含以下三种行为
OnInit() //初始化
OnDestroy() //销毁
Run(closeSig chan bool) //运行,接收关闭信号
而用户可以定义这些Module,它继承于leaf框架 *module.Skeleton,也表明其是依赖Skeleton配置信息,且支持模块间通信的。
// 自定义的Module 继承于module.Skeleton
type Module struct {*module.Skeleton
}
最后这些Module,需交由leaf去执行。也就是在main.go中被leaf.Run遍历执行的。
leaf.Run(gate.Module,//路由login.Module,//登录game.Module,//游戏)
刚开始使用时,有人会好奇: 这么多模块, 模块之间是怎么协同运作的呢?
关键在于,总要有其中一个模块站出来为人民服务,大家才能和睦共处呀。
于是乎,gate站了出来,它的工作非常单一,就是分派协议。
例如:
前提有一份login.proto的协议文件//login.proto文件
//注册请求
message RegisterReq{string Name = 1; //用户string Password = 2; //密码
}
//注册成功的结果反馈
message RegisterResp{uint32 Success = 1; //0:失败 1:成功
}//msg.go文件 主要是注册协议用的
import ("github.com/golang/protobuf/proto"//"github.com/name5566/leaf/network/json""github.com/name5566/leaf/network/protobuf"protoMsg "server/msg/go" //protobuff的go代码
)
var ProcessorProto = protobuf.NewProcessor()
func init() {//将login.proto文件中的协议注册进来ProcessorProto.Register(&protoMsg.RegisterReq{})ProcessorProto.Register(&protoMsg.RegisterResp{})
}//gate.go文件 主要是派发协议
func init() {//派发至login包内的ChanRPC进行处理msg.ProcessorProto.SetRouter(&protobuf.RegisterReq{}, login.ChanRPC)msg.ProcessorProto.SetRouter(&protobuf.RegisterRsp{}, login.ChanRPC)
}//login包下面的external.go文件 ChanRPC的定义,其实它是来源于子包internal的ChanRPC
//这个协程是一个skeleton.ChanRPCServer,顾名思义就是RPC服务的的协程
var (Module = new(internal.Module)ChanRPC = internal.ChanRPC
)那么这个协程里到底是要执行什么,怎么去执行呢?
执行什么由用户说了算,于是在login的子包internal下新建一个处理文件handler.go// internal下新建handler.go
func init() {// handleRegister是服务端对接收到的RegisterReq协议后的处理函数handleMsg(&protoMsg.RegisterReq{}, handleRegister)
}
func handleMsg(m interface{}, h interface{}) {skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
}
//注册请求的处理
func handleRegister(args []interface{}) {m := args[0].(*protoMsg.RegisterReq)a := args[1].(gate.Agent)result := .... //略过 (数据库查下该用户是否存在作为是否注册成功的依据)// 反馈注册结果a.WriteMsg(&protoMsg.RegisterResp{Success: result,})
}
到此,逻辑梳理结束
而在gate的模块初始化时,需要注意的是AgentChanRPC,因为在底层leaf是会触发两个ChanPRC:"NewAgent"和"CloseAgent"
func (m *Module) OnInit() {m.Gate = &gate.Gate{MaxConnNum: conf.Server.MaxConnNum,PendingWriteNum: conf.PendingWriteNum,MaxMsgLen: conf.MaxMsgLen,WSAddr: conf.Server.WSAddr,HTTPTimeout: conf.HTTPTimeout,CertFile: conf.Server.CertFile,KeyFile: conf.Server.KeyFile,TCPAddr: conf.Server.TCPAddr,LenMsgLen: conf.LenMsgLen,LittleEndian: conf.LittleEndian,Processor: msg.ProcessorProto, //消息处理器对象(proto|json)AgentChanRPC: game.ChanRPC,//包含agent的一个chan}}
而如何处理呢,是由game.ChanRPC来处理。(game的chanrpc.go)
func init() {skeleton.RegisterChanRPC("NewAgent", rpcNewAgent)skeleton.RegisterChanRPC("CloseAgent", rpcCloseAgent)skeleton.RegisterChanRPC("OffLine", rpcOfflineAgent)//清场//AsyncChan.Register("clearUp", func(args []interface{}) {//// log.Debug("clearUp:%v", args)// //table := args[0].(*Table)////}) // 广播消息 调用参考:game.ChanRPC.Go("Broadcast", agent, args)
}func rpcNewAgent(args []interface{}) {a := args[0].(gate.Agent) //【模块间通信】跟路由之间的通信//GetClientManger().Append(INVALID, a)_ = a}
所以,go leaf的大体流程就出来了。
【协议派发】- 【绑定处理接口】- 【在接口内实现逻辑】
msg.ProcessorProto.SetRouter(&protoMsg.Register{}, login.ChanRPC)
skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
func h(args []interface{}){
....//
}
开始实战:
...
以下仅是个人实战所用
障碍一:go leaf如何使用protobuf??
我这边使用的是protobuffer的3.7.0版本,
第一步:咱们在msg目录下,创建一个proto子目录,并添加一个login.proto文件。
syntax = "proto3";
package go;/info//
//个人信息
message UserInfo{uint64 UserID = 1; //IDstring Name = 2; //用户string Account = 3; //帐号string Password = 4; //密码uint32 FaceID = 5; //头像uint32 Gender = 6; //性别uint32 Age = 7; //年龄uint32 VIP = 8; //VIP级别uint32 Level = 9; //级别int64 Money = 10; //金钱(余额)string PassPortID = 11; //证件号string RealName = 12; //真实名字string PhoneNum = 13; //手机string Email = 14; //邮箱string Address = 15; //住址string Identity = 16; //识别码(平台生成)uint64 AgentID = 17; //代理标识(上级代理人)string ReferralCode = 18; //推荐标识(推荐码,由邀请码生成)string ClientAddr = 19; //连接地址(当前实际IP)string ServerAddr = 20; //(跳转至该地址 由登录服务返回的真实服务器地址)string MachineCode = 21; //机器序列
}
//注册
message RegisterReq{string Name = 1; //用户string Password = 2; //密码string SecurityCode = 3; //验证码string MachineCode = 4; //机器码string InvitationCode = 5; //邀请码uint64 PlatformID = 6; //需要注明平台ID (测试用: id == 1)//选填uint32 Gender = 7; //性别uint32 Age = 8; //年龄uint32 FaceID = 9; //头像string PassPortID = 10; //证件号string RealName = 11; //真实名字string PhoneNum = 12; //手机string Email = 13; //邮箱string Address = 14; //住址
}
message RegisterResp{UserInfo Info = 1;
}
第二步:将*.proto文件转成*.go文件。
#写一个批处理专门将proto目录下的文件 转成 go文件。
syntax = "proto3";
package go;
在与main.go同级目录下,新建一个tools目录存放脚本 以下是生成proto转go文件的脚本 其他python文件可以略过。
@echo OFF
chcp 65001
@echo "-----------fix package name(本地化)------------------"
rem py .\amend.py
timeout 1
md ..\msg\go
@echo "-----------Proto-file(待处理)------------------"
echo _generate.bat path : %~dp0
rem dir %~dp0\..\msg\proto\*.proto /B > list.txt
REM '待处理的Proto文件'
for /f %%a in (list.txt) do (
echo 正在转换 %%a
protoc -I=%~dp0\..\msg\proto\ --go_out=..\msg\go %%a
echo 忙碌中...
)@echo "------------Go-file(已生成)--------------------"
for /R "..\msg\go" %%s in (*.go) do (@echo "creating->file:%%s")@echo "------------c++代码(协议注册)--------------------"
rem py .\convertCpp.py@echo "------------若无操作 3秒后自动退出--------------------"
timeout 3
Exit
第三步:已经有了go文件,接下来就是将协议注册到protobuf的解析器当中。咱们想法独特,所以就在msg.go里做文章
package msgimport ("github.com/golang/protobuf/proto""github.com/name5566/leaf/network/json""github.com/name5566/leaf/network/protobuf"protoMsg "server/msg/go""sync"
)// 使用默认的 JSON 消息处理器(默认还提供了 protobuf 消息处理器)
var ProcessorProto = protobuf.NewProcessor()func init() {//这里的注册顺序,必须,必须,必须与【客户端】一致RegisterMessage(&protoMsg.PacketData{})}//对外接口 【这里的注册函数并非线程安全】
func RegisterMessage(message proto.Message) {ProcessorProto.Register(message)//log.Debug("reg ID:%v",ProcessorProto.Register(message))
}
第四步,在gate/router.go里去指派需要处理的协议消息。 需要处理的协议,是指由客户端主动发起的协议。
package gateimport ("server/login""server/msg"protoMsg "server/msg/go"
)//路由模块分发消息【模块间使用 ChanRPC 通讯,消息路由也不例外】
//注:需要解析的结构体才进行路由分派,即用客户端主动发起的
func init() {//派给login模块进行处理msg.ProcessorProto.SetRouter(&protoMsg.PacketData{}, login.ChanRPC) //[proto]
}
第五步:在指定的模块内处理消息。
//login/internal/handler.go
package internalimport ("github.com/golang/protobuf/proto""github.com/name5566/leaf/gate""reflect". "server/base"protoMsg "server/msg/go"
)func init() {// 向当前模块(login 模块)注册 Login 消息的消息处理函数 handleTestregister(&protoMsg.PacketData{}, handleMsg) //反馈--->用户信息(由客户端反馈过来的)
}//注册模块间的通信
func register(m interface{}, h interface{}) {skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
}//处理消息
func handleMsg(args []interface{}) {m := args[0].(*protoMsg.PacketData)//a := args[1].(gate.Agent)log.Debug("msg: %v psw:%v", m.GetMainID(), m.GetSubID())}
protobuf到此,流程走通。
障碍二:如何接受或发送字节,针对客户端。 因为go leaf在说明文档里,已经清楚说明了,自己是怎样发送protobuf的字节的。
//以go语言作为客户端
//封装消息
func packageMsg(message proto.Message) []byte{//data, _ := msg.ProcessorProto.Marshal(message)//...此处可先进行数据加密// len +id + datam := make([]byte, 4+len(data[1]))// 默认使用大端序binary.BigEndian.PutUint16(m, uint16(2+len(data[1])))//两个字节+数据copy(m[2:], data[0])copy(m[4:], data[1])return m
}
障碍三:protobuf 协议文件 更新后,导致客户端必须强制同步更新的问题?
由于go leaf的 消息注册机制 是根据proto文件里的message书写顺序,自动生成的。一旦生成,即会给消息体分配固定ID。
即message a{} 一旦在msg.go的
ProcessorProto.Register(message)//此处的返回值就是消息的ID
注册了,则底层的ID便固定下来,不容更改了。
所以,废弃的message一旦正式版发布后,不容废弃,最多把message的字段给删除。而且,新增的message必须以追加形式放在init()末。
msg.go里的
init(//'''//'''
RegisterMessage(&protoMsg.GameOverResp{})//新增
)
这样会引起文件体积膨胀。
如需彻底解决此问题,可修改注册函数为Register(id int32, msg message),提供主动设置msgID的接口。做一张映射表,记录所有历史的消息ID。新增时,在历史最大值中加一,并注册到ProcessorProto。
接下来,就是各位大侠按照业务逻辑实现需求了。
基本思路就是
1、接口类(如对战类、百人、益智类等等)
2、实现管理类(如客户端连接管理、玩家管理、子游戏管理等等)
3、实现数据库功能(用户注册、金币增减等等)
待更新...
如有疑问,欢迎留言。
go leaf 从入坑到起飞相关推荐
- 启航篇——四旋翼飞行器之入坑两年心路历程和毕设总结
笔者今年大四毕业,由于之前参加比赛及准备考研,没有时间总结这两年来做四旋翼飞行器的心得体会.现在借毕业设计这个契机,想把这件事做了,算是两年的收尾工作,也是个新的开始. 先从介绍这两年的经历开始吧.开 ...
- 四旋翼飞行器之入坑两年心路历程和毕设总结(转载)
摘自:https://blog.csdn.net/weixin_36773706/article/details/89320224 用来学习,包括里面有讲到不同线程的设计,叫定时器时分复用,这个我在& ...
- 发布开源框架到CocoaPods入坑指南
个人原文博客地址: 发布开源框架到CocoaPods入坑指南 在开发过程中一定会用到一些第三方框架, 只要安装了CocoaPods, 然后通过pod install命令, 就可以集成框架到项目中了 可 ...
- 资源 |“从蒙圈到入坑”,推荐新一波ML、DL、RL以及数学基础等干货资源
向AI转型的程序员都关注了这个号☝☝☝ 编译 | AI科技大本营(rgznai100) 参与 | suiling 此前营长曾发过一篇高阅读量.高转发率,高收藏量的文章<爆款 | Medium上6 ...
- 魔兽世界多玩服务器位置,选择服务器也有大学问?新手入坑《魔兽世界》该在哪里“扎根”...
<魔兽世界:暗影国度>开服至今已经五个多月了,圈内圈外都在讨论新版本的话题,不少萌新与老玩家都选择了在这个版本中加入探索暗影界的行列.但面对茫茫多的区服,许多玩家都犯起了"选择困 ...
- 2020《图像分割》从入坑到出坑指南
本文经授权转载自机器之心(almosthuman2014),来源:medium,作者:Jakub Czakon,编译:小舟.Racoon.张倩,未经授权禁止二次转载与摘编. 本文长度为2400字,建议 ...
- 一份详细的“入坑Phd指南”---教你如何做笔记、整理参考书目、管理时间、如何写作、对自己和导师合理预期...
点击上方,选择星标或置顶,每天给你送干货! 阅读大概需要5分钟 pick小博主,每天进步一丢丢 [导读]今天给大家强烈推荐一份详细的读博指南,本指南教你如何做笔记.整理参考书目.管理时间.如何写作.对 ...
- 干货丨从感知机到深度神经网络,带你入坑深度学习
作者:Adi Chris 机器之心编译 参与:朱乾树.刘晓坤 机器学习工程师 Adi Chris 最近学习完吴恩达在 Coursera 上的最新课程后,决定写篇博客来记录下自己对这一领域的理解.他建议 ...
- python这个软件学会能做什么工作-学会Python真的有高收入?盯,请查收这份入坑指南...
学会Python真的有高收入?盯,请查收这份入坑指南 2018-10-10 20:51:00 567点赞 6312收藏 186评论 小编注:想获得更多专属福利吗?金币加成.尊享众测.专属勋章.达人福利 ...
最新文章
- 逻辑斯蒂回归(logisic regression)和SVM的异同
- 抖音AI火了!以视频搜视频,不知小姐姐叫什么,也能搜出她的影像
- Django的Form表单
- Java实例---计算器实例
- linux netperf的安装
- 查看mysql单个表大小限制_查看单个mysql数据库中各个表的大小
- QT5实现摄像头预览与扑捉图像
- 期待人工智能在合作时的表现
- 安卓内录声音软件scr_录屏内录大师软件下载
- js三元运算符 js运算符优先级
- J2EE是什么(一)
- 走进小作坊(二十)----商道:胡雪岩叱咤商场的经营智慧
- Spring源码分析系列——bean创建过程分析(三)——工厂方法创建bean
- Axure中继器组件的使用
- SAS概念知识点 (复习1)
- 我的rpg小游戏(2)怪物设计
- 用matlab验证傅里叶变换的基本性质
- 金融危机下的中国经济(二)
- 拉线式电子尺|直线传感器|拉线电子尺,拉绳电子尺
- Latex使用ctex宏包没有隶书