• 6.2用户上线流程
    • B)创建Player模块
    • C)实现上线业务
    • D)测试用户上线业务

    6.2用户上线流程

    好了,那么我们第一次就要尝试将客户端的MMO游戏和移动端做一次上线测试了。

    我们第一个测试用户上线的流程比较简单:

     6.2用户上线流程  - 图1A)定义proto协议

    我们从图中可以看到,上线的业务会涉及到MsgID:1 和 MsgID:200 两个消息,根据我们上一个章节的介绍,我们需要在msg.proto中定义出两个proto类型,并且声称对应的go代码.

    mmo_game/pb/msg.proto

    1. syntax="proto3"; //Proto协议
    2. package pb; //当前包名
    3. option csharp_namespace="Pb"; //给C#提供的选项
    4. //同步客户端玩家ID
    5. message SyncPid{
    6. int32 Pid=1;
    7. }
    8. //玩家位置
    9. message Position{
    10. float X=1;
    11. float Y=2;
    12. float Z=3;
    13. float V=4;
    14. }
    15. //玩家广播数据
    16. message BroadCast{
    17. int32 Pid=1;
    18. int32 Tp=2;
    19. oneof Data {
    20. string Content=3;
    21. Position P=4;
    22. int32 ActionData=5;
    23. }
    24. }

    执行build.sh生成对应的msg.pb.go代码.

    B)创建Player模块

    • 首先我们先创建一个Player玩家模块

    mmo_game/core/player.go

    1. //玩家对象
    2. type Player struct {
    3. Pid int32 //玩家ID
    4. Conn ziface.IConnection //当前玩家的连接
    5. X float32 //平面x坐标
    6. Y float32 //高度
    7. Z float32 //平面y坐标 (注意不是Y)
    8. V float32 //旋转0-360度
    9. }
    10. /*
    11. Player ID 生成器
    12. */
    13. var PidGen int32 = 1 //用来生成玩家ID的计数器
    14. var IdLock sync.Mutex //保护PidGen的互斥机制
    15. //创建一个玩家对象
    16. func NewPlayer(conn ziface.IConnection) *Player {
    17. //生成一个PID
    18. IdLock.Lock()
    19. id := PidGen
    20. PidGen ++
    21. IdLock.Unlock()
    22. p := &Player{
    23. Pid : id,
    24. Conn:conn,
    25. X:float32(160 + rand.Intn(10)),//随机在160坐标点 基于X轴偏移若干坐标
    26. Y:0, //高度为0
    27. Z:float32(134 + rand.Intn(17)), //随机在134坐标点 基于Y轴偏移若干坐标
    28. V:0, //角度为0,尚未实现
    29. }
    30. return p
    31. }

    Plyaer类中有当前玩家的ID,和当前玩家与客户端绑定的conn,还有就是地图的坐标信,NewPlayer()提供初始化玩家方法。

    • 由于Player经常需要和客户端发送消息,那么我们可以给Player提供一个SendMsg()方法,供客户端发送消息

    mmo_game/core/player.go

    1. /*
    2. 发送消息给客户端,
    3. 主要是将pb的protobuf数据序列化之后发送
    4. */
    5. func (p *Player) SendMsg(msgId uint32, data proto.Message) {
    6. fmt.Printf("before Marshal data = %+v\n", data)
    7. //将proto Message结构体序列化
    8. msg, err := proto.Marshal(data)
    9. if err != nil {
    10. fmt.Println("marshal msg err: ", err)
    11. return
    12. }
    13. fmt.Printf("after Marshal data = %+v\n", msg)
    14. if p.Conn == nil {
    15. fmt.Println("connection in player is nil")
    16. return
    17. }
    18. //调用Zinx框架的SendMsg发包
    19. if err := p.Conn.SendMsg(msgId, msg); err != nil {
    20. fmt.Println("Player SendMsg error !")
    21. return
    22. }
    23. return
    24. }

    这里要注意的是,SendMsg()是将发送的数据,通过proto序列化,然后再调用Zinx框架的SendMsg方法发送给对方客户端.

    C)实现上线业务

    我们先在Server的main入口,给链接绑定一个创建之后的hook方法,因为上线的时候是服务器自动回复客户端玩家ID和坐标,那么需要我们在连接创建完毕之后,自动触发,正好我们可以利用Zinx框架的SetOnConnStart方法.

    mmo_game/server.go

    1. package main
    2. import (
    3. "fmt"
    4. "zinx/ziface"
    5. "zinx/zinx_app_demo/mmo_game/core"
    6. "zinx/znet"
    7. )
    8. //当客户端建立连接的时候的hook函数
    9. func OnConnecionAdd(conn ziface.IConnection) {
    10. //创建一个玩家
    11. player := core.NewPlayer(conn)
    12. //同步当前的PlayerID给客户端, 走MsgID:1 消息
    13. player.SyncPid()
    14. //同步当前玩家的初始化坐标信息给客户端,走MsgID:200消息
    15. player.BroadCastStartPosition()
    16. fmt.Println("=====> Player pidId = ", player.Pid, " arrived ====")
    17. }
    18. func main() {
    19. //创建服务器句柄
    20. s := znet.NewServer()
    21. //注册客户端连接建立和丢失函数
    22. s.SetOnConnStart(OnConnecionAdd)
    23. //启动服务
    24. s.Serve()
    25. }

    根据我们之前的流程分析,那么在客户端建立连接过来之后,Server要自动的回复给客户端一个玩家ID,同时也要讲当前玩家的坐标发送给客户端。所以我们这里面给Player定制了两个方法Player.SyncPid()Player.BroadCastStartPosition()

    SyncPid()则为发送MsgID:1的消息,将当前上线的用户ID发送给客户端

    mmo_game/core/player.go

    1. //告知客户端pid,同步已经生成的玩家ID给客户端
    2. func (p *Player) SyncPid() {
    3. //组建MsgId0 proto数据
    4. data := &pb.SyncPid{
    5. Pid:p.Pid,
    6. }
    7. //发送数据给客户端
    8. p.SendMsg(1, data)
    9. }

    BroadCastStartPosition()则为发送MsgID:200的广播位置消息,虽然现在没有其他用户,不是广播,但是当前玩家自己的坐标也是要告知玩家的。

    mmo_game/core/player.go

    1. //广播玩家自己的出生地点
    2. func (p *Player) BroadCastStartPosition() {
    3. msg := &pb.BroadCast{
    4. Pid:p.Pid,
    5. Tp:2,//TP2 代表广播坐标
    6. Data: &pb.BroadCast_P{
    7. &pb.Position{
    8. X:p.X,
    9. Y:p.Y,
    10. Z:p.Z,
    11. V:p.V,
    12. },
    13. },
    14. }
    15. p.SendMsg(200, msg)
    16. }

    D)测试用户上线业务

    1. $cd mmo_game/
    2. $go run server.go

    启动服务端程序。

    然后再windows终端打开client.exe

    注意,要确保windows和启动服务器的Linux端要能够ping通,为了方便测试,建议将Linux的防火墙设置为关闭状态,或者要确保服务器的端口是开放的,以免耽误调试

     6.2用户上线流程  - 图2

    在此处输入服务器的IP地址,和服务器zinx.json配置的端口号。然后点击Connect。

     6.2用户上线流程  - 图3如果游戏界面顺利进入,并且已经显示为Player_1玩家ID,表示等录成功,并且我们在服务端也可以看到一些调试信息。操作WASD也可以进行玩家移动。如果没有显示玩家ID或者为TextView则为登录失败,我们需要再针对协议的匹配进行调试。