Project Purpose:
- Develop an interactive mini-game, limited to service-side experience less, so began to learn the leaf framework, the client with Cocos Creator.
- This kind of learning case on the network is less, so want to learn, while sharing to the post-scholar, I would like to encourage myself!
Environment Construction:
Golang Environment Building and Cocos Creator Environment Building online tutorial a lot, no longer repeat, Golang IDE can use Goland.
Leaf Frame Address: https://github.com/name5566/leaf.git
Leaf Getting Started Tutorial: https://github.com/name5566/leaf/blob/master/TUTORIAL_ZH.md
Example Gihub Address:
Server:https://github.com/ddkgo/leafserverexample.git
Client:https://github.com/ddkgo/leafservercocosclient.git
Leafserverexample Construction:
Get:
git clone https://github.com/ddkgo/LeafServerExample.git
Set the Leafserverexample directory to the GOPATH environment variable to get the Leaf:
go get github.com/name5566/leaf
Get PROTOBUF Support:
go get github.com/golang/protobuf/proto
Body:
The server receives and processes messages:
1. Create a Msgpro.proto:
syntax = "proto3";package msg;message Hello { int32 id = 1; string name = 2;}
Compile the Msgpro.proto file (not known about this?) Please read the article "using Protobuf in Golang") to get the MsgPro.pb.go file with the following command:
protoc --go_out=. msgPro.proto
Place the MsgPro.pb.go under the Leafserverexample src/msg folder.
2. Edit the Msg.go file:
package msgimport ( "github.com/name5566/leaf/network/protobuf")// 使用 Protobuf 消息处理器var Processor = protobuf.NewProcessor()func init() { Processor.Register(&Hello{})}
3. Next process the routing of the Hello message:
Route the Hello message to the game module. Open Leafserverexample gate/router.go, typing the following code:
package gateimport ( "server/msg" "server/game")func init() { // 这里指定消息 Hello 路由到 game 模块 msg.Processor.SetRouter(&msg.Hello{}, game.ChanRPC)}
4. Processing messages:
The Hello message is processed in the game module. Open Leafserverexample game/internal/handler.go, typing the following code:
package internalimport ( "server/msg" "reflect" "github.com/name5566/leaf/gate" "github.com/name5566/leaf/log" "github.com/golang/protobuf/proto")func init() { // 向当前模块(game 模块)注册 Hello 消息的消息处理函数 handleHello handler(&msg.Hello{}, handleHello)}func handler(m interface{}, h interface{}) { skeleton.RegisterChanRPC(reflect.TypeOf(m), h)}func handleHello(args []interface{}) { // 收到的 Hello 消息 m := args[0].(*msg.Hello) // 消息的发送者 a := args[1].(gate.Agent) // 输出收到的消息的内容 log.Debug("hello %v", m.GetName()) retBuf :=&msg.Hello{ Name: *proto.String("client"), } // 给发送者回应一个 Hello 消息 a.WriteMsg(retBuf)}
Client receives and processes messages:
Get Protobufjs, under the Leafservercocosclient directory:
npm install protobufjs
1.proto compiled into a static file:
Copy the Msgpro.proto into the Leafservercocosclient Node_modules.bin folder and compile the proto file into a static file using:
pbjs -t static-module -w commonjs -o protocol.js msgPro.protopbts -o protocol.d.ts protocol.js
Copy the Protocol.js and protocol.d.ts to the Leafservercocosclient Assets\script\protocol folder.
2. Create the WebSocket and connect:
New Netcontrol class:
const {Ccclass, property} = cc._decorator;//defines the global variable import * as Onfire from "./libs/onfire/onfire.js"; Class library for handling events import {Netconfig} from './netconfig ' @ccclassexport class Netcontrol extends CC. Component {Private _sock:websocket = NULL//The object of the current WebSocket connect () {if (This._sock ==null | | this._so Ck.readystate!==1) {//Current interface not open/reconnect This._sock = new WebSocket (netconfig.host+ ":" +netco Nfig.port) This._sock.onopen = This._onopen.bind (this); This._sock.onclose = This._onclose.bind (this); This._sock.onmessage = This._onmessage.bind (this); } return this; } _onopen () {Onfire.fire ("OnOpen")} _onclose (Err) {Onfire.fire ("OnClose", err) let self = th IS-let Revar = setinterval (function () {/////WebSocket after the re-connection is determined, and if the re-connection succeeds, the loop if is broken (self._sock.read Ystate = = 1) {clearinterval (revar)} Self._sock = newWebSocket (netconfig.host+ ":" +netconfig.port)}, 5000)//every 5 seconds try to re-connect} _onmessage (obj) {onfire.fire ("O Nmessage ", obj)} send (msg) {if (this._sock.readystate = = 1) {this._sock.send (msg); }} protobufaddtag (Tag:number,buffer:uint8array) {Let addtag_buffer=new Uint8array (buffer.length+2) Let tagbinary = This.binary (tag,2) addtag_buffer.set (tagbinary,0) Addtag_buffer.set (Buffer.subarray (0,buffer . length), 2) return Addtag_buffer} parseprotobufid (obj,callback:function) {Let Blob:blob = Obj.data Let reader = new FileReader (); Reader.readasarraybuffer (BLOB); Reader.onload = function (e) {Let unit16 = new Uint16array (e.target.result) Let id = unit16[0] Console.log ("Receive Message ID =" +id) Let Dataunit8array = new Uint8array (e.target.result) d Ataunit8array = Dataunit8array.slice (2) Callback (iD,dataunit8array)}} binary (Num:number, bits:number) {Let Resarry = [] Let Xresarry = [] Let i=0; for (;num>0;) {resarry.push (num% 2) NUM=NUM/2 i++; } for (Let j=i-1;j>=0;j--) Xresarry.push (Resarry[j]) if (Bits < xresarry.length) { Console.log ("Number of bits less than bits")} if (bits) {for (Let R = Xresarry.length; r < Bits; r++) { Xresarry.unshift (0)}}//return Xresarry.join (). Replace (/,/g, '); Return Xresarry}}
Because in the Leaf, the default Protobuf Processor defines a complete PROTOBUF message in the following format:
-------------------------| id | protobuf message |-------------------------
So you need to add a header ID when sending a message:
sendHello(name : string){ let protocolId = 0 let message = msg.Hello.create({ id:0,name:name }) let buffer = msg.Hello.encode(message).finish() //leaf 前两位为协议序号,故需包装一下 let addtag_buffer = this.netControl.protoBufAddtag(protocolId,buffer) this.netControl.send(addtag_buffer); console.log("sendToWS");}
When the message returned by the leaf is received:
onMessage(obj){ //leaf 前两位为协议序号,需要解一下啊协议序号 this.netControl.parseProtoBufId(obj,this.OnGameMessage.bind(this)) }
The same top two is the leaf auto-plus ID that needs to be processed:
parseProtoBufId(obj,callback:Function){ let blob:Blob = obj.data let reader = new FileReader(); reader.readAsArrayBuffer(blob); reader.onload = function(e) { let unit16 = new Uint16Array(e.target.result) let id = unit16[0] console.log("receive message id = "+id) let dataUnit8Array = new Uint8Array(e.target.result) dataUnit8Array = dataUnit8Array.slice(2) callback(id,dataUnit8Array) } }
Specific HelloWorld class:
Import {Netcontrol} from './netcontrol ' import * as Onfire from './libs/onfire/onfire ' import {msg} from './protocol/prot Ocol.js "Const {Ccclass, property} = Cc._decorator@ccclassexport Default class Helloworld extends CC. Component {@property (cc). Label) label:cc. Label = null; @property text:string = ' Hello '; Private Msssagefire Private Netcontrol:netcontrol = null private proto OnLoad () {This.netcontrol = new net Control ()} start () {//init logic this.label.string = This.text; This.netControl.connect (); This.msssagefire=onfire.on ("OnMessage", This.onMessage.bind (This))} Onbtnsendhello () {This.sendhello ("DDK") } Sendhello (name:string) {Let ProtocolId = 0 let message = Msg. Hello.create ({id:0,name:name}) let buffer = Msg. Hello.encode (Message). Finish ()//leaf the first two bits is the protocol number, so you need to pack let Addtag_buffer = This.netControl.protoBufAddtag (pro Tocolid,buffer) this.netcOntrol.send (Addtag_buffer); Console.log ("Sendtows"); } onMessage (obj) {//leaf The first two bits is the protocol number, need to solve the protocol sequence number This.netControl.parseProtoBufId (Obj,this. Ongamemessage.bind (This)}} ongamemessage (Id:number,data:uint8array) {if (id===0) {Console.log ( "Get Hello message!"); Let gamemsg = Msg. Hello.decode (data) Console.log (GAMEMSG)}} OnDestroy () {onfire.un (this.msssagefire); }}
:
Server
Client
Goland Build Setting:
Reference:
Using Protobuf in the Leaf