flatbuffers 在cocos2dx中的应用

这篇文章 接着 前面的http://my.oschina.net/chenleijava/blog/423503,继续说说flatbuffers的应用

这里主要说的是flatbuffer 作为以cocos2dx引擎为基础 ,网络部分的序列化方案采用flatbuffers。

之所以考虑flatbuffers,依赖性低 ,性能较好。

这里只谈论设计方案,该设计方案对代码设计和结构上有一定的约定(相对订制):

客户端 会接受来自服务器的响应,解码到消息,进行数据处理。针对flatbuffers生成的类和对应处理该消息事的方案映射

在以前protbuffer中有提过(http://my.oschina.net/chenleijava/blog/376560)。这里依旧采用此方案:

注意以下所提及的类或者接口都是代码生成器生成(主要适用于这类方案 定制的,采用java编写),会解析fbs文件,生成对应的controller 文件。编写逻辑 只在

virtualvoiddispatcherMessage(char*data);

中完成;

fbs测试:

//1.使用genTools生成对应客户端代码你需要严格约定
//2.table命名约定上下行消息xxRequestxxResponse
//3.前后端交互消息存在一个消息ID;命名约定:msgID
//4.命名空间使用gen---主要限制前端

namespacegen;

tableLoginFlatRequest{

msgID:int=1;
username:string;


}

tableLoginFlatResponse{
msgID:int=2;
time:long;
}

tableBattleRequest{
msgID:int=3;

}

tableBattleResponse{
msgID:int=4;

}


接口类设计:IController.h

//warnthisisgenfile,don'tmodifyit
#ifndef_ICONTROLLER_H
#define_ICONTROLLER_H
#include<map>
#include"cocos2d.h"
#include"flatgen/Game_generated.h"
usingnamespacecocos2d;
usingnamespacegen;
usingnamespacestd;
#ifndefdelete_array
#definedelete_array(p)do{if(p){delete[](p);(p)=nullptr;}}while(0)
#endif//delete_array
classIController{

public:
IController(){}
/**
注册消息ID和对应处理controller的关系
以便于快速索引处理
*/
staticstd::map<int,IController*>controller_map;
/**
负责处理数据分发
*/
virtualvoiddispatcherMessage(char*data);
/**
*获取对应的消息ID
*/
virtualintmsgID()=0;
/**
转化对应消息为flatbuffers实体
data+4:消息ID占用4字节
*/
template<typenameT>constT*getRoot(char*data);
/**
*@Author石头哥哥,15-06-0123:06:26
*
*@brief注册controolerandmapper
*/
staticvoidregisterMapperController();
};
template<typenameT>
inlineconstT*IController::getRoot(char*data){
returnflatbuffers::GetRoot<T>(data+4);
}
#endif//_ICONTROLLER_H

.cpp

//thisisgenfile,don'tmodifyit
#include"IController.h"
#include"LoginFlatController.h"
#include"BattleController.h"

map<int,IController*>IController::controller_map;
voidIController::dispatcherMessage(char*data){}
/**
*注册controllermapper
*/
voidIController::registerMapperController(){
log("%s","registerMapperController开始注册controller......");
controller_map[LoginFlatController::controller->msgID()]=LoginFlatController::controller;
controller_map[BattleController::controller->msgID()]=BattleController::controller;
}


生成对应的子类:LoginFlatController.h

//thisisgenfile,don'tmodifyit
#ifndefLoginFlatController_h
#defineLoginFlatController_h
#include"IController.h"
classLoginFlatController:publicIController{
public:
staticLoginFlatController*controller;
public:
/**
负责处理数据分发处理来自服务器数据
*/
virtualvoiddispatcherMessage(char*data);
/**
*获取对应的消息ID
*/
virtualintmsgID()override;
};
#endif//LoginFlatController_h

.cpp

//thisisgenfile,logiccontroler

#include"LoginFlatController.h"
LoginFlatController*LoginFlatController::controller=newLoginFlatController;

intLoginFlatController::msgID(){
flatbuffers::FlatBufferBuilderbuilder;
builder.Finish(CreateLoginFlatResponse(builder));
automsgID=flatbuffers::GetRoot<LoginFlatResponse>(builder.GetBufferPointer())->msgID();
builder.ReleaseBufferPointer();
returnmsgID;
}

/**
负责处理数据分发处理来自服务器数据
*/
voidLoginFlatController::dispatcherMessage(char*data){

autologinflatresponse=getRoot<LoginFlatResponse>(data);
//TODO::处理来自服务器数据LoginFlatResponse

}

因为生成代码的工具还在开发中。目前只完成了cpp生成 ,后期支持java(服务器端)---暂时只定制flatbuffer代码生成。

后期开源分享,这里提供java代码生成类:

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

importorg.apache.commons.io.FileUtils;

importjava.io.File;
importjava.io.FilenameFilter;
importjava.io.IOException;
importjava.io.RandomAccessFile;
importjava.util.ArrayList;

/**
*@author石头哥哥
*</P>
*Date:2015/6/2
*</P>
*Time:9:35
*</P>
*Package:dcServer-parent
*</P>
*<p/>
*注解:
*/
publicclassGenFlatCpp{

/**
*客户端处理消息类型注意消息命名XXXResponse---服务器响应消息类型
*/
privatestaticfinalStringclient="Response";

/**
*用于生成处理消息类型引用的变量名注意是静态类型
*/
privatestaticfinalStringcontrollorname="controller";


/**
*对应客户端处理来自服务器端消息命名
*xxxResponse
*<p/>
*tableLoginResponse{
*objectID:int=2;
*username:string;
*}
*<p/>
*生成处理来自服务器端的Response类型消息<p/>
*自动生成映射的mapper如:controller_map[msgID<LoginResponse>()]=LoginResponseController::controller;<p/>
*同时生成对应的controller处理部分<p/>
*逻辑编写部位:virtualvoiddispatcherMessage(char*data);<p/>
*
*@paramfbspathflatbufferfbs所在文件路径<p/>
*@paramcontrollerpath对应controller生成路径<p/>
*@throwsIOException
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
publicstaticvoidgenCppMapper(Stringfbspath,Stringcontrollerpath)throwsException{
Filefile;
//getfbstablelist
file=newFile(fbspath);
String[]fbsNames=file.list(newFilenameFilter(){
@Override
publicbooleanaccept(Filedir,Stringname){
returnname.endsWith(".fbs");
}
});
if(fbsNames==null){
thrownewException("fbsnotfoundinres!!!");
}

ArrayList<String>fbsPaths=newArrayList<String>();

for(Stringfbsname:fbsNames){
fbsPaths.add(fbspath+"/"+fbsname);
}

//读取所有fbs文件获取文件中的tablename
ArrayList<String>mapperTable=newArrayList<String>();

//loadingfbsandfiltertablename
for(Stringfbs:fbsPaths){
file=newFile(fbs);
RandomAccessFilerandomAccessFile;
try{
randomAccessFile=newRandomAccessFile(file,"r");//O_RDONLY
Stringtxt="";
while(txt!=null){
txt=randomAccessFile.readLine();
if(txt!=null
&&txt.startsWith("table")){
Stringtemp=txt.substring(0,txt.lastIndexOf("{"))
.replace("table","");
if(temp.endsWith(client))mapperTable.add(temp.trim());

}
}
randomAccessFile.close();//closerandomAccessFile
}catch(IOExceptione){
e.printStackTrace();
}
}
fbsPaths.clear();


ArrayList<String>controllerList=newArrayList<String>();


//foricontroller
StringBuildermapperBuilder=newStringBuilder();
//controller_map[LoginFlatController::controller->msgID()]=LoginFlatController::controller;
for(Stringtable:mapperTable){
Stringcontrollername=table+"Controller";
controllername=controllername.replace(client,"");
mapperBuilder.append("controller_map[")
.append(controllername)
.append("::")
.append(controllorname+"->msgID()")
.append("]")
.append("=")
.append(controllername)
.append("::")
.append(controllorname)
.append(";\n");
controllerList.add(controllername);//addcontroller
}

mapperTable.clear();


ArrayList<String>genIcontrollerHeader=newArrayList<String>();
genIcontrollerHeader.addAll(controllerList);
/**
*don'toverridehadgenxxxcontroller
*becausemaybehadlogicinit!!!!
*/
String[]exsiteController=newFile(controllerpath).list(newFilenameFilter(){
@Override
publicbooleanaccept(Filedir,Stringname){
returnname.endsWith(".cpp")&&!name.endsWith("IController.cpp");
}
});

for(Stringname:exsiteController){
Stringtemp=name.substring(0,name.indexOf("."));
if(controllerList.contains(temp)){
controllerList.remove(temp);
}
}



StringBuilderbuilderCpp=newStringBuilder();
//gen.hfile
for(Stringcontroller:controllerList){
Stringdefine=controller+"_h";
builderCpp.append("//thisisgenfile,don'tmodifyit\n")
.append("#ifndef")
.append("").append(define).append("\n")
.append("#define")
.append("").append(define).append("\n")
.append("#include\"IController.h\"")
.append("\n")
.append("class")
.append(controller)
.append(":").append("publicIController{")
.append("\n")
.append("public:")
.append("\n")
.append("static")
.append(controller)
.append("*")
.append(controllorname)
.append(";\n")
.append("public:\n")
.append("/**\n"+
"负责处理数据分发处理来自服务器数据\n"+
"*/\n")
.append("virtualvoiddispatcherMessage(char*data);\n")
.append("/**\n"+
"*获取对应的消息ID\n"+
"*/\n")
.append("virtualintmsgID()override;\n")
.append("};\n")
.append("#endif")
.append("//").append(define);
byte[]cpp_h=builderCpp.toString().getBytes();
builderCpp.delete(0,cpp_h.length);

//writeheader
FilecontrollerFile=newFile(controllerpath);
if(!controllerFile.exists()){
controllerFile.mkdirs();
}
FileUtils.writeByteArrayToFile(newFile(controllerpath+"/"+controller+".h"),cpp_h);




//gencontrollercppfile
StringtableName=controller.substring(0,controller.indexOf("Controller"))+client;
StringlowTablename=tableName.toLowerCase();
builderCpp.append("//thisisgenfile,logiccontroler\n")
.append("\n")
.append("#include")
.append("")
.append("\"")
.append(controller).append(".h")
.append("\"")
.append("\n")
.append(controller).append("")
.append("*")
.append(controller)
.append("::")
.append(controllorname).append("=")
.append("new").append(controller)
.append(";\n")
.append("\n")
.append("int")
.append(controller+"::msgID(){\n")
.append("flatbuffers::FlatBufferBuilderbuilder;\n"+
"builder.Finish(Create"+tableName+"(builder));\n"+
"automsgID=flatbuffers::GetRoot<"+tableName+">(builder.GetBufferPointer())->msgID();\n"+
"builder.ReleaseBufferPointer();\n"+
"returnmsgID;\n")
.append("}\n\n")
.append("/**\n"+
"负责处理数据分发处理来自服务器数据\n"+
"*/\n")
.append("void").append(controller).append("::")
.append("dispatcherMessage(char*data){\n\n").append("auto")
.append(lowTablename)
.append("=getRoot<")
.append(tableName)
.append(">(data);\n")
.append("//TODO::处理来自服务器数据")
.append(tableName)
.append("\n\n")
.append("}");

byte[]cpp=builderCpp.toString().getBytes();
builderCpp.delete(0,cpp.length);
FileUtils.writeByteArrayToFile(newFile(controllerpath+"/"+controller+".cpp"),cpp);
}









//IControlleralwaysoverride
//genIControllergeneratedheadername
ArrayList<String>generatedHeaders=newArrayList<String>();
Stringtempname;
Stringtempnameup;
charfirst;
for(Stringname:fbsNames){
tempname=name.substring(1,name.lastIndexOf("."));
tempnameup=name.substring(0,name.lastIndexOf(".")).toUpperCase();
first=tempnameup.charAt(0);
generatedHeaders.add(first+tempname);
}


builderCpp.append("//warnthisisgenfile,don'tmodifyit\n")
.append("#ifndef").append("_ICONTROLLER_H\n")
.append("#define").append("_ICONTROLLER_H\n")
.append("#include<map>\n")
.append("#include\"cocos2d.h\"\n");
//appendgeneratedheaderinclude
for(Stringname:generatedHeaders){
builderCpp.append("#include\"flatgen/").append(name)
.append("_generated.h\"").append("\n");
}
generatedHeaders.clear();

builderCpp.append("usingnamespacecocos2d;\n")
.append("usingnamespacegen;\n")
.append("usingnamespacestd;\n")
.append("#ifndefdelete_array\n")
.append("#definedelete_array(p)do{if(p){delete[](p);(p)=nullptr;}}while(0)\n")
.append("#endif//delete_array\n")
.append("classIController{\n")
.append("\n")
.append("public:\n")
.append("IController(){}\n")
.append("/**\n"+
"注册消息ID和对应处理controller的关系\n"+
"以便于快速索引处理\n"+
"*/\n")
.append("staticstd::map<int,IController*>controller_map;\n")
.append("/**\n"+
"负责处理数据分发\n"+
"*/\n")
.append("virtualvoiddispatcherMessage(char*data);\n")
.append("/**\n"+
"*获取对应的消息ID\n"+
"*/\n")
.append("virtualintmsgID()=0;\n")
//.append("/**\n"+
//"*@Author石头哥哥,15-06-0123:06:00\n"+
//"*\n"+
//"*@brief获取生成消息对应的msgID\n"+
//"*\n"+
//"*@return<#returnvaluedescription#>\n"+
//"*/\n")
//.append("template<typenameT>staticintmsgID();\n")
.append("/**\n"+
"转化对应消息为flatbuffers实体\n"+
"data+4:消息ID占用4字节\n"+
"*/\n")
.append("template<typenameT>constT*getRoot(char*data);\n")
.append("/**\n"+
"*@Author石头哥哥,15-06-0123:06:26\n"+
"*\n"+
"*@brief注册controolerandmapper\n"+
"*/\n")
.append("staticvoidregisterMapperController();\n")
.append("};\n")
//.append("template<typenameT>\n")
//.append("inlineintIController::msgID(){\n"+
//"flatbuffers::FlatBufferBuilderbuilder;\n"+
//"builder.Finish(CreateLoginRequest(builder));\n"+
//"automsgID=flatbuffers::GetRoot<T>(builder.GetBufferPointer())->msgID();\n"+
//"builder.ReleaseBufferPointer();\n"+
//"returnmsgID;\n"+
//"}\n")
.append("template<typenameT>\n"+
"inlineconstT*IController::getRoot(char*data){\n"+
"returnflatbuffers::GetRoot<T>(data+4);\n"+
"}")
.append("\n").append("#endif//_ICONTROLLER_H");

byte[]data=builderCpp.toString().getBytes();
builderCpp.delete(0,data.length);
FileUtils.writeByteArrayToFile(newFile(controllerpath+"/IController.h"),data);


//genicontrollercpp
builderCpp.append("//thisisgenfile,don'tmodifyit\n")
.append("#include\"IController.h\"\n");

for(Stringcontroller:genIcontrollerHeader){
builderCpp.append("#include")
.append("\"").append(controller).append(".h")
.append("\"\n");
}
genIcontrollerHeader.clear();
controllerList.clear();
builderCpp.append("\n")
.append("map<int,IController*>IController::controller_map;\n")
.append("voidIController::dispatcherMessage(char*data){}\n")
.append("/**\n"+"*注册controllermapper\n"+"*/\n"+"voidIController::registerMapperController(){\n"+
"log(\"%s\",\"registerMapperController开始注册controller......\");\n")
.append(mapperBuilder.toString()).append("}\n");
data=builderCpp.toString().getBytes();
builderCpp.delete(0,data.length);
FileUtils.writeByteArrayToFile(newFile(controllerpath+"/IController.cpp"),data);

System.out.println("-------gensuccess!!!---------------------");

}


}
packagecom.genflat.mapper;/*
*Copyright(c)2015.
*游戏服务器核心代码编写人石头哥哥拥有使用权
*最终使用解释权归创心科技所有
*联系方式:E-mail:13638363871@163.com;
*个人博客主页:http://my.oschina.net/chenleijava
*poweredby石头哥哥
*/

importjava.io.File;
importjava.io.IOException;

/**
*@author石头哥哥
*</P>
*Date:2015/6/2
*</P>
*Time:9:34
*</P>
*Package:dcServer-parent
*</P>
*<p/>
*注解:
*/
publicclassGenFlatMapper{


/**
*defaultgenc++
*args[0]-l
*args[1](java,cpp)
*args[2]-p
*args[3](controllerfilepath)
*args[4]-o
*args[5]fbsfilepath
*
*@paramargs
*/
publicstaticvoidmain(String[]args)throwsIOException{
Stringshow=">>>>flatbuffergen"+
"\n该工具可以生成基于flatbuffers的代码,接口说明如下\n"+
"使用命令:java-jargenflatMapper.jar-lcpp-ccontrollerpath-ffbspath\n"+
"-l:编程语言类型这里暂时支持c++;\n"+
"-p:游戏中处理对应消息的controller文件路径;\n"+
"-o:编写的fbs文件路径;\n"+
"游戏中fbs针对上行下行table命名规则有严格约定,如不使用该约定\n"+
"使用该工具无效,详见readme!";
System.out.println(show);

if(args.length!=0){
System.out.println("--------begingenclientcode----------------");
if(args.length<6){
System.err.println(show);
System.exit(0);
}
Stringl=args[1];
Stringcontrollerpath=args[3];
Stringfbspath=args[5];
if(l.equals("java")){
System.err.println("onlysupportclient,c++\n");
System.err.println("useeg:\n"+
"java-jargenflatMapper.jar-lcpp"+
"-ccontrollerpath-ffbspath");
System.exit(0);
}elseif(l.equals("cpp")){
try{
GenFlatCpp.genCppMapper(fbspath,controllerpath);
}catch(Exceptione){
e.printStackTrace();
}
}

}else{
try{
//gencurrentfile
System.out.println("toolswillgentocurrentfiles,ifyourfbsinresfile!!!");
Filefile=newFile("./controller");
if(!file.exists())file.mkdirs();
//findfbs
GenFlatCpp.genCppMapper("res","./controller");
}catch(Exceptione){
e.printStackTrace();
}

}


}

}


该生代码成器约定如下--如果你使用该方案,那么请严格遵循改约束,或许你有更好的idea:

###基于flatbuffers,使用fbs文件生成对应前端代码。
###完成对应的controller和消息ID映射,目前暂时支持c++。


>1.使用genTools生成对应客户端代码你需要严格约定
2.table命名约定上下行消息xxRequestxxResponse
3.前后端交互消息存在一个消息ID;命名约定:msgID
>4.命名空间使用gen---主要限制前端

namespacegen;
//下行消息xxRequest
tableLoginRequest{
msgID:int=1;//消息IDmsgID
username:string;
}
//下行消息xxResponse
tableLoginResponse{
msgID:int=1;//消息IDmsgID
username:string;
}

####如何使用:
该工具可以生成基于flatbuffers的代码,接口说明如下
使用命令:
java-jargenflatMapper.jar-lcpp-pcontrollerpath-ofbspath
>-l:编程语言类型这里暂时支持c++;
>-c:游戏中处理对应消息的controller文件路径;
>-f:编写的fbs文件路径;

生成对应的文件如下:

上面代码都是生成的。

因为无法上传jar,如果你需要 留言你的邮箱 !

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...