您的位置:首页 > Web前端

flatbuffers 和netty的结合使用

2015-06-01 17:34 1466 查看
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:13638363871@163.com ;
* 个人博客主页: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