代码生成(引擎、前端)
介绍
ioGame 是非常注重开发体验的,代码注释即文档、方法即交互接口的原则。
ioGame 具备一次编写到处对接的能力,从而让你们团队提升巨大的生产力。 一次编写指的是编写一次 java 业务代码,而到处对接则是指为不同的前端项目生成与服务器交互的代码。
你只需要编写一次 java 代码,就能为 Godot、 UE、 Unity、 CocosCreator、 Laya、 React、 Vue、 Angular ...等项目生成统一的交互接口
ioGame 能为各种前端项目生成 action、广播、错误码 相关接口代码。 这将意味着,你只需要编写一次业务代码,就可以同时与这些游戏引擎或现代化的前端框架交互。
面临的问题
在介绍此特性之前,我们先回顾一下传统的对接与工作流程。
在开发过程中,当我们编写完业务需求后,就需要与前端同学进行联调对接。 为了方便对接,通常需要提供相应的文档,包括业务方法、参数、响应和描述等。 你将要抽出一些时间来编写、维护对接文档的工作,而且当团队人数多了之后, 文档就会很乱、不同步、不是最新的、忘记更新等情况就会出现。
另一种对接方式是通过协议文件的形式,就是把 .proto 文件给到前端,让前端开发者自行解读。 这种方式有一些缺点,比如在客户端发送请求后并不能知道该请求会返回什么, 这通常需要在协议文件中阅读与查找。 如果协议较少时,这样做的问题并不大,但如果有数百个协议时,这样的工作方式是低效的, 因为协议文件中存在着大量的干扰因素。
除了上述缺点外,前端开发者在拿到你的文档后,还需要写一些模板代码,目的是为了访问你对外公开的接口。 在接口较少时,这么做没问题。 但如果你的服务器有数百个接口时,那么前端开发者将要花费更多的时间来编写这些模板代码。
我们面临的问题至少包括以下几点
- 服务器开发者需要花费额外的时间来维护对接文档,并且要确保文档是最新的。
- 为了访问服务器公开的接口,前端开发者需要编写很多模板代码。
- 当协议较多时,会让对接工作效率低下,因为协议文件中存在着大量的干扰因素。
- 各方面都缺乏实时性,可以说是无法做到实时性,可能唯一的实时性就是“一声吼”。
- 无法明确接口需要的参数类型及服务器会响应什么内容。
- 对接时有安全隐患,因为前端开发者在编写交互接口时,参数类型会有写错的可能性。 比如参数需要 User 类型,却传了一个 Student 类型的参数给服务器,从而引发莫名其妙的问题。 由于无法在编译期发现问题,且只能在运行时才有可能发现问题,此时就需要双方花费更多的时间来排查。
以上这些问题,在 ioGame 中得到了很好的解决,并且 ioGame 做到了一次编写到处对接。 你只需要编写一次 java 代码,就能为 Godot、UE、Unity、CocosCreator、Laya、React、Vue、Angular ...等项目生成统一的交互接口。
解决问题的关键
上一小节中,我们谈到了对接方面的几个问题,这一小节我们将对这些问题给出解决方案。
ioGame 从两个主要方面来解决此问题
- 为不同语言提供 ioGame SDK,如 C# SDK、TypeScript SDK、 GDScript SDK、C++ SDK。
- 为前端生成可与服务器交互的接口,根据服务器对外公开的接口,为前端生成 action、广播、错误码 相关的对接交互代码。
ioGame SDK 为我们提供了基础的通信接口,而代码生成则为我们提供了直接的交互接口,这些交互接口的使用与本地方法调用一般丝滑。
SDK 代码生成的几个优势
- 帮助客户端开发者减少巨大的工作量,不需要编写大量的模板代码。
- 语义明确,清晰。生成的交互代码即能明确所需要的参数类型,又能明确服务器是否会有返回值。这些会在生成接口时就提前明确好。
- 明确的交互接口,确保了方法的参数类型安全且明确,使我们在编译阶段就能发现潜在问题。 这种做法有效避免了安全隐患,并减少了联调时可能出现的低级错误。
- 减少服务器与客户端双方对接时的沟通成本,代码即文档。生成的联调代码中有文档与使用示例,方法上的示例会教你如何使用,即使是新手也能做到零学习成本。
- 帮助客户端开发者屏蔽与服务器交互部分,将更多的精力放在真正的业务上。
- 为双方联调减少心智负担。联调代码使用简单,与本地方法调用一般丝滑。
- 抛弃传统面向协议对接的方式,转而使用面向接口方法的对接方式。
- 当我们的 java 代码编写完成后,我们的文档及交互接口可做到同步更新,不需要额外花时间去维护对接文档及其内容。
代码生成的使用场景
据服务器所提供的 action、广播、错误码,自动生成前端客户端所需要的交互代码。 目前提供了多种语言的支持,分别是 C#、TypeScript、GDScript,其语言对应的游戏引擎有
支持的语言 | 支持的游戏引擎 |
---|---|
C# SDK | Godot、Unity |
TypeScript SDK | Cocos Creator、Laya |
GDScript SDK | Godot |
TypeScript SDK 除了能用在游戏引擎外,还能用在各种现代的前端框架,如 React、Vue、Angular 等。 简单的说,TypeScript SDK 适用于任意支持 TypeScript 的相关项目。同样,C# SDK 适用于任意支持 C# 的相关项目。
假如现在你的服务器项目已经提供了数百个 action 方法,而前端引擎需要由 TypeScript 切换到 C# 语言相关的引擎。 那么 ioGame 的代码生成将会派上巨大的用场,因为可以瞬间生成与前端交互所需的代码。 这个操作将为前端节省巨大的工作量,同时也为沟通节省了巨大的成本。 生成的交互 api 是直观的,如本地方法调用般丝滑。
换句话说,你可以将你的 ioGame 项目带到下个新项目中使用(或者新公司的项目), 无论新项目前端使用的是 TypeScript 游戏引擎还是 C# 游戏引擎,都能为前端生成交互代码。
此外,除了切换前端引擎的场景外,ioGame 还支持同时与不同的游戏引擎交互,因为无论生成哪种语言的交互 api,在使用的感观上几乎是一致的。
其他案例:我们在开发个人项目时,当没能找到可合作的前端开发者时,仍然可以先编写游戏服务器相关的代码。 将来如果遇见合适的合作人员时,就可以通过代码生成将已有的 action 生成一份给到前端哥们。
Example Source Code
服务器示例源码
see https://github.com/iohao/ioGameExamples
path : SdkExample
- SdkApplication
- GenerateTest
前端示例源码
以下这些 demo 已经与本项目调通,前端代码 action、广播、错误码等,由 ioGame 生成。 前端开发者在通信的交互上本地方法调用一般丝滑,让前端开发者将更多的精力放在真正的业务,而不是编写这些模板式的交互代码上。
Github | 语言 | 描述 |
---|---|---|
ioGameSdkGDScriptExampleGodot | GDScript | 与 Godot 互通的一个示例。 Godot、Protobuf、Netty、ioGame、GDScript、WebSocket |
ioGameSdkC#ExampleGodot | C# | 与 Godot 互通的一个示例。 Godot、Protobuf、Netty、ioGame、C#、Csharp、WebSocket |
ioGameSdkC#ExampleUnity | C# | 与 Unity 互通的一个示例。 Unity、Protobuf、Netty、ioGame、C#、Csharp、WebSocket |
ioGameSdkTsExampleCocos | TypeScript | 与 Cocos Creator 互通的一个示例。 CocosCreator、Protobuf、Netty、ioGame、TypeScript、WebSocket |
ioGameSdkTsExampleVue | TypeScript | 与 Vue 互通的一个示例。 Vue、Protobuf、Netty、ioGame、TypeScript、WebSocket |
ioGameSdkTsExampleAngular | TypeScript | 与 Angular 互通的一个示例。 Angular、Protobuf、Netty、ioGame、TypeScript、WebSocket |
ioGameSdkTsExampleHtml | TypeScript | 与 webpack 互通的一个示例。 (webpack: html + ts)、Protobuf、Netty、ioGame、TypeScript、WebSocket |
Action Java Code
让我们先看一段 java 代码,这段代码我们只需要关注类上的注释及 action 方法上的注释。 接下来,我们将用这段代码来演示其生成所对应的 TypeScript、C#、GDScript 代码。
我们为 action 方法提供了一些描述,分别是
- 方法描述
- 参数的描述
- 返回值的描述
生成的 TypeScript 代码
根据 MyAction.java 生成 TypeScript 对应的代码内容如下
- 生成了方法描述
- 生成了方法参数类型及其描述
- 生成了方法返回值类型及其描述
- 更重要的是生成了相关的使用示例,方法上的示例会教你如何使用,即使是新手也能做到零学习本。
ioGame 会为每个 action 生成两种编码风格的方法,分别是
- code style: callback 回调风格,方法名以 of 打头。优点:简洁,一体化。
- code style: async await,方法名以 ofAwait 打头。优点:可避免回调地狱。
两种风格该如何选择?
两个编码风格在本质上没有太大差别,简单业务选择 callback、复杂业务建议使用 async await。 这里的复杂指的是你需要在回调中继续请求游戏服务器,这样就可能产生回调地狱,而 async await 编码风格可以很好的避免此问题。
code style: callback
图中是 code style: callback. 编码风格:回调风格的演示,优点:简洁,一体化。
code style: async await
下图是 code style: async await. 编码风格:async await 风格的演示,优点:可避免回调地狱。
注意看方法中的使用示例,ioGame 会根据不同的编码风格生成对应的使用示例。
生成的 C# 代码
根据 MyAction.java 生成 C# 对应的代码内容如下
- 生成了方法描述
- 生成了方法参数类型及其描述
- 生成了方法返回值类型及其描述
- 更重要的是生成了相关的使用示例,方法上的示例会教你如何使用,即使是新手也能做到零学习本。
ioGame 会为每个 action 生成两种编码风格的方法,分别是
- code style: callback 回调风格,方法名以 Of 打头。优点:简洁,一体化。
- code style: async await,方法名以 OfAwait 打头。优点:可避免回调地狱。
两种风格该如何选择?
两个编码风格在本质上没有太大差别,简单业务选择 callback、复杂业务建议使用 async await。 这里的复杂指的是你需要在回调中继续请求游戏服务器,这样就可能产生回调地狱,而 async await 编码风格可以很好的避免此问题。
code style: callback
图中是 code style: callback. 编码风格:回调风格的演示,优点:简洁,一体化。
code style: async await
下图是 code style: async await. 编码风格:async await 风格的演示,优点:可避免回调地狱。
注意看方法中的使用示例,ioGame 会根据不同的编码风格生成对应的使用示例。
生成的 GDScript 代码
根据 MyAction.java 生成 GDScript 对应的代码内容如下
- 生成了方法描述
- 生成了方法参数类型及其描述
- 生成了方法返回值类型及其描述
- 更重要的是生成了相关的使用示例,方法上的示例会教你如何使用,即使是新手也能做到零学习本。
ioGame 会为每个 action 生成两种编码风格的方法,分别是
- code style: callback 回调风格,方法名以 of 打头。优点:简洁,一体化。
- code style: async await,方法名以 of_await 打头。优点:可避免回调地狱。
两种风格该如何选择?
两个编码风格在本质上没有太大差别,简单业务选择 callback、复杂业务建议使用 async await。 这里的复杂指的是你需要在回调中继续请求游戏服务器,这样就可能产生回调地狱,而 async await 编码风格可以很好的避免此问题。
code style: callback
图中是 code style: callback. 编码风格:回调风格的演示,优点:简洁,一体化。
code style: async await
下图是 code style: async await. 编码风格:async await 风格的演示,优点:可避免回调地狱。
注意看方法中的使用示例,ioGame 会根据不同的编码风格生成对应的使用示例。
小结
现在我们知道了一次编写,到处对接的威力了。 我们只需要编写一份 java 代码就能生成各客户端的对接交互代码, 为前端开发者减少了巨大的工作量,生成的交互 api 是直观的,如本地方法调用般丝滑。
代码生成可以让开发者专注于业务逻辑,而不是文档编写。 它也可以提高团队协作的效率和质量,保证文档的同步和准确。 如果没有游戏文档的生成,那么你将要抽出一些时间来编写、维护对接文档的工作, 而且当团队人数多了之后,文档就会很乱、不同步、不是最新的、忘记更新等情况就会出现。
ioGame 让我们做到了 Java 方法注释即文档,action 代码即对接。 当你的代码编写完后,对应的文档及交互代码也就完成了。 这样就不需要额外的花时间去编写和维护文档了,可以节省更多的时间。
ioGame 会为每个 action 生成两种编码风格的方法,分别是
- code style: callback 回调风格,方法名以 of 打头。优点:简洁,一体化。
- code style: async await,方法名以 ofAwait 打头。优点:可避免回调地狱。
如何生成代码
安装代码生成模块,示例源码中已经安装好了
下面是关于代码生成的,大致阅读即可,后面会有详细的介绍。 下一小节会对每一个 generateCode_XXX 做详细的说明。
public final class GenerateTest {
...
public static void main(String[] args) {
// CHINA or US
Locale.setDefault(Locale.CHINA);
// Load the business framework of each gameLogicServer
// 加载业务逻辑服
SdkApplication.listLogic().forEach(BrokerClientStartup::createBarSkeleton);
/*
* GameExternalServer accessAuthentication
* 游戏对外服访问权限,不生成权限控制的 action
*/
SdkApplication.extractedAccess();
DocumentAccessAuthentication reject = ExternalGlobalConfig.accessAuthenticationHook::reject;
IoGameDocumentHelper.setDocumentAccessAuthentication(reject);
/*
* Generate actions, broadcasts, and error codes.
* cn: 生成 action、广播、错误码
*/
// ----- About generating TypeScript code -----
// generateCodeVue();
// generateCocosCreator();
// ----- About generating C# code -----
// generateCodeCsharpGodot();
// generateCodeCsharpUnity();
// ----- About generating GDScript code -----
generateCodeGDScriptGodot();
// Added an enumeration error code class to generate error code related information
IoGameDocumentHelper.addErrorCodeClass(SdkGameCodeEnum.class);
// Generate document;
IoGameDocumentHelper.generateDocument();
// Generate .proto
generateProtoFile();
}
static void generateProtoFile() {
// By default, it will be generated in the target/proto directory
// .proto 默认生成的目录为 target/proto
// The package name to be scanned
String packagePath = "com.iohao.example.sdk.data";
GenerateFileKit.generate(packagePath);
}
}
ioGame 生成各 SDK 的交互代码是简单,通常有以下几个步骤
- 加载需要生成代码的游戏逻辑服(业务逻辑服),用于生成 xxxAction、广播
- 添加需要生成的交互语言 generateCode_XXX
- 添加错误码:addErrorCodeClass,用于生成错误码
- 生成 .proto 协议:generateProtoFile,用于生成 .proto 文件。
现在,我们只需要关注 generateCodeXXX 开头的方法。 虽然代码中给出了多个 generateCodeXXX 的代码生成,但在实际开发中,我们通常只需要生成其中一种。 交互代码默认会生成到当前项目的 target/code 目录中。
前我们只提供了 TypeScript、C#、GDScript 的生成实现
Class | 描述 |
---|---|
GDScriptDocumentGenerate | 用于生成 GDScript 代码 |
CSharpDocumentGenerate | 用于生成 C# 代码 |
TypeScriptDocumentGenerate | 用于生成 TypeScript 代码 |
Generate TypeScript
generateCodeVue()、generateCocosCreator() 都是通过 TypeScriptDocumentGenerate 类生成的。
下面是关于生成 TypeScript 语言的代码,分别生成了 Vue、CocosCreator 项目所需的交互代码。
public final class GenerateTest {
String rootPath = "/Users/join/gitme/ioGame-sdk/";
...
public static void main(String[] args) {
...
/*
* Generate actions, broadcasts, and error codes.
* cn: 生成 action、广播、错误码
*/
// About generating TypeScript code
generateCodeVue();
generateCocosCreator();
}
private static void generateCodeVue() {
var documentGenerate = new TypeScriptDocumentGenerate();
// 设置代码生成所存放的路径,如果不做任何设置,将会生成在 target/code 目录中
// By default, it will be generated in the target/code directory
String path = rootPath + "ioGameSdkTsExampleVue/src/assets/gen/code";
documentGenerate.setPath(path);
IoGameDocumentHelper.addDocumentGenerate(documentGenerate);
}
private static void generateCocosCreator() {
var documentGenerate = new TypeScriptDocumentGenerate();
// 设置代码生成所存放的路径,如果不做任何设置,将会生成在 target/code 目录中
// By default, it will be generated in the target/code directory
String path = rootPath + "ioGameSdkTsExampleCocos/assets/scripts/gen/code";
documentGenerate.setPath(path);
IoGameDocumentHelper.addDocumentGenerate(documentGenerate);
}
}
Generate C#
generateCodeCsharpUnity()、generateCodeCsharpGodot() 都是通过 CsharpDocumentGenerate 类生成的。
下面是关于生成 C# 语言的代码,分别生成了 Unity、Godot 项目所需的交互代码。
public final class GenerateTest {
String rootPath = "/Users/join/gitme/ioGame-sdk/";
...
public static void main(String[] args) {
...
/*
* Generate actions, broadcasts, and error codes.
* cn: 生成 action、广播、错误码
*/
// ----- About generating C# code -----
generateCodeCsharpUnity();
generateCodeCsharpGodot();
}
private static void generateCodeCsharpUnity() {
var documentGenerate = new CsharpDocumentGenerate();
// 设置代码生成所存放的路径,如果不做任何设置,将会生成在 target/code 目录中
// By default, it will be generated in the target/code directory
String path = rootPath + "ioGameSdkCsharpExampleUnity/Assets/Scripts/Gen/Code";
documentGenerate.setPath(path);
IoGameDocumentHelper.addDocumentGenerate(documentGenerate);
}
private static void generateCodeCsharpGodot() {
var documentGenerate = new CsharpDocumentGenerate();
// 设置代码生成所存放的路径,如果不做任何设置,将会生成在 target/code 目录中
// By default, it will be generated in the target/code directory
String path = rootPath + "ioGameSdkCsharpExampleGodot/script/gen/code";
documentGenerate.setPath(path);
IoGameDocumentHelper.addDocumentGenerate(documentGenerate);
}
}
Generate GDScript
generateCodeGDScriptGodot() 是通过 GDScriptDocumentGenerate 类生成的。
下面是关于生成 GDScript 语言的代码,生成了 Godot 项目所需的交互代码。
public final class GenerateTest {
String rootPath = "/Users/join/gitme/ioGame-sdk/";
...
public static void main(String[] args) {
...
/*
* Generate actions, broadcasts, and error codes.
* cn: 生成 action、广播、错误码
*/
// ----- About generating GDScript code -----
generateCodeGDScriptGodot();
}
private static void generateCodeGDScriptGodot() {
var documentGenerate = new GDScriptDocumentGenerate();
// cn: 设置代码生成所存放的路径,如果不做任何设置,将会生成在 target/code 目录中
// By default, it will be generated in the target/code directory
String path = rootPath + "ioGameSdkGDScriptExampleGodot/gen/code";
documentGenerate.setPath(path);
IoGameDocumentHelper.addDocumentGenerate(documentGenerate);
}
}
生成广播代码
广播代码生成时与 action 不同,需要单独编写。 这是因为 action 有固定的源码信息可解析,而广播没有。 所以需要额外的对广播进行配置,这些配置信息用来生成可交互的代码。
代码中,我们配置了两个对外公开的广播方法,内容包括
- 设置广播路由
- setMethodDescription 设置方法描述
- setMethodName 设置方法名
- setDataClass 设置广播的返回值及其描述,单参数的返回值。
- setDataClassList 设置广播的返回值及其描述,List 类型的返回值。
通常建议将广播代码配置到与其关联的游戏逻辑服内,比如邮件相关的广播配置到邮件逻辑服。
public final class SdkApplication extends AbstractBrokerClientStartup {
@Override
public BarSkeleton createBarSkeleton() {
BarSkeletonBuilder builder = ...
extractedDoc(builder);
...
}
private void extractedDoc(BarSkeletonBuilder builder) {
// single value
builder.addBroadcastDocument(BroadcastDocument.newBuilder(SdkCmd.of(SdkCmd.broadcastInt))
.setDataClass(int.class, "biz data description")
.setMethodDescription("IntValue method description")
.setMethodName("IntValue")
);
// list value
builder.addBroadcastDocument(BroadcastDocument.newBuilder(SdkCmd.of(SdkCmd.broadcastListInt))
.setDataClassList(int.class, "biz id list")
.setMethodDescription("IntValueList method description")
.setMethodName("IntValueList")
);
}
}
对应生成的代码如下,下面是 TypeScript 的广播代码截图,其他语言的展示是差不多的,这里就不截图了。
路由访问权限控制
开发过程中,有些 action 只能内部访问,比如增加金币、敏感数值的增加等。 这些 action 是不能由外部直接访问的,这里说的外部指的是连接中的玩家。
因为是内部访问的 action,所以是不需要出现在对接文档中的。如果不加以控制,还会给阅读对接文档的开发者造成一定的干扰。
ExternalGlobalConfig.accessAuthenticationHook, 相关文档 - 路由访问权限控制
public class SdkApplication {
static void extractedAccess() {
...
/*
* Notice:
* Reject cmd: Routes that deny access to any player.
* These action methods will not be generated by the SDK.
*
* cn: sdk 将不会生成这些配置了 Rejection 的 action 方法。
*/
accessAuthenticationHook.addRejectionCmd(SdkCmd.cmd, SdkCmd.internalAddMoney);
}
}
public class GenerateTest {
...
public static void main(String[] args) {
SdkApplication.extractedAccess();
// Set route access control.
// cn: 设置路由访问权限控制
IoGameDocumentHelper.setDocumentAccessAuthentication(ExternalGlobalConfig.accessAuthenticationHook::reject);
...
IoGameDocumentHelper.generateDocument();
}
}
注意事项
默认情况下,生成的 action 交互代码的方法名使用的是 java action 的方法名。 如果你的 java action 方法名有变动,则会影响客户端的代码。
为了避免此类情况,有两种方法
- 不要更改已经发布的 action 方法名。
- 给 action 添加 DocumentMethod 注解来固定方法名。框架在生成对接代码时,会优先使用 DocumentMethod 注解的值。
@ActionController(MyCmd.cmd)
public final class MyAction {
@ActionMethod(MyCmd.noReturn)
@DocumentMethod("noReturnMethod")
public void noReturn(String name) {
...
}
}
通常情况下,如果你没有经常更改 action 方法名的习惯,可以不需要使用 DocumentMethod 注解,因为这可以让你的代码保持简洁。 但无论如何你都要记住,不要更改已经发布的方法名,因为这会让前端开发者产生不必要的小情绪。
错误码及错误处理的使用
错误码相关的代码文件统一命名为 GameCode,在触发错误时,服务器会将错误码放到 result responseStatus 字段中。
以下展示了两种编码风格关于错误的处理,这里使用的 C# 演示。其他语言的处理类似,这里就不演示了。
public static async Task OnTestError()
{
var value = _testErrorValue++;
Log("------- OnTestError ------");
// code style: callback.
SdkAction.OfTestError(value, result =>
{
result.Log(result.GetInt());
}).OnError(result =>
{
// error
result.Log(result.GetErrorInfo());
// result.GetResponseStatus()
});
// code style: async await.
var result = await SdkAction.OfAwaitTestError(value);
// result.GetResponseStatus()
if (result.Success())
result.Log(result.GetInt());
else
result.Log(result.GetErrorInfo());
}
广播代码的使用
TypeScript、C#、GDScript 生成广播相关的代码文件统一命名为 Listener,如 Listener.cs、Listener.ts。 Listener 文件中包含了服务器所有主动的广播动作,通常来说我们只需要在广播监听回调中编写自己的业务逻辑即可,如下
C#
Listener.ListenBulletBroadcast(result =>
{
var bulletMessage = result.GetValue<BulletMessage>();
result.Log(bulletMessage);
});
// This method adds print behavior for all broadcast listeners, aiming to inform developers which broadcast methods haven't been handled.
// cn: 该方法为所有广播监听添加了打印的行为,目的是让开发者知道有哪些广播方法没有处理
Listener.Listener_ioGame();
Typescript
Listener.listenBulletBroadcast(result => {
const bullet = result.getValue(BulletMessageSchema);
result.log(bullet);
})
// This method adds print behavior for all broadcast listeners, aiming to inform developers which broadcast methods haven't been handled.
// cn: 该方法为所有广播监听添加了打印的行为,目的是让开发者知道有哪些广播方法没有处理
Listener.listener_ioGame();
GDScript
# GDScript
func listene():
Listener.listen_bullet_broadcast(func(result: IoGame.ResponseResult):
var bullet := result.get_value(Common.BulletMessage) as Common.BulletMessage
result.log(bullet)
)
// This method adds print behavior for all broadcast listeners, aiming to inform developers which broadcast methods haven't been handled.
// cn: 该方法为所有广播监听添加了打印的行为,目的是让开发者知道有哪些广播方法没有处理
Listener.listener_ioGame()
小结
ioGame 做到了一次编写到处对接,提升了巨大的生产力。
ioGame 的游戏代码成功能可以让开发者专注于业务逻辑,而不是文档编写。 显著地提高了团队的协作效率和质量,保证了文档的同步和准确性。
虽然我们花了大量的篇幅来介绍对接文档,但是实际的使用中是简单的,只需要几句代码就能生成 action、广播、错误码、.proto 相关的代码。
public final class GenerateTest {
...
public static void main(String[] args) {
// CHINA or US
Locale.setDefault(Locale.CHINA);
// Load the business framework of each gameLogicServer
// c: 加载业务逻辑服
SdkApplication.listLogic().forEach(BrokerClientStartup::createBarSkeleton);
// Set route access control.
// cn: 设置路由访问权限控制
SdkApplication.extractedAccess();
DocumentAccessAuthentication reject = ExternalGlobalConfig.accessAuthenticationHook::reject;
IoGameDocumentHelper.setDocumentAccessAuthentication(reject);
/*
* Generate actions, broadcasts, and error codes.
* cn: 生成 action、广播、错误码
*/
// ----- About generating C# code -----
generateCodeCsharpGodot();
// Added an enumeration error code class to generate error code related information
// cn: 错误码的生成
IoGameDocumentHelper.addErrorCodeClass(SdkGameCodeEnum.class);
// Generate document;
IoGameDocumentHelper.generateDocument();
// Generate .proto
// cn: 生成 .proto 文件
generateProtoFile();
}
private static void generateCodeCsharpGodot() {
var documentGenerate = new CsharpDocumentGenerate();
// 设置代码生成所存放的路径,如果不做任何设置,将会生成在 target/code 目录中
// By default, it will be generated in the target/code directory
String path = rootPath + "ioGameSdkCsharpExampleGodot/script/gen/code";
documentGenerate.setPath(path);
IoGameDocumentHelper.addDocumentGenerate(documentGenerate);
}
static void generateProtoFile() {
// By default, it will be generated in the target/proto directory
// .proto 默认生成的目录为 target/proto
// The package name to be scanned
String packagePath = "com.iohao.example.sdk.data";
GenerateFileKit.generate(packagePath);
}
}
public final class SdkApplication {
static List<AbstractBrokerClientStartup> listLogic() {
return List.of(
new SdkLogicServer()
);
}
}
游戏逻辑服模块化
通常,我们会将游戏逻辑服模块化来积累我们自己的生态。同时,模块化也可以让其他项目复用已有的功能,从而避免重复开发。
在代码生成阶段,我们配置了哪些游戏逻辑服,ioGame 就会为我们生成相关游戏逻辑服的内容(action、广播)。 之前我们在广播配置这一小节中提到了建议将广播代码配置到与其关联的游戏逻辑服内,比如邮件相关的广播配置到邮件逻辑服。
这么做也体现了模块化的思维,当我们不需要邮件逻辑服时,不将该模块加载到代码生成即可,这样就不会生成邮件逻辑服相关的内容了。
public final class SdkApplication {
static List<AbstractBrokerClientStartup> listLogic() {
return List.of(
new SdkLogicServer()
// new MailLogicServer()
// new ChatLogicServer()
);
}
}
如何扩展更多语言的支持
目前,我们提供了 TypeScript、C#、GDScript 的支持。 开发者如果有需要可以通过 DocumentGenerate 接口来实现扩展。
开发者可以通过实现 DocumentGenerate 接口来扩展不同的文档生成,开发者可以扩展此接口来定制更多个性化的扩展,如
- html 版本的文档。
- json 版本的文档。
- 其他语言的联调文档 ...等。
void test() {
var documentGenerate = new YourDocumentGenerate();
IoGameDocumentHelper.addDocumentGenerate(documentGenerate);
}