Wind主打简单易上手特性,默认在Windows下开发,多个平台部署。游戏业务开发流程只需要三步,第一步是定义与客户端通信的协议,第二步是生成协议数据,第三步是编写业务逻辑函数。

  1. 协议定义

Wind支持Protobuf通信协议,协议文件放在engine\codec\proto目录下,分为客户端通信协议proto_client和服务间通信协议proto_server。与客户端通信的协议一般以RequestResponse结尾,如下。

  1. message PlayerLoginRequest
  2. {
  3. string player_id = 1;
  4. }
  5. message PlayerLoginResponse
  6. {
  7. string player_id = 1;
  8. bool result = 2;
  9. }
  1. 协议生成

运行script 目录下的gen_protobuf.bat 脚本,生成协议的Protobuf对象。

  1. RPC逻辑函数

Wind的RPC函数采用自动注册的方式,你只需要在对应服务handlers 目录下编写以Handler_开头的函数就行,起服时会自动注册函数。

  1. # 客户端rpc函数以Handler_开头 后面接Protobuf的协议名
  2. async def Handler_PlayerLoginRequest(client: ClientConn, request):
  3. logging.info(f"player_id:{client}, request:{request} ")
  4. client.set_player_id(request.player_id)
  5. GateRouterMgr().on_player_login(request.player_id)

上面就是Wind业务逻辑处理的流程,非常的简单清晰。

逻辑服务

Wind整个核心的目录是engineengine包含服务引擎所有的底层基础功能,比如网络、序列化、服务发现等等。engine目录下最重要的文件是SrvEngine.pySrvEngine.py包含Engine 类,它是所有逻辑服务的基类,其他的逻辑服务需要继承Engine类,并且增加自己的逻辑功能。
service.png
逻辑服务放在service目录下,目前有gateway服务和game服务。gateway 是与客户端直连的服务,如果是分布式服务的话,用于路由客户端的RPC请求到后端服务。game 服务是后端服务,用于处理游戏逻辑,一般gateway 服务的RPC 请求就会路由到这。这里的gamegateway 服务的功能只是我测试使用的的,你可以根据业务需求,自定义服务功能。
一般各个逻辑服务的逻辑写到对应服务的handlersmgr的目录下。
handler.png

逻辑模块

各个服务的逻辑模块写在mgrs 目录下,当然你也可以新建文件夹。Python层的逻辑是单线程异步协程,单个时刻写逻辑时只需要考虑一个客户端的请求就行,所以逻辑模块通常使用单例类来管理各个玩家的本地缓存数据,比如:

  1. from engine.utils.Singleton import Singleton
  2. # 玩家管理模块
  3. class GamePlayerMgr(Singleton):
  4. def __init__(self):
  5. # 各个玩家的服务绑定信息
  6. self.player2server = {}
  7. def get_player_bind_server(self, player_id):
  8. return self.player2server.get(player_id, "*")

定时器

服务器的一些定时任务可以用Engine的add_timer 函数注册,比如:

  1. self.add_timer(int(Config.ETCD_TTL / 2), self.registry.tick)

服务间的RPC

服务间的RPC流程跟上面使用流程是一样的,不过服务间协议一般以S_开头。比如:

  1. message S_PlayerRegister
  2. {
  3. string player_id = 1;
  4. string gate_server_id = 2;
  5. }
  6. message S_PlayerRegisterAck
  7. {
  8. bool result = 1;
  9. }

服务器间的通信使用Nats消息队列,因为Nats能保证有序,这对服务器逻辑非常重要。发送RPC时只需要知道对方server_id就行。

  1. SrvEngine.srv_inst.send_server_message(SeverType.GAME, bind_server, player_id, sync)