flatbuffers 和netty的结合使用

flatbuffers 可以应用到游戏中。媲美google protobuffer。 

https://github.com/google/flatbuffers

http://google.github.io/flatbuffers/index.html

下载flatbuffers 编译对应平台的flatc。 

接下来是编写fbs:

namespace com.dc.gameserver.genflat;


table LoginRequest{
    msgID:int=1;
    username:string;
    password:string;
    time:long;
}
///

table LoginResponse{
    msgID:int=2;
    time:long;
    uid:string;
}

详细说明见:http://google.github.io/flatbuffers/md__schemas.html

flatc脚本方式:http://google.github.io/flatbuffers/md__compiler.html

flatc.exe  -j   -o ../../java/ game.fbs

会生成对应的java文件.

// automatically generated, do not modify

package com.dc.gameserver.genflat;

import com.google.flatbuffers.FlatBufferBuilder;
import com.google.flatbuffers.Table;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class LoginRequest extends Table {
  public static LoginRequest getRootAsLoginRequest(ByteBuffer _bb) { return getRootAsLoginRequest(_bb, new LoginRequest()); }
  public static LoginRequest getRootAsLoginRequest(ByteBuffer _bb, LoginRequest obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__init(_bb.getInt(_bb.position()) + _bb.position(), _bb)); }
  public LoginRequest __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }

  public int msgID() { int o = __offset(4); return o != 0 ? bb.getInt(o + bb_pos) : 1; }
  public String username() { int o = __offset(6); return o != 0 ? __string(o + bb_pos) : null; }
  public ByteBuffer usernameAsByteBuffer() { return __vector_as_bytebuffer(6, 1); }
  public String password() { int o = __offset(8); return o != 0 ? __string(o + bb_pos) : null; }
  public ByteBuffer passwordAsByteBuffer() { return __vector_as_bytebuffer(8, 1); }
  public long time() { int o = __offset(10); return o != 0 ? bb.getLong(o + bb_pos) : 0; }

  public static int createLoginRequest(FlatBufferBuilder builder,
      int msgID,
      int username,
      int password,
      long time) {
    builder.startObject(4);
    LoginRequest.addTime(builder, time);
    LoginRequest.addPassword(builder, password);
    LoginRequest.addUsername(builder, username);
    LoginRequest.addMsgID(builder, msgID);
    return LoginRequest.endLoginRequest(builder);
  }

  public static void startLoginRequest(FlatBufferBuilder builder) { builder.startObject(4); }
  public static void addMsgID(FlatBufferBuilder builder, int msgID) { builder.addInt(0, msgID, 1); }
  public static void addUsername(FlatBufferBuilder builder, int usernameOffset) { builder.addOffset(1, usernameOffset, 0); }
  public static void addPassword(FlatBufferBuilder builder, int passwordOffset) { builder.addOffset(2, passwordOffset, 0); }
  public static void addTime(FlatBufferBuilder builder, long time) { builder.addLong(3, time, 0); }
  public static int endLoginRequest(FlatBufferBuilder builder) {
    int o = builder.endObject();
    return o;
  }
};
// automatically generated, do not modify

package com.dc.gameserver.genflat;

import com.google.flatbuffers.FlatBufferBuilder;
import com.google.flatbuffers.Table;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

///

public class LoginResponse extends Table {
  public static LoginResponse getRootAsLoginResponse(ByteBuffer _bb) { return getRootAsLoginResponse(_bb, new LoginResponse()); }
  public static LoginResponse getRootAsLoginResponse(ByteBuffer _bb, LoginResponse obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__init(_bb.getInt(_bb.position()) + _bb.position(), _bb)); }
  public LoginResponse __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }

  public int msgID() { int o = __offset(4); return o != 0 ? bb.getInt(o + bb_pos) : 2; }
  public long time() { int o = __offset(6); return o != 0 ? bb.getLong(o + bb_pos) : 0; }
  public String uid() { int o = __offset(8); return o != 0 ? __string(o + bb_pos) : null; }
  public ByteBuffer uidAsByteBuffer() { return __vector_as_bytebuffer(8, 1); }

  public static int createLoginResponse(FlatBufferBuilder builder,
      int msgID,
      long time,
      int uid) {
    builder.startObject(3);
    LoginResponse.addTime(builder, time);
    LoginResponse.addUid(builder, uid);
    LoginResponse.addMsgID(builder, msgID);
    return LoginResponse.endLoginResponse(builder);
  }

  public static void startLoginResponse(FlatBufferBuilder builder) { builder.startObject(3); }
  public static void addMsgID(FlatBufferBuilder builder, int msgID) { builder.addInt(0, msgID, 2); }
  public static void addTime(FlatBufferBuilder builder, long time) { builder.addLong(1, time, 0); }
  public static void addUid(FlatBufferBuilder builder, int uidOffset) { builder.addOffset(2, uidOffset, 0); }
  public static int endLoginResponse(FlatBufferBuilder builder) {
    int o = builder.endObject();
    return o;
  }
};

消息设计原则除了基础的游戏数据会有一个消息ID。

获取生成Table消息ID可以这样:

 LoginResponse.getRootAsLoginResponse(ByteBuffer.allocate(4)).msgID();

修改了下源码(Table),避免new一个ByteBuffer,可以这样

  // Look up a field in the vtable, return an offset into the object, or 0 if the field is not
    // present.
    protected int __offset(int vtable_offset) {
        if (bb == null) return 0; // get msgID  ,ignore allocate ByteBuffer
        int vtable = bb_pos - bb.getInt(bb_pos);
        return vtable_offset < bb.getShort(vtable) ? bb.getShort(vtable + vtable_offset) : 0;
    }
new LoginResponse().msgID();

flatbuffer中有对默认的数值填充,将会不对其存储!。如没有默认值的字段都为0,不管基础数据类型还是string.;存在默认值如id:int=1;在填充数据value==1时,不会被存储。

反正默认值不被存储就对了,详见代码Table;当然可以强制进行存储,builder.setForcexx(true);

netty只负责网络传输。解析网络字节码就对了,根据协议包长+消息体(ID+游戏操作数据)。

针对java,采用多态的方式进行处理对应的消息体。 

  static final IController game_controllers[] = new IController[2];

存储消息处理类的引用。

Controller接口设计:

/**
     * 基于flatBuffers 结构数据传输
     *
     * @param data   cast subclass of table .
     * @param player game session
     * @throws Exception
     */
    void DispatchFlatBuffer(final byte[] data, final PlayerInstance player) throws Exception;

实现类:

/*
 * Copyright (c) 2015.
 * 游戏服务器核心代码编写人石头哥哥拥有使用权
 * 最终使用解释权归创心科技所有
 * 联系方式:E-mail:[email protected] ;
 * 个人博客主页:http://my.oschina.net/chenleijava
 * powered by 石头哥哥
 */

package com.dc.gameserver.servercore.controller.testflatbuffercontroller;

import com.dc.gameserver.genflat.LoginRequest;
import com.dc.gameserver.genflat.LoginResponse;
import com.dc.gameserver.model.character.PlayerInstance;
import com.dc.gameserver.servercore.controller.abstractController.AbstractController;
import com.google.flatbuffers.FlatBufferBuilder;
import com.google.protobuf.MessageLite;
import org.springframework.stereotype.Service;

import java.nio.ByteBuffer;
import java.util.UUID;

/**
 * @author 石头哥哥
 *         </P>
 *         Date:   2015/6/1
 *         </P>
 *         Time:   14:36
 *         </P>
 *         Package: dcServer-parent
 *         </P>
 *         <p/>
 *         注解: 测试flatbuf 转化到游戏逻辑处理
 */
@Service
public class TestFlatBufferController extends AbstractController {

    /**
     * spring 容器初始化 加载并初始化相应控制器处理句柄
     * 非spring环境下 可以采用静态的初始化方案
     */
    @Override
    public void PostConstruct() {
        game_controllers[new LoginRequest().msgID()] = this;
    }

    /**
     * messageLite数据结构体分发
     *
     * @param messageLite 数据载体
     * @param player      active object
     * @throws Exception 注意messageLite应该递交给GC直接处理  ,set null
     */
    @Override
    @Deprecated
    public void DispatchMessageLit(MessageLite messageLite, PlayerInstance player) throws Exception {
    }



    /**
     * 基于flatBuffers 结构数据传输
     *
     * @param data   cast subclass of table .         msgID + 游戏数据(可反序列化为flatbuffers Table 子类 这里如LoginRequest)
     * @param player game session
     * @throws Exception
     */
    @Override
    public void DispatchFlatBuffer(byte[] data, PlayerInstance player) throws Exception {
        //4 为消息ID 所占用的位置
        LoginRequest login = LoginRequest.getRootAsLoginRequest(ByteBuffer.wrap(data, 4, data.length - 4));
        String username = login.username();
        String password = login.password();
        // do something

        // get msgID
        int msgID = new LoginResponse().msgID();
        FlatBufferBuilder builder = new FlatBufferBuilder(8);
        builder.finish(LoginResponse.createLoginResponse(builder, msgID
                , System.currentTimeMillis()
                , builder.createString(UUID.randomUUID().toString())));

        wrappedBufferInt(msgID, builder);

    }

    public static void main(String[] args) {
        //builder data  and finish it
        int msgID = new LoginResponse().msgID();
        FlatBufferBuilder builder = new FlatBufferBuilder(8);
        builder.finish(LoginResponse.createLoginResponse(builder, msgID
                , System.currentTimeMillis()
                , builder.createString(UUID.randomUUID().toString())));
        wrappedBufferInt(msgID, builder);
    }

}



由于flatbuf相对protobuffer 体积较小  在cocos2dx中应用 相对较为方便。  目前已经有一套完整的应用解决方案思路(前后端代码生成器)。欢迎交流!


你可能感兴趣的:(netty,FlatBuffers)