当所有玩家都离开后,GameManager 会自动帮您销毁当前房间及相关的 MasterClient。此时如果您没有其他的逻辑要做,则不需要关心本节文档。如果您希望自己做一些清理工作,例如保存用户数据等,可以使用 autoDestroy 装饰器,这个装饰器会在所有玩家离开后自动触发 Game 子类中的 destroy() 方法,您可以将相关逻辑写在这个方法中。
import { autoDestroy, Game } from "@leancloud/client-engine";
@autoDestroy()
export default class SampleGame extends Game {
protected destroy() {
super.destroy();
console.log('在这里可以做额外的清理工作');
}
}
负载均衡
Client Engine 会根据整体实例负载的高低自动对实例数量进行调整。
在 Client Engine 中,需要负载均衡的情况有两种:第一种是客户端通过 REST API 发起的请求,第二种是每一个实例运行的 Game 数量的负载。对于客户端通过 REST API 发起的请求,Client Engine 会自动将请求均匀的分配给当前的所有实例,不需要我们再做任何配置工作。对于第二种情况,每个 Game 对象(每局游戏)一般都会持续存在一段时间,为了让每个实例承载的 Game 对象尽可能达到均衡,我们需要额外配置 GameManager 到负载均衡系统中。
Client Engine 开发指南 · Node.js
请先阅读 Client Engine 快速入门 · Node.js 及你的第一个 Client Engine 小游戏来初步了解如何使用初始项目来开发游戏。本文档将在初始项目的基础上深入讲解 Client Engine SDK。
Client Engine 初始项目依赖了专门的 Client Engine SDK, Client Engine SDK 在多人在线对战 SDK 的基础上进行了封装,帮助您更好的撰写服务端游戏逻辑。您可以通过快速入门安装依赖。
组件
SDK 提供以下组件:
Game
:负责房间内游戏的具体逻辑。Client Engine 维护了许多游戏房间,每一个游戏房间都是一个 Game 实例,即每个 Game 实例对应一个唯一的 Play Room 与 MasterClient。游戏房间内的逻辑由 Game 中的代码来控制,因此房间内的游戏逻辑必须继承自该类。GameManager
:负责创建、管理及分配具体的 Game 对象。Game 的管理及销毁由 SDK 负责,不需要您再自己额外写代码。GameManager
GameManager 实例化
GameManager
会帮您自动创建、管理并销毁 Game,因此在项目启动时,您需要实例化GameManager
。相关示例代码如下:自定义 GameManager
首先需要自定义一个 Class 继承自
GameManager
,例如示例代码中的SampleGameManager
:在 GameManager 中自定义方法
Client Engine 的核心用法之一是负责创建 Game 并返回 roomName 给客户端,因此在
SampleGameManager
这个类中,我们需要撰写创建Game
的方法提供给 Web API 使用。例如示例项目中的快速开始和创建新游戏。这里的示例代码我们以创建新游戏为例:写完自定义方法之后,在下文我们还需要为这里的方法配置负载均衡,需要注意的是,受负载均衡系统的要求,
GameManager
子类中的public
方法,其参数与返回值必须是string
、number
、boolean
、null
、Object
、Array
中的一种。以上代码中可以看到GameManager
的createGame()
方法返回的是一个Game
,不符合负载均衡的要求,因此我们在这里封装为自己的方法createGameAndGetName()
。创建 GameManager 子类对象
接下来创建
GameManager
的子类对象,在创建SampleGameManager
的时候,需要在第一个参数内传入自定义的 Game ,这里使用的是示例 Demo 猜拳游戏中的RPSGame
。设置负载均衡
GameManager
需要配置负载均衡,以确保GameManager
创建的Game
能够尽可能均匀分配到每一个 Client Engine 实例中。负载均衡的详细文档请看下文,在这里,我们先讲解如何配置。在这里我们创建一个负载均衡对象,然后将上面的
gameManager
绑定到负载均衡中:loadBalancerFactory
的bind()
方法中,第一个参数是gameManager
对象,第二个参数是一个数组,传入需要进行负载均衡的方法名["createGameAndGetName"]
。到这里,
gameManager
的配置就完成了,您可以在自己定义的 Web API 处这样调用相关方法:gameManager.createGameAndGetName()
。创建房间
在 GameManager 实例化这一节中,我们在子类中使用了
GameManager
的createGame()
来创建房间。createGame()
接受以下参数:customRoomProperties
,customRoomPropertyKeysForLobby
,visible
,对这三个参数的说明请参考创建房间。minSeatCount
和maxSeatCount
之间,否则 Client Engine 会拒绝创建房间。如果不指定,则以defaultSeatCount
为准。例如创建一个带有匹配条件的新房间时,可以这样调用
createGame()
:在你的第一个 Client Engine 小游戏中,
reception.ts
中使用createGame()
撰写了两个自定义方法用于「快速开始」和「创建新游戏」,并通过index.ts
中的 Web API/reservation
和/game
来调用相关逻辑,如果没有自定义需求您可以直接使用示例 Demo 中的接口并传入以上参数。获取当前可用的房间
GameManager 提供了
getAvailableGames()
方法来获取当前 GameManager 对象所在的 Client Engine 实例中的可用游戏列表。这里的可用指的是房间还有空位。使用示例代码如下:需要注意的是,这个方法获取的不是多人对战服务中所有的可用房间,仅限于当前所在 Client Engine 实例中的可用房间,Client Engine 多实例负载均衡请参考负载均衡。
匹配
GameManager
暂时没有提供匹配机制,如果客户端只需要随机加入某个房间,可以参考示例项目中「快速开始」的实现方案。这个实现方案会在负载最低的实例中寻找可用房间或创建房间,最终返回给客户端一个可加入的房间名称。如果您希望实现有条件的匹配,可以这样实现:
/game
入口。这个流程在客户端中的示例代码如下(非 Client Engine):
客户端首先向多人对战服务发起有条件的加入房间请求:
如果多人对战服务此时有可以加入的新房间,您会自动加入到新房间中,并触发加入房间成功事件:
如果没有可以加入的房间,会触发加入房间失败事件。在这个事件中,4301 错误码代表着没有可以加入的空房间,此时我们向 Client Engine 请求创建一个新的房间,获得新房间的 roomName 后加入新房间:
Game
Game 生命周期
Game
由 SDK 中的GameManager
管理,GameManager
会在收到创建房间的请求时根据情况创建Game
。Game
的控制权从 SDK 中GameManager
移交给Game
本身。从这个时刻开始,会有玩家陆续加入游戏房间。Game
将控制权交回GameManager
,GameManager
做最后的清理工作,包括断开并销毁该房间的 masterClient、将Game
从管理的游戏列表中删除等。Game 通用属性
在实现游戏逻辑的过程中,
Game
类提供下面这些属性来简化常见需求的实现,您可以在继承Game
的自己的类中方便的获得以下属性:room
属性:游戏对应的房间,这是一个 Play SDK 中的 Room 实例。masterClient
属性:游戏对应的 masterClient,这是一个 Play SDK 中的 Play 实例。players
属性:不包含 masterClient 的玩家列表。注意,如果您通过 Play SDK Room 实例的playerList
属性获取的房间成员列表是包括 masterClient 的。Game 通用方法
Game 类在多人对战 SDK 的基础上封装了以下方法,使得 MasterClient 可以更便利的发送自定义事件:
broadcast()
方法:向所有玩家广播自定义事件。示例代码请参考广播自定义事件。forwardToTheRests()
方法:将一个玩家发送的自定义事件转发给其他玩家。示例代码请参考转发自定义事件。实现自己的 Game
实现自己的房间内游戏逻辑时,您需要创建一个继承自
Game
的类来撰写自己的游戏逻辑,示例方法如下:设置房间内玩家数量
这里的玩家数量指的是不包括 MasterClient 的玩家数量,根据多人对战服务的限制,最多不能超过 9 个人。
在
Game
中需要指定defaultSeatCount
静态属性作为默认的玩家数量,Client Engine 会根据这个值向多人对战服务请求创建房间。例如斗地主需要 3 个人才能玩,可以这样设置:如果您的游戏需要的玩家数量在某个范围内,除了设置
defaultSeatCount
外,还需要使用minSeatCount
静态属性限定最小玩家数量,maxSeatCount
静态属性设定最大玩家数量。例如三国杀要求至少 2 个人,最多 8 个人才能玩,默认 5 个人可以玩,可以这样设置:在创建房间的接口中,可以将客户端 request 请求中的
seatCount
参数来动态覆盖掉defaultSeatCount
。当房间人数达到
seatCount
时,您可以选择配置触发房间人满事件,如果您的客户端没有指定seatCount
,人满事件时将以defaultSeatCount
的值为准。加入房间事件
当客户端成功加入房间后,位于 Client Engine 的 MasterClient 会收到新玩家加入事件,如果您需要监听此事件,可以在自定义的
Game
中的constructor()
方法中撰写监听的代码:房间人满事件
当房间的人数满足设置房间内玩家数量的人满逻辑时,
watchRoomFull
装饰器会让您收到 Game 抛出的AutomaticGameEvent.ROOM_FULL
事件,您可以在这个事件中撰写相应的游戏逻辑,例如关闭房间,向客户端广播游戏开始:广播自定义事件
在房间人满事件中,
Game
向房间内所有成员广播了游戏开始:在广播事件时您还可以带有一些数据:
此时客户端的接收自定义事件方法会被触发,如果发现是
game-start
事件,客户端可以在 UI 上展示对战开始。转发自定义事件
MasterClient 可以转发某个客户端发来的事件给其他客户端,在转发时还可以处理数据:
在这个代码中,
event
参数是某个客户端发来的原始事件,eventData
是原始事件的数据,您可以在转发事件给其他客户端时处理该数据,例如抹去或增加一些信息。MasterClient 发送该事件后,客户端的接收自定义事件会被触发。MasterClient 与客户端通信
除了上方初始项目提供的广播自定义事件及转发自定义事件外,您依然可以使用多人对战服务中的自定义属性、自定义事件进行通信。
除此之外,
Game
还提供了以下 RxJS 方法方便您对事件进行流处理,进而精简自己的代码及逻辑:getStream()
方法:获取玩家发送的自定义事件的流,这是一个 RxJS 中的 Observable 对象。接口说明请参考 API 文档。takeFirst()
方法:获取玩家发送的指定条件的从现在开始算的第一条自定义事件的流,返回一个 RxJS 中的 Observable 对象。接口说明请参考 API 文档。注意,以上两个方法需要您了解 RxJS 才能使用,如果您不了解 RxJS,依然可以使用多人对战服务中的事件方法进行通信。
游戏结束
当所有玩家都离开后,
GameManager
会自动帮您销毁当前房间及相关的 MasterClient。此时如果您没有其他的逻辑要做,则不需要关心本节文档。如果您希望自己做一些清理工作,例如保存用户数据等,可以使用autoDestroy
装饰器,这个装饰器会在所有玩家离开后自动触发Game
子类中的destroy()
方法,您可以将相关逻辑写在这个方法中。负载均衡
Client Engine 会根据整体实例负载的高低自动对实例数量进行调整。
在 Client Engine 中,需要负载均衡的情况有两种:第一种是客户端通过 REST API 发起的请求,第二种是每一个实例运行的
Game
数量的负载。对于客户端通过 REST API 发起的请求,Client Engine 会自动将请求均匀的分配给当前的所有实例,不需要我们再做任何配置工作。对于第二种情况,每个Game
对象(每局游戏)一般都会持续存在一段时间,为了让每个实例承载的Game
对象尽可能达到均衡,我们需要额外配置GameManager
到负载均衡系统中。这个特性由 SDK 提供的
LoadBalancerFactory
类实现。在 GameManager 实例化中我们可以看到,LoadBalancerFactory
通过绑定gameManager
生成一个LoadBalancer
的对象,每一个 Client Engine 实例中都会有这样一个对象。当 Client Engine 的某个实例接收到来自客户端的 REST API 请求,并调用
gameManager
中的方法时,接收请求的实例中的负载均衡节点LoadBalancer
会找出集群中承载Game
数量最小的实例,将指定的gameManager
的 API 调用转发给该实例的gameManager
运行并将结果返回。在这里,LoadBalancer
只负责请求的转发,不关心如何处理请求。API 文档
您可以在 API 文档中找到更多 SDK 的类、方法及属性说明,点击查看 Client Engine SDK API 文档。