之所以使用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 从入坑到起飞相关推荐

  1. 启航篇——四旋翼飞行器之入坑两年心路历程和毕设总结

    笔者今年大四毕业,由于之前参加比赛及准备考研,没有时间总结这两年来做四旋翼飞行器的心得体会.现在借毕业设计这个契机,想把这件事做了,算是两年的收尾工作,也是个新的开始. 先从介绍这两年的经历开始吧.开 ...

  2. 四旋翼飞行器之入坑两年心路历程和毕设总结(转载)

    摘自:https://blog.csdn.net/weixin_36773706/article/details/89320224 用来学习,包括里面有讲到不同线程的设计,叫定时器时分复用,这个我在& ...

  3. 发布开源框架到CocoaPods入坑指南

    个人原文博客地址: 发布开源框架到CocoaPods入坑指南 在开发过程中一定会用到一些第三方框架, 只要安装了CocoaPods, 然后通过pod install命令, 就可以集成框架到项目中了 可 ...

  4. 资源 |“从蒙圈到入坑”,推荐新一波ML、DL、RL以及数学基础等干货资源

    向AI转型的程序员都关注了这个号☝☝☝ 编译 | AI科技大本营(rgznai100) 参与 | suiling 此前营长曾发过一篇高阅读量.高转发率,高收藏量的文章<爆款 | Medium上6 ...

  5. 魔兽世界多玩服务器位置,选择服务器也有大学问?新手入坑《魔兽世界》该在哪里“扎根”...

    <魔兽世界:暗影国度>开服至今已经五个多月了,圈内圈外都在讨论新版本的话题,不少萌新与老玩家都选择了在这个版本中加入探索暗影界的行列.但面对茫茫多的区服,许多玩家都犯起了"选择困 ...

  6. 2020《图像分割》从入坑到出坑指南

    本文经授权转载自机器之心(almosthuman2014),来源:medium,作者:Jakub Czakon,编译:小舟.Racoon.张倩,未经授权禁止二次转载与摘编. 本文长度为2400字,建议 ...

  7. 一份详细的“入坑Phd指南”---教你如何做笔记、整理参考书目、管理时间、如何写作、对自己和导师合理预期...

    点击上方,选择星标或置顶,每天给你送干货! 阅读大概需要5分钟 pick小博主,每天进步一丢丢 [导读]今天给大家强烈推荐一份详细的读博指南,本指南教你如何做笔记.整理参考书目.管理时间.如何写作.对 ...

  8. 干货丨从感知机到深度神经网络,带你入坑深度学习

    作者:Adi Chris 机器之心编译 参与:朱乾树.刘晓坤 机器学习工程师 Adi Chris 最近学习完吴恩达在 Coursera 上的最新课程后,决定写篇博客来记录下自己对这一领域的理解.他建议 ...

  9. python这个软件学会能做什么工作-学会Python真的有高收入?盯,请查收这份入坑指南...

    学会Python真的有高收入?盯,请查收这份入坑指南 2018-10-10 20:51:00 567点赞 6312收藏 186评论 小编注:想获得更多专属福利吗?金币加成.尊享众测.专属勋章.达人福利 ...

最新文章

  1. 逻辑斯蒂回归(logisic regression)和SVM的异同
  2. 抖音AI火了!以视频搜视频,不知小姐姐叫什么,也能搜出她的影像
  3. Django的Form表单
  4. Java实例---计算器实例
  5. linux netperf的安装
  6. 查看mysql单个表大小限制_查看单个mysql数据库中各个表的大小
  7. QT5实现摄像头预览与扑捉图像
  8. 期待人工智能在合作时的表现
  9. 安卓内录声音软件scr_录屏内录大师软件下载
  10. js三元运算符 js运算符优先级
  11. J2EE是什么(一)
  12. 走进小作坊(二十)----商道:胡雪岩叱咤商场的经营智慧
  13. Spring源码分析系列——bean创建过程分析(三)——工厂方法创建bean
  14. Axure中继器组件的使用
  15. SAS概念知识点 (复习1)
  16. 我的rpg小游戏(2)怪物设计
  17. 用matlab验证傅里叶变换的基本性质
  18. 金融危机下的中国经济(二)
  19. 拉线式电子尺|直线传感器|拉线电子尺,拉绳电子尺
  20. Latex使用ctex宏包没有隶书

热门文章

  1. 机械专业与计算机结合的论文,机械类工程师论文范文
  2. CF923D Picking Strings
  3. QGIS最受欢迎的20个插件
  4. 数据结构(C语言版)严蔚敏---图的操作的相关代码
  5. 【Android】Kotlin学习(一)
  6. 磁带和黑胶模拟器插件-Initial Audio Analog Pro LoFi v1.0.0 WiN-MAC
  7. Linux系统SPI驱动总结(一)
  8. 如果我当上技术经理如何展开工作
  9. 夕食の後のレギュラーエクスプレスについての議論(1)
  10. Bryanyzhu对比学习串烧