### 6.2用戶上線流程
好了,那么我們第一次就要嘗試將客戶端的MMO游戲和移動端做一次上線測試了。
我們第一個測試用戶上線的流程比較簡單:
A\)定義proto協議
我們從圖中可以看到,上線的業務會涉及到MsgID:1 和 MsgID:200 兩個消息,根據我們上一個章節的介紹,我們需要在msg.proto中定義出兩個proto類型,并且聲稱對應的go代碼.
> mmo\_game/pb/msg.proto
```go
syntax="proto3"; //Proto協議
package pb; //當前包名
option csharp_namespace="Pb"; //給C#提供的選項
//同步客戶端玩家ID
message SyncPid{
int32 Pid=1;
}
//玩家位置
message Position{
float X=1;
float Y=2;
float Z=3;
float V=4;
}
//玩家廣播數據
message BroadCast{
int32 Pid=1;
int32 Tp=2;
oneof Data {
string Content=3;
Position P=4;
int32 ActionData=5;
}
}
```
執行build.sh生成對應的`msg.pb.go`代碼.
#### B\)創建Player模塊
1. 首先我們先創建一個Player玩家模塊
> mmo\_game/core/player.go
```go
//玩家對象
type Player struct {
Pid int32 //玩家ID
Conn ziface.IConnection //當前玩家的連接
X float32 //平面x坐標
Y float32 //高度
Z float32 //平面y坐標 (注意不是Y)
V float32 //旋轉0-360度
}
/*
Player ID 生成器
*/
var PidGen int32 = 1 //用來生成玩家ID的計數器
var IdLock sync.Mutex //保護PidGen的互斥機制
//創建一個玩家對象
func NewPlayer(conn ziface.IConnection) *Player {
//生成一個PID
IdLock.Lock()
id := PidGen
PidGen ++
IdLock.Unlock()
p := &Player{
Pid : id,
Conn:conn,
X:float32(160 + rand.Intn(10)),//隨機在160坐標點 基于X軸偏移若干坐標
Y:0, //高度為0
Z:float32(134 + rand.Intn(17)), //隨機在134坐標點 基于Y軸偏移若干坐標
V:0, //角度為0,尚未實現
}
return p
}
```
Plyaer類中有當前玩家的ID,和當前玩家與客戶端綁定的conn,還有就是地圖的坐標信,`NewPlayer()`提供初始化玩家方法。
1. 由于`Player`經常需要和客戶端發送消息,那么我們可以給`Player`提供一個`SendMsg()`方法,供客戶端發送消息
> mmo\_game/core/player.go
```go
/*
發送消息給客戶端,
主要是將pb的protobuf數據序列化之后發送
*/
func (p *Player) SendMsg(msgId uint32, data proto.Message) {
fmt.Printf("before Marshal data = %+v\n", data)
//將proto Message結構體序列化
msg, err := proto.Marshal(data)
if err != nil {
fmt.Println("marshal msg err: ", err)
return
}
fmt.Printf("after Marshal data = %+v\n", msg)
if p.Conn == nil {
fmt.Println("connection in player is nil")
return
}
//調用Zinx框架的SendMsg發包
if err := p.Conn.SendMsg(msgId, msg); err != nil {
fmt.Println("Player SendMsg error !")
return
}
return
}
```
這里要注意的是,`SendMsg()`是將發送的數據,通過proto序列化,然后再調用`Zinx`框架的SendMsg方法發送給對方客戶端.
#### C\)實現上線業務
我們先在Server的main入口,給鏈接綁定一個創建之后的hook方法,因為上線的時候是服務器自動回復客戶端玩家ID和坐標,那么需要我們在連接創建完畢之后,自動觸發,正好我們可以利用`Zinx`框架的`SetOnConnStart`方法.
> mmo\_game/server.go
```go
package main
import (
"fmt"
"zinx/ziface"
"zinx/zinx_app_demo/mmo_game/core"
"zinx/znet"
)
//當客戶端建立連接的時候的hook函數
func OnConnecionAdd(conn ziface.IConnection) {
//創建一個玩家
player := core.NewPlayer(conn)
//同步當前的PlayerID給客戶端, 走MsgID:1 消息
player.SyncPid()
//同步當前玩家的初始化坐標信息給客戶端,走MsgID:200消息
player.BroadCastStartPosition()
fmt.Println("=====> Player pidId = ", player.Pid, " arrived ====")
}
func main() {
//創建服務器句柄
s := znet.NewServer()
//注冊客戶端連接建立和丟失函數
s.SetOnConnStart(OnConnecionAdd)
//啟動服務
s.Serve()
}
```
根據我們之前的流程分析,那么在客戶端建立連接過來之后,Server要自動的回復給客戶端一個玩家ID,同時也要講當前玩家的坐標發送給客戶端。所以我們這里面給Player定制了兩個方法`Player.SyncPid()`和`Player.BroadCastStartPosition()`
`SyncPid()`則為發送`MsgID:1`的消息,將當前上線的用戶ID發送給客戶端
> mmo\_game/core/player.go
```go
//告知客戶端pid,同步已經生成的玩家ID給客戶端
func (p *Player) SyncPid() {
//組建MsgId0 proto數據
data := &pb.SyncPid{
Pid:p.Pid,
}
//發送數據給客戶端
p.SendMsg(1, data)
}
```
`BroadCastStartPosition()`則為發送`MsgID:200`的廣播位置消息,雖然現在沒有其他用戶,不是廣播,但是當前玩家自己的坐標也是要告知玩家的。
> mmo\_game/core/player.go
```go
//廣播玩家自己的出生地點
func (p *Player) BroadCastStartPosition() {
msg := &pb.BroadCast{
Pid:p.Pid,
Tp:2,//TP2 代表廣播坐標
Data: &pb.BroadCast_P{
&pb.Position{
X:p.X,
Y:p.Y,
Z:p.Z,
V:p.V,
},
},
}
p.SendMsg(200, msg)
}
```
#### D\)測試用戶上線業務
```bash
$cd mmo_game/
$go run server.go
```
啟動服務端程序。
然后再windows終端打開`client.exe`
> 注意,要確保windows和啟動服務器的Linux端要能夠ping通,為了方便測試,建議將Linux的防火墻設置為關閉狀態,或者要確保服務器的端口是開放的,以免耽誤調試

在此處輸入服務器的IP地址,和服務器`zinx.json`配置的端口號。然后點擊Connect。
如果游戲界面順利進入,并且已經顯示為`Player_1`玩家ID,表示等錄成功,并且我們在服務端也可以看到一些調試信息。操作WASD也可以進行玩家移動。如果沒有顯示玩家ID或者為`TextView`則為登錄失敗,我們需要再針對協議的匹配進行調試。
- 一、引言
- 1、寫在前面
- 2、初探Zinx架構
- 二、初識Zinx框架
- 1. Zinx-V0.1-基礎Server
- 2.Zinx-V0.2-簡單的連接封裝與業務綁定
- 三、Zinx框架基礎路由模塊
- 3.1 IRequest 消息請求抽象類
- 3.2 IRouter 路由配置抽象類
- 3.3 Zinx-V0.3-集成簡單路由功能
- 3.4 Zinx-V0.3代碼實現
- 3.5 使用Zinx-V0.3完成應用程序
- 四、Zinx的全局配置
- 4.1 Zinx-V0.4增添全局配置代碼實現
- 4.2 使用Zinx-V0.4完成應用程序
- 五、Zinx的消息封裝
- 5.1 創建消息封裝類型
- 5.2 消息的封包與拆包
- 5.3 Zinx-V0.5代碼實現
- 5.4 使用Zinx-V0.5完成應用程序
- 六、Zinx的多路由模式
- 6.1 創建消息管理模塊
- 6.2 Zinx-V0.6代碼實現
- 6.3 使用Zinx-V0.6完成應用程序
- 七、Zinx的讀寫分離模型
- 7.1 Zinx-V0.7代碼實現
- 7.2 使用Zinx-V0.7完成應用程序
- 八、Zinx的消息隊列及多任務機制
- 8.1 創建消息隊列
- 8.2 創建及啟動Worker工作池
- 8.3 發送消息給消息隊列
- 8.4 Zinx-V0.8代碼實現
- 8.5 使用Zinx-V0.8完成應用程序
- 九、Zinx的鏈接管理
- 9.1 創建鏈接管理模塊
- 9.2 鏈接管理模塊集成到Zinx中
- 9.3 鏈接的帶緩沖的發包方法
- 9.4 注冊鏈接啟動/停止自定義Hook方法功能
- 9.5 使用Zinx-V0.9完成應用程序
- 十、Zinx的連接屬性設置
- 10.1 給鏈接添加鏈接配置接口
- 10.2 鏈接屬性方法實現
- 10.3 鏈接屬性Zinx-V0.10單元測試
- 基于Zinx的應用案例
- 一、應用案例介紹
- 二、服務器應用基礎協議
- 三、MMO多人在線游戲AOI算法
- 3.1 網絡法實現AOI算法
- 3.2 實現AOI格子結構
- 3.3 實現AOI管理模塊
- 3.4 求出九宮格
- 3.5 AOI格子添加刪除操作
- 3.6 AOI模塊單元測試
- 四、數據傳輸協議protocol buffer
- 4.1 簡介
- 4.2 數據交換格式
- 4.3 protobuf環境安裝
- 4.4 protobuf語法
- 4.5 編譯protobuf
- 4.6 利用protobuf生成的類來編碼
- 五、MMO游戲的Proto3協議
- 六、構建項目與用戶上線
- 6.1 構建項目
- 6.2用戶上線流程
- 七、世界聊天系統實現
- 7.1 世界管理模塊
- 7.2 世界聊天系統實現
- 八、上線位置信息同步
- 九、移動位置與AOI廣播(未跨越格子)
- 十、玩家下線
- 十一、移動與AOI廣播(跨越格子)