Skip to main content

代码组织与约定

这里给出一些关于代码的命名约定,与项目的代码组织。

目的

  • 减轻接手二手项目人员的负担,因为我们也有可能成为接手人员。
  • 让团队新进成员快速的熟悉整个项目脉络。

路由

一个游戏项目,通常由多个模块组成,每个模块下会有多个业务方法。 将所有模块的主路由定义到一个接口中,并统一命名为 CmdModule

每个模块单独一个文件,而每个模块文件管理对应的业务方法。 模块文件命名以 xxxCmd 结尾,单独模块下主路由统一命名为 cmd

public interface CmdModule {
int userCmd = 1;
int emailCmd = 2;
}

public interface UserCmd {
int cmd = CmdModule.userCmd;
int loginVerify = 1;
}

public interface EmailCmd {
int cmd = CmdModule.emailCmd;
int openEmail = 1;
int listEmail = 2;
}

如果你将来接手了一个二手项目是遵循这些约定的,那么你可以通过 CmdModule.java 文件查看当前项目有多少个模块。 通过对应的模块文件,可以查看该模块下有多少个业务方法。

关于命名,可以使用标准的接口属性命名,全大写加下划线分割, 也可以使用驼峰命名,个人倾向使用驼峰命名。


广播路由

建议在路由文件中添加一个广播起始标记,这种方式可以让我们不需要关注具体的路由值。

public interface RoomCmd {
int cmd = 1;
...
/** broadcastIndex. cn:广播起始 */
AtomicInteger inc = new AtomicInteger(50);
CmdInfo enterRoomBroadcast = CmdInfo.of(cmd, inc.getAndIncrement());
CmdInfo quitRoomBroadcast = CmdInfo.of(cmd, inc.getAndIncrement());
}

// test example
@ActionController(RoomCmd.cmd)
public class RoomAction {
@ActionMethod(RoomCmd.enterRoom)
public void enterRoom(FlowContext flowContext) {
...
flowContext.broadcast(RoomCmd.enterRoomBroadcast, yourData);
}
}

Action

Action 类的命名建议使用 xxxAction 结尾。

@ActionController(UserCmd.cmd)
public class UserAction {
@ActionMethod(UserCmd.loginVerify)
public void loginVerify(String jwt) {
}
}

异常枚举

断言 + 异常机制 = 清晰简洁的代码

推荐使用枚举实现接口的方式,这样可以很方便的管理异常消息。

建议统一命名为 GameCode

@Getter
public enum GameCode implements MsgExceptionInfo {
emailChecked(100, "Email error.");

final int code;
final String msg;

GameCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
}

使用示例

@ActionController(EmailCmd.cmd)
public class EmailAction {
@ActionMethod(EmailCmd.openEmail)
public void openEmail(String email) {
Pattern pattern = Pattern.compile("[a-zA-Z0-9_-]+@\\w+\\.[a-z]+(\\.[a-z]+)?");
var checkedResult = pattern.matcher(email).find();
GameCode.emailChecked.assertTrue(checkedResult);
}
}

数据协议

现在,我们定义数据协议,用于客户端与服务器的数据交互。 如果你只做过 web 开发而没有游戏服务器开发经验的,可以把这理解成 DTO、业务数据载体等,其主要目的是用于业务数据的传输。

由于原生的 PB 使用起来比较麻烦,这里我们使用 Jprotobuf。 这是对 PB 的简化使用,性能与原生的同等。

在普通的 java 类上添加 ProtobufClass 注解,就表示这是一个业务数据协议。


标准的数据协议定义如下

  • code 1,标记为 Protobuf。
  • code 2,所有成员属性统一使用 public。
  • code 3,数据协议类名以 xxMessage 结尾。
@ProtobufClass
@FieldDefaults(level = AccessLevel.PUBLIC)
public class HelloMessage {
String name;
int level;
}

tip

所有成员属性统一使用 public 能明确知道该数据协议类没有使用 setter、getter 做逻辑加工处理, 另外一点是原生的 proto 也不能在 setter、getter 内做逻辑加工处理。

当数据协议使用了 setter、getter,我们能快速的知道该数据协议类在里面做了一些额外的逻辑处理。

游戏逻辑服

游戏逻辑服的命名建议以 "xxLogicServer" 结尾。

public class UserLogicServer extends AbstractBrokerClientStartup {
@Override
public BarSkeleton createBarSkeleton() {
...
}

@Override
public BrokerClientBuilder createBrokerClientBuilder() {
BrokerClientBuilder builder = BrokerClient.newBuilder();
builder.appName("UserLogicServer");
return builder;
}
}

项目结构

  • code 1,Broker(游戏网关),及相关扩展。
  • code 4,游戏对外服,及相关扩展。
  • code 7,该目录存放所有的游戏逻辑服模块。
  • code 10,单体启动入口,方便日常开发。
  • code 13,模拟客户端,方便日常模拟测试。
  • code 16,该目录存放所有的游戏逻辑服模块对外提供服务的数据, 如路由、数据协议、访问该模块对应的游戏逻辑服接口。
  • code 17,公共的模块包,提供给所有模块使用。如 CmdModule、公共的数据协议、公用类...等。
├── broker
│ ├── pom.xml
│ └── src
├── external
│ ├── pom.xml
│ └── src
├── logic
│ ├── email-logic
│ └── user-logic
├── one-application
│ ├── pom.xml
│ └── src
├── one-client
│ ├── pom.xml
│ └── src
└── provide
├── common-provide
├── email-provide
└── user-provide