这篇文章 接着 前面的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代码生成。
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,如果你需要 留言你的邮箱 !
原文链接:https://www.f2er.com/cocos2dx/342819.html