通信协议

帧同步与消息同步

引擎为开发提供帧同步与消息同步两套同步协议。同一款游戏可以同时使用两种协议。

消息同步

向房间内所有玩家广播消息。

该消息协议有以下几个特点

  • 玩家加入游戏后即可发送
  • 消息不保存
  • 可能存在丢包。基于udp

    用途

因玩家加入游戏后便可使用,可用于游戏开始前同步数据,也用于作为游戏主协议

接口

发送:

BK.Room.sendBroadcastData(buffer)

监听:

BK.Room.setBroadcastDataCallBack(broadcastCallback)

例子

  1. function sendBroadcastData(game) {
  2. //用户定义的字段
  3. let req = '{"s":"12345678901234567890AB","m":"map","d":"2"}';
  4. BK.Script.log(0,0,"sendBroadcastData :"+ req.length);
  5. var data = new BK.Buffer(req.length);
  6. data.writeStringBuffer(req);
  7. game.sendBroadcastData(data);
  8. }
  9. function broadcastCallback(fromId,buff)
  10. {
  11. var data = buff.readStringBuffer()
  12. BK.Script.log(0,0,"broadcastCallback :"+ buff.bufferLength());
  13. BK.Script.log(0,0,"broadcastCallback str:"+ data);
  14. }
  15. var game = new BK.Room();
  16. //创建或加入房间后
  17. ....
  18. //设置广播回调监听
  19. game.setBroadcastDataCallBack(broadcastCallback);
  20. //发送广播事件
  21. sendBroadcastData(game);

生命周期

加入游戏后,直游戏结束都可以使用

帧同步

向房间内所有玩家发送帧同步消息。

该消息协议有以下几个特点

  • 房主调用startgame后,才可使用
  • 消息可回溯

    接口

  1. function frameSyncCallback(frameDataArray){
  2. BK.Script.log(0,0,"收到帧同步数据");
  3. var frameCount = frameDataArray.length;
  4. for (var index = 0; index < frameDataArray.length; index++) {
  5. var players =frameDataArray[index];
  6. BK.Script.log(0,0,"帧同步帧序列号 = " +players.frameSeq);
  7. BK.Script.log(1,1,"players count :" + players.length);
  8. if(players){
  9. for (var i = 0; i < players.length; i++) {
  10. var player = players[i];
  11. BK.Script.log(0,0,"sync !!!!!!!!!!!! openid :"+player.openId);
  12. BK.Script.log(0,0,"sync !!!!!!!!!!!! itemId :"+player.itemId);
  13. var cmd = player.dataBuffer.readStringBuffer();
  14. BK.Script.log(1,1,"sync !!!!!!!!!!!! cmd len="+player.dataBuffer.bufferLength());
  15. BK.Script.log(1,1,"sync !!!!!!!!!!!! cmd="+cmd);
  16. }
  17. }
  18. }
  19. }
  20. function sendFrameSyncCmd(game,cmd)
  21. {
  22. //用户定义的字段
  23. var opt = new BK.Buffer(1,1);
  24. opt.writeUint8Buffer(cmd);
  25. BK.Script.log(1,1,"sync !!!!!send frame 1");
  26. var status = new BK.Buffer(1,1);
  27. status.writeUint8Buffer(0);
  28. //预留字段
  29. var extend = new BK.Buffer(1,1);
  30. extend.writeUint8Buffer(0);
  31. //send
  32. game.syncOpt(status,opt,extend,undefined,function(){
  33. BK.Script.log(1,1,"sync !!!!!recv ack= "+game.ackSeq);
  34. });
  35. }
  36. var game = new BK.Room();
  37. //收到startgame回调后
  38. ....
  39. //监听帧同步
  40. game.setFrameSyncListener(frameSyncCallback);
  41. sendFrameSyncCmd(game);

数据读写

帧同步、消息同步中数据的读写需要一一匹配。

例如:

使用writeStringBuffer将"Hello"字符串写入BK.Buffer并且发送消息同步

  1. //发送端
  2. var str = "Hello";
  3. var data = new BK.Buffer(str.length);
  4. data.writeStringBuffer(req);
  5. game.sendBroadcastData(data);

接收端,需用writeStringBuffer对应的读取函数readAsString将得到的BK.Buffer以字符串读取出来

  1. var data = buff.readStringBuffer()

多类型数据读写

此处需要注意的是,writeAsString函数与readAsString函数是将整个BK.Buffer对象存储的数据都看做字符串进行读写。也就是当BK.Buffer存储的内容都为字符串时,可以使用两个函数整体的读写。

因BK.Buffer支持多种不同类型的格式写入到一个对象中,例如写入一个unit8,写入字符串,再写入一个uint32位的数据。引擎将各个数据以字符偏移的形式写入到BK.Buffer中,当需要写入多个对象中存在字符时需多申请3个字节的空间,用于标识字符的大小。

//发送时写入一个unit8,写入字符串,再写入一个uint32位的数据则需申请 1 + 字符串长度+3 + 4 个字节的长度BK.Buffer。如下例子,字符为“hello”时,需申请1+3+3+4 = 11字节的长度的buffer。

  1. var str = "hello"
  2. var buff = new BK.Buffer(str.length + 3 + 1 + 4);
  3. var numUint8 = 1;
  4. var numUint32 = 2;
  5. buff.writeUint8Buffer(numUint8);
  6. buff.writeStringBuffer(str);
  7. buff.writeUint32Buffer(numUint32);

依次写入后buffer结构的结构如下 2.2.2 通信协议  - 图1

终端接收到服务器的推送时,读取也应该按顺序进行读取。这需要注意的是读取字符串时需使用和writeStringBuffer匹配的读取函数readStringBuffer,不可使用writeAsString。writeAsString是将整段buffer以字符串的形式读取出来,并无考虑字符标识长度。

  1. //接收端
  2. BK.Script.log(0,0,buff.readUint8Buffer());
  3. BK.Script.log(0,0,buff.readStringBuffer());
  4. BK.Script.log(0,0,buff.readUint32Buffer());

原文: https://hudong.qq.com/docs/engine/server/cmshowserver/room/room-protocol.html