【Zinx應(yīng)用-MMO游戲案例-(5)構(gòu)建項(xiàng)目及用戶上線】Golang輕量級(jí)并發(fā)服務(wù)器框架

Zinx源代碼

github
https://github.com/aceld/zinx
gitee碼云
https://gitee.com/Aceld/zinx


在線開(kāi)發(fā)教程

【B站】
zinx視頻教程-Golang輕量級(jí)TCP服務(wù)器框架-適合自學(xué)者

【YouTube】
zinx開(kāi)發(fā)YouTube中國(guó)版

微信端文檔

技術(shù)資源分享.jpg

【Zinx教程目錄】
完整教程電子版(在線高清)-下載
Zinx框架視頻教程(框架篇)(完整版下載)鏈接在下面正文
Zinx框架視頻教程(應(yīng)用篇)(完整版下載)鏈接在下面正文
Zinx開(kāi)發(fā)API文檔
Zinx第一章-引言
Zinx第二章-初識(shí)Zinx框架
Zinx第三章-基礎(chǔ)路由模塊
Zinx第四章-全局配置
Zinx第五章-消息封裝
Zinx第六章-多路由模式
Zinx第七章-讀寫(xiě)分離模型
Zinx第八章-消息隊(duì)列及多任務(wù)
Zinx第九章-鏈接管理
Zinx第十章-連接屬性設(shè)置


【Zinx應(yīng)用案例-MMO多人在線游戲】
(1)案例介紹
(2)AOI興趣點(diǎn)算法
(3)數(shù)據(jù)傳輸協(xié)議protocol buffer
(4)Proto3協(xié)議定義
(5)構(gòu)建項(xiàng)目及用戶上線
(6)世界聊天
(7)上線位置信息同步
(8)移動(dòng)位置與AOI廣播
(9)玩家下線
(10)模擬客戶端AI模塊


六蓝晒、構(gòu)建項(xiàng)目與用戶上線

? 現(xiàn)在辆床,我們應(yīng)該基于Zinx框架來(lái)構(gòu)建一個(gè)MMO的游戲服務(wù)器應(yīng)用程序的項(xiàng)目了。

我們這里創(chuàng)建一個(gè)項(xiàng)目mmo_game,在項(xiàng)目?jī)?nèi)分別創(chuàng)建幾個(gè)文件夾api,conf,core,game_client,pb

6.1 構(gòu)建項(xiàng)目

api:主要是注冊(cè)一些mmo業(yè)務(wù)的一些Router處理業(yè)務(wù)死宣。

conf:存放mmo_game的一些配置文件,比如"zinx.json"奸笤。

core:存放一些核心算法惋啃,或者游戲控制等模塊。

game_client:存放游戲客戶端监右。

pb:存放一些protobuf的協(xié)議文件和go文件边灭。

1、我們?cè)?code>mmo_game下健盒,創(chuàng)建一個(gè)server.go作為我們main包绒瘦,主要作為服務(wù)器程序的主入口。

mmo_game/server.go

package main

import (
    "zinx/znet"
)

func main() {
    //創(chuàng)建服務(wù)器句柄
    s := znet.NewServer()

    //啟動(dòng)服務(wù)
    s.Serve()
}

2扣癣、在conf文件添加zinx.conf

mmo_game/conf/zinx.conf

{
  "Name":"Zinx Game",
  "Host":"0.0.0.0",
  "TcpPort":8999,
  "MaxConn":3000,
  "WorkerPoolSize":10
}

3惰帽、在pb下創(chuàng)建msg.proto文件和build.sh編譯指令腳本

mmo_game/pb/msg.proto

syntax="proto3";                //Proto協(xié)議
package pb;                     //當(dāng)前包名
option csharp_namespace="Pb";   //給C#提供的選項(xiàng)

mmo_game/pb/build.sh

#!/bin/bash
protoc --go_out=. *.proto

當(dāng)前我們的項(xiàng)目路徑應(yīng)該結(jié)構(gòu)如下:

.
└── mmo_game
    ├── api
    ├── conf
    │   └── zinx.json
    ├── core
    │   ├── aoi.go
    │   ├── aoi_test.go
    │   ├── grid.go
    ├── game_client
    │   └── client.exe
    ├── pb
    │   ├── build.sh
    │   └── msg.proto
    ├── README.md
    └── server.go

6.2用戶上線流程

? 好了,那么我們第一次就要嘗試將客戶端的MMO游戲和移動(dòng)端做一次上線測(cè)試了父虑。

我們第一個(gè)測(cè)試用戶上線的流程比較簡(jiǎn)單:


14-Zinx游戲-上線流程.jpeg

A)定義proto協(xié)議

我們從圖中可以看到该酗,上線的業(yè)務(wù)會(huì)涉及到MsgID:1 和 MsgID:200 兩個(gè)消息,根據(jù)我們上一個(gè)章節(jié)的介紹士嚎,我們需要在msg.proto中定義出兩個(gè)proto類(lèi)型呜魄,并且聲稱對(duì)應(yīng)的go代碼.

mmo_game/pb/msg.proto

syntax="proto3";                //Proto協(xié)議
package pb;                     //當(dāng)前包名
option csharp_namespace="Pb";   //給C#提供的選項(xiàng)

//同步客戶端玩家ID
message SyncPid{
    int32 Pid=1;
}

//玩家位置
message Position{
    float X=1;
    float Y=2;
    float Z=3;
    float V=4;
}

//玩家廣播數(shù)據(jù)
message BroadCast{
    int32 Pid=1;
    int32 Tp=2;
    oneof Data {
        string Content=3;
        Position P=4;
        int32 ActionData=5;
        }
}

執(zhí)行build.sh生成對(duì)應(yīng)的msg.pb.go代碼.

B)創(chuàng)建Player模塊

  1. 首先我們先創(chuàng)建一個(gè)Player玩家模塊

mmo_game/core/player.go

//玩家對(duì)象
type Player struct {
    Pid int32   //玩家ID
    Conn ziface.IConnection //當(dāng)前玩家的連接
    X   float32 //平面x坐標(biāo)
    Y   float32 //高度
    Z   float32 //平面y坐標(biāo) (注意不是Y)
    V   float32 //旋轉(zhuǎn)0-360度
}

/*
    Player ID 生成器
 */
var PidGen int32 = 1   //用來(lái)生成玩家ID的計(jì)數(shù)器
var IdLock sync.Mutex   //保護(hù)PidGen的互斥機(jī)制

//創(chuàng)建一個(gè)玩家對(duì)象
func NewPlayer(conn ziface.IConnection) *Player {
    //生成一個(gè)PID
    IdLock.Lock()
    id := PidGen
    PidGen ++
    IdLock.Unlock()

    p := &Player{
        Pid : id,
        Conn:conn,
        X:float32(160 + rand.Intn(10)),//隨機(jī)在160坐標(biāo)點(diǎn) 基于X軸偏移若干坐標(biāo)
        Y:0, //高度為0
        Z:float32(134 + rand.Intn(17)), //隨機(jī)在134坐標(biāo)點(diǎn) 基于Y軸偏移若干坐標(biāo)
        V:0, //角度為0,尚未實(shí)現(xiàn)
    }

    return p
}

Plyaer類(lèi)中有當(dāng)前玩家的ID莱衩,和當(dāng)前玩家與客戶端綁定的conn爵嗅,還有就是地圖的坐標(biāo)信,NewPlayer()提供初始化玩家方法。

  1. 由于Player經(jīng)常需要和客戶端發(fā)送消息膳殷,那么我們可以給Player提供一個(gè)SendMsg()方法操骡,供客戶端發(fā)送消息

mmo_game/core/player.go

/*
    發(fā)送消息給客戶端九火,
    主要是將pb的protobuf數(shù)據(jù)序列化之后發(fā)送
 */
func (p *Player) SendMsg(msgId uint32, data proto.Message) {
    fmt.Printf("before Marshal data = %+v\n", data)
    //將proto Message結(jié)構(gòu)體序列化
    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
    }

    //調(diào)用Zinx框架的SendMsg發(fā)包
    if err := p.Conn.SendMsg(msgId, msg); err != nil {
        fmt.Println("Player SendMsg error !")
        return
    }

    return
}

這里要注意的是,SendMsg()是將發(fā)送的數(shù)據(jù)册招,通過(guò)proto序列化岔激,然后再調(diào)用Zinx框架的SendMsg方法發(fā)送給對(duì)方客戶端.

C)實(shí)現(xiàn)上線業(yè)務(wù)

我們先在Server的main入口,給鏈接綁定一個(gè)創(chuàng)建之后的hook方法是掰,因?yàn)樯暇€的時(shí)候是服務(wù)器自動(dòng)回復(fù)客戶端玩家ID和坐標(biāo)虑鼎,那么需要我們?cè)谶B接創(chuàng)建完畢之后,自動(dòng)觸發(fā)键痛,正好我們可以利用Zinx框架的SetOnConnStart方法.

mmo_game/server.go

package main

import (
    "fmt"
    "zinx/ziface"
    "zinx/zinx_app_demo/mmo_game/core"
    "zinx/znet"
)

//當(dāng)客戶端建立連接的時(shí)候的hook函數(shù)
func OnConnecionAdd(conn ziface.IConnection)  {
    //創(chuàng)建一個(gè)玩家
    player := core.NewPlayer(conn)
    //同步當(dāng)前的PlayerID給客戶端炫彩, 走M(jìn)sgID:1 消息
    player.SyncPid()
    //同步當(dāng)前玩家的初始化坐標(biāo)信息給客戶端,走M(jìn)sgID:200消息
    player.BroadCastStartPosition()

    fmt.Println("=====> Player pidId = ", player.Pid, " arrived ====")
}


func main() {
    //創(chuàng)建服務(wù)器句柄
    s := znet.NewServer()

    //注冊(cè)客戶端連接建立和丟失函數(shù)
    s.SetOnConnStart(OnConnecionAdd)

    //啟動(dòng)服務(wù)
    s.Serve()
}

根據(jù)我們之前的流程分析絮短,那么在客戶端建立連接過(guò)來(lái)之后江兢,Server要自動(dòng)的回復(fù)給客戶端一個(gè)玩家ID,同時(shí)也要講當(dāng)前玩家的坐標(biāo)發(fā)送給客戶端丁频。所以我們這里面給Player定制了兩個(gè)方法Player.SyncPid()Player.BroadCastStartPosition()

SyncPid()則為發(fā)送MsgID:1的消息杉允,將當(dāng)前上線的用戶ID發(fā)送給客戶端

mmo_game/core/player.go

//告知客戶端pid,同步已經(jīng)生成的玩家ID給客戶端
func (p *Player) SyncPid() {
    //組建MsgId0 proto數(shù)據(jù)
    data := &pb.SyncPid{
        Pid:p.Pid,
    }

    //發(fā)送數(shù)據(jù)給客戶端
    p.SendMsg(1, data)
}

BroadCastStartPosition()則為發(fā)送MsgID:200的廣播位置消息,雖然現(xiàn)在沒(méi)有其他用戶席里,不是廣播叔磷,但是當(dāng)前玩家自己的坐標(biāo)也是要告知玩家的。

mmo_game/core/player.go

//廣播玩家自己的出生地點(diǎn)
func (p *Player) BroadCastStartPosition() {

    msg := &pb.BroadCast{
        Pid:p.Pid,
        Tp:2,//TP2 代表廣播坐標(biāo)
        Data: &pb.BroadCast_P{
            &pb.Position{
                X:p.X,
                Y:p.Y,
                Z:p.Z,
                V:p.V,
            },
        },
    }

    p.SendMsg(200, msg)
}

D)測(cè)試用戶上線業(yè)務(wù)

$cd mmo_game/
$go run server.go

啟動(dòng)服務(wù)端程序奖磁。

? 然后再windows終端打開(kāi)client.exe

注意改基,要確保windows和啟動(dòng)服務(wù)器的Linux端要能夠ping通,為了方便測(cè)試咖为,建議將Linux的防火墻設(shè)置為關(guān)閉狀態(tài)秕狰,或者要確保服務(wù)器的端口是開(kāi)放的,以免耽誤調(diào)試

15-zinx游戲案例-客戶端登錄.png

在此處輸入服務(wù)器的IP地址案疲,和服務(wù)器zinx.json配置的端口號(hào)封恰。然后點(diǎn)擊Connect。
16-zinx游戲案例-上線登錄成功.png

如果游戲界面順利進(jìn)入褐啡,并且已經(jīng)顯示為Player_1玩家ID诺舔,表示等錄成功,并且我們?cè)诜?wù)端也可以看到一些調(diào)試信息备畦。操作WASD也可以進(jìn)行玩家移動(dòng)低飒。如果沒(méi)有顯示玩家ID或者為TextView則為登錄失敗,我們需要再針對(duì)協(xié)議的匹配進(jìn)行調(diào)試懂盐。



關(guān)于作者:

作者:Aceld(劉丹冰)
簡(jiǎn)書(shū)號(hào):IT無(wú)崖子

mail: danbing.at@gmail.com
github: https://github.com/aceld
原創(chuàng)書(shū)籍gitbook: http://legacy.gitbook.com/@aceld

原創(chuàng)聲明:未經(jīng)作者允許請(qǐng)勿轉(zhuǎn)載, 如果轉(zhuǎn)載請(qǐng)注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末褥赊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子莉恼,更是在濱河造成了極大的恐慌拌喉,老刑警劉巖速那,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尿背,居然都是意外死亡端仰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)田藐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)荔烧,“玉大人,你說(shuō)我怎么就攤上這事汽久『捉撸” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵景醇,是天一觀的道長(zhǎng)臀稚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)三痰,這世上最難降的妖魔是什么烁涌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮酒觅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘微峰。我一直安慰自己舷丹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布蜓肆。 她就那樣靜靜地躺著颜凯,像睡著了一般夸赫。 火紅的嫁衣襯著肌膚如雪敬扛。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天但荤,我揣著相機(jī)與錄音早芭,去河邊找鬼彼城。 笑死,一個(gè)胖子當(dāng)著我的面吹牛退个,可吹牛的內(nèi)容都是我干的募壕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼语盈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼舱馅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起刀荒,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤代嗤,失蹤者是張志新(化名)和其女友劉穎棘钞,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體干毅,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宜猜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溶锭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宝恶。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖趴捅,靈堂內(nèi)的尸體忽然破棺而出垫毙,到底是詐尸還是另有隱情,我是刑警寧澤拱绑,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布综芥,位于F島的核電站,受9級(jí)特大地震影響猎拨,放射性物質(zhì)發(fā)生泄漏膀藐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一红省、第九天 我趴在偏房一處隱蔽的房頂上張望额各。 院中可真熱鬧,春花似錦吧恃、人聲如沸虾啦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)傲醉。三九已至,卻和暖如春呻率,著一層夾襖步出監(jiān)牢的瞬間硬毕,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工礼仗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吐咳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓元践,卻偏偏與公主長(zhǎng)得像挪丢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卢厂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359