最近用golang写斗地主游戏客户端用的是Cocos Creator 因为之前一直使用的是tcp协议与客户端Cocos(lua)使用protobuf通信,切到js上还是遇到了点麻烦。因为不懂nodejs,一直以为Cocos Creator使用的就是js所以遇到了点麻烦。
先说一下在web页面使用protobufjs与服务器之间的通信吧!(直接用web方式在Cocos Creator里一直找不到proto文件,可能还没搞清楚Cocos Creator加载资源的原理跟路径映射)
通信包的格式:前8位(包大小)、2位(主命令)、2位(子命令)、包体;
先引入所需要的js文件:
<script type="text/javascript" src="./dist/long.js"></script>
<script type="text/javascript" src="./dist/bytebuffer.min.js"></script>
<script type="text/javascript" src="./dist/protobuf.min.js"></script>
所以需要两个函数把数值类型转换为所需要的字节类型,一个是转64,一个是转16位
function int64ToByte(num) {
//只有int类型,小于256点1字节,大于256点两字节,所有只能返过来
var bytes = new Array();
//没有64位只能前4位补0
bytes.push(0);
bytes.push(0);
bytes.push(0);
bytes.push(0);
bytes.push(num >> 24 & 0xff)
bytes.push(num >> 16 & 0xff)
bytes.push(num >> 8 & 0xff)
bytes.push(num & 0xff)
return bytes;
}
function int16ToByte(num) {
//只有int类型,小于256点1字节,大于256点两字节,所有只能返过来
var bytes = new Array();
bytes.push(num >> 8 & 0xff)
bytes.push(num & 0xff)
return bytes;
}
编写解码函数:
function bytes2int16(arr) {
return ((arr[0] & 0xFF)<<8)|(arr[1]&0xFF);
}
function bytes2int64(arr) {
//没有64位只能取后四位
return ((arr[4] & 0xFF)<<24)|((arr[5] & 0xFF)<<16)|((arr[6] & 0xFF)<<8)|(arr[7]&0xFF);
}
然后就是包体的封装:
class Packet{
constructor(options){
this.mainId=options.mainId||0;
this.subId=options.subId||0;
this.sendData=options.sendData||new Uint8Array(0);
}
encode(){
//包的总长度
var all_length = 12 + this.sendData.byteLength;
//返回的数组
var myArray = new ArrayBuffer(all_length);
//以byte形式的数组返回
var resData = new Uint8Array(myArray);
//转换成服务器uint64的包头类型
var all_length_byte = int64ToByte(all_length)
//添加包头
for (let i = 0; i < all_length_byte.length; i++) {
resData[i] = all_length_byte[i];
}
//主命令
var mainId_byte = int16ToByte(this.mainId);
for (let i = 0; i < mainId_byte.length; i++) {
resData[i + 8] = mainId_byte[i];
}
//子命令
var subId_byte = int16ToByte(this.subId);
for (let i = 0; i < subId_byte.length; i++) {
resData[i + 10] = subId_byte[i];
}
//内容
for (let i = 0; i < this.sendData.length; i++) {
resData[i + 12] = this.sendData[i];
}
return myArray;
}
}
封装发送数据:
var buffer;
var ModelRegGameServerInfo;
protobuf.load("reg_rpc_model.proto", function (err, root) {
if (err)
throw err;
ModelRegGameServerInfo = root.lookupType("grpcmodel.ModelRegGameServerInfo");
var server = ModelRegGameServerInfo.create({
serverId: 23,
addr: "abc",
count: 1,
serverType: 2,
gameName: "kwo",
gameType: 3
});
//编码
buffer = ModelRegGameServerInfo.encode(server).finish();
//解码
// var svr=ModelRegGameServerInfo.decode(buffer);
// console.log(svr);
});
然后就是发送数据与解析数据:
ws = new WebSocket("ws://localhost:7026/GameWs");
ws.binaryType = "arraybuffer";
// ws.binaryType = "blob";
ws.onopen = function (event) {
console.log("Send Text WS was opened.");
};
ws.onmessage = function (event) {
var arr = new Uint8Array(event.data);
// 包大小
// console.log(bytes2int64(arr.slice(0, 8)));
let mainId = bytes2int16(arr.slice(8, 10));
let subId = bytes2int16(arr.slice(10, 12));
if (mainId==22&&subId==3&&arr.byteLength > 12) {
var svr=ModelRegGameServerInfo.decode(arr.slice(12, arr.byteLength));
console.log(svr);
}
};
ws.onerror = function (event) {
console.log("Send Text fired an error");
};
ws.onclose = function (event) {
console.log("WebSocket instance closed.");
};
//写个按钮调用这个方法
function send() {
if (ws.readyState == WebSocket.OPEN) {
var pck = new Packet({
mainId: 22,
subId: 3,
sendData: buffer
});
ws.send(pck.encode());
}
else {
console.log("WebSocket instance wasn't ready...");
}
}
后来这种方式在Cocos Creator里没法使用,查了好多资料才搞定,便还是需要用到上面包体的封装跟数值与字节的转换。
首先要装的是nodejs在https://nodejs.org/zh-cn/download/下载,我下载的zip版本
将文件解压到要安装的位置,并新建两个目录:node-global :npm全局安装位置、node-cache:npm 缓存路径。
把解压的根目录配置到环境变量里。
输入:node -v 与 npm -v 来测试安装是否成功
配置全局安装位置:npm config set prefix "D:\dev\node\node-global"
配置全局缓存位置:npm config set cache "D:\dev\node\node-cache"
安装protobufjs:npm install -g protobufjs
进入到node-cache\node_modules\protobufjs\dist目录把protobuf.js复制到Cocos Creator里然后导入为插件
进入到node-cache目录下,把写好的proto文件复制一份到此目录下
然后执行:pbjs -t static-module -w commonjs -o reg_rpc_model.js reg_rpc_model.proto
生成所需要的js文件,把js文件复制到Cocos Creator里;把生成的js文件第一行:var $protobuf = require("protobufjs/minimal");改为var $protobuf = protobuf;
然后开始写与服务器通信的代码:
// { grpcmodel }这个是proto文件里的包名 from后面是路径
import { grpcmodel } from "..model/reg_rpc_model"
ws = new WebSocket("ws://localhost:7026/GameWs");
ws.binaryType = "arraybuffer";
// ws.binaryType = "blob";
ws.onopen = function (event) {
console.log("Send Text WS was opened.");
};
ws.onmessage = function (event) {
var arr = new Uint8Array(event.data);
// 包大小
// console.log(bytes2int64(arr.slice(0, 8)));
let mainId = bytes2int16(arr.slice(8, 10));
let subId = bytes2int16(arr.slice(10, 12));
if (mainId==22&&subId==3&&arr.byteLength > 12) {
console.log(arr);
let msg=grpcmodel.ModelRegGameServerInfo.decode(arr.slice(12, arr.byteLength));
console.log(msg);
}
};
ws.onerror = function (event) {
console.log("Send Text fired an error");
};
ws.onclose = function (event) {
console.log("WebSocket instance closed.");
};
function send(){
let message = grpcmodel.ModelRegGameServerInfo.create({
serverId: 23,
addr: "abc",
count: 1,
serverType: 2,
gameName: "kwo",
gameType: 3
});
let msg_data = grpcmodel.ModelRegGameServerInfo.encode(message).finish();
if (ws.readyState == WebSocket.OPEN) {
var pck = new Packet({
mainId: 22,
subId: 3,
sendData: msg_data
});
ws.send(pck.encode());
} else {
console.log("web socket not open")
}
}
原文地址:http://www.1025m.com/37.html